Merge tag '2023.10.1' into merge-upstream

This commit is contained in:
riku6460
2023-10-12 20:59:40 +09:00
15 changed files with 287 additions and 60 deletions

View File

@@ -868,8 +868,8 @@ export class NoteCreateService implements OnApplicationShutdown {
// 基本的にvisibleUserIdsには自身のidが含まれている前提であること
if (note.visibility === 'specified' && !note.visibleUserIds.some(v => v === following.followerId)) continue;
// 自分自身以外への返信
if (note.replyId && note.replyUserId !== note.userId) {
// 自分自身への返信 or そのフォロワーへの返信」のどちらでもない場合
if (note.replyId && !(note.replyUserId === note.userId || note.replyUserId === following.followerId)) {
if (!following.withReplies) continue;
}
@@ -886,8 +886,8 @@ export class NoteCreateService implements OnApplicationShutdown {
!note.visibleUserIds.some(v => v === userListMembership.userListUserId)
) continue;
// 自分自身以外への返信
if (note.replyId && note.replyUserId !== note.userId) {
// 自分自身への返信 or そのリストの作成者への返信」のどちらでもない場合
if (note.replyId && !(note.replyUserId === note.userId || note.replyUserId === userListMembership.userListUserId)) {
if (!userListMembership.withReplies) continue;
}
@@ -907,6 +907,10 @@ export class NoteCreateService implements OnApplicationShutdown {
// 自分自身以外への返信
if (note.replyId && note.replyUserId !== note.userId) {
this.redisTimelineService.push(`userTimelineWithReplies:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r);
if (note.visibility === 'public' && note.userHost == null) {
this.redisTimelineService.push('localTimelineWithReplies', note.id, 300, r);
}
} else {
this.redisTimelineService.push(`userTimeline:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r);
if (note.fileIds.length > 0) {

View File

@@ -55,6 +55,7 @@ export const paramDef = {
includeLocalRenotes: { type: 'boolean', default: true },
withFiles: { type: 'boolean', default: false },
withRenotes: { type: 'boolean', default: true },
withReplies: { type: 'boolean', default: false },
},
required: [],
} as const;
@@ -94,12 +95,29 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
this.cacheService.userBlockedCache.fetch(me.id),
]);
const [htlNoteIds, ltlNoteIds] = await this.redisTimelineService.getMulti([
ps.withFiles ? `homeTimelineWithFiles:${me.id}` : `homeTimeline:${me.id}`,
ps.withFiles ? 'localTimelineWithFiles' : 'localTimeline',
], untilId, sinceId);
let noteIds: string[];
if (ps.withFiles) {
const [htlNoteIds, ltlNoteIds] = await this.redisTimelineService.getMulti([
`homeTimelineWithFiles:${me.id}`,
'localTimelineWithFiles',
], untilId, sinceId);
noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds]));
} else if (ps.withReplies) {
const [htlNoteIds, ltlNoteIds, ltlReplyNoteIds] = await this.redisTimelineService.getMulti([
`homeTimeline:${me.id}`,
'localTimeline',
'localTimelineWithReplies',
], untilId, sinceId);
noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds, ...ltlReplyNoteIds]));
} else {
const [htlNoteIds, ltlNoteIds] = await this.redisTimelineService.getMulti([
`homeTimeline:${me.id}`,
'localTimeline',
], untilId, sinceId);
noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds]));
}
let noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds]));
noteIds.sort((a, b) => a > b ? -1 : 1);
noteIds = noteIds.slice(0, ps.limit);

View File

@@ -45,6 +45,7 @@ export const paramDef = {
properties: {
withFiles: { type: 'boolean', default: false },
withRenotes: { type: 'boolean', default: true },
withReplies: { type: 'boolean', default: false },
excludeNsfw: { type: 'boolean', default: false },
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
sinceId: { type: 'string', format: 'misskey:id' },
@@ -90,7 +91,19 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
this.cacheService.userBlockedCache.fetch(me.id),
]) : [new Set<string>(), new Set<string>(), new Set<string>()];
let noteIds = await this.redisTimelineService.get(ps.withFiles ? 'localTimelineWithFiles' : 'localTimeline', untilId, sinceId);
let noteIds: string[];
if (ps.withFiles) {
noteIds = await this.redisTimelineService.get('localTimelineWithFiles', untilId, sinceId);
} else {
const [nonReplyNoteIds, replyNoteIds] = await this.redisTimelineService.getMulti([
'localTimeline',
'localTimelineWithReplies',
], untilId, sinceId);
noteIds = Array.from(new Set([...nonReplyNoteIds, ...replyNoteIds]));
noteIds.sort((a, b) => a > b ? -1 : 1);
}
noteIds = noteIds.slice(0, ps.limit);
if (noteIds.length === 0) {
@@ -112,6 +125,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (me && (note.userId === me.id)) {
return true;
}
if (!ps.withReplies && note.replyId && (me == null || note.replyUserId !== me.id)) return false;
if (me && isUserRelated(note, userIdsWhoBlockingMe)) return false;
if (me && isUserRelated(note, userIdsWhoMeMuting)) return false;
if (note.renoteId) {

View File

@@ -150,7 +150,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.leftJoinAndSelect('reply.user', 'replyUser')
.leftJoinAndSelect('renote.user', 'renoteUser');
if (!ps.withChannelNotes) {
if (ps.withChannelNotes) {
if (!isSelf) query.andWhere('channel.isSensitive = false');
} else {
query.andWhere('note.channelId IS NULL');
}

View File

@@ -19,6 +19,7 @@ class HybridTimelineChannel extends Channel {
public static shouldShare = false;
public static requireCredential = true;
private withRenotes: boolean;
private withReplies: boolean;
private withFiles: boolean;
constructor(
@@ -39,6 +40,7 @@ class HybridTimelineChannel extends Channel {
if (!policies.ltlAvailable) return;
this.withRenotes = params.withRenotes ?? true;
this.withReplies = params.withReplies ?? false;
this.withFiles = params.withFiles ?? false;
// Subscribe events
@@ -90,7 +92,7 @@ class HybridTimelineChannel extends Channel {
if (isInstanceMuted(note, new Set<string>(this.userProfile!.mutedInstances ?? []))) return;
// 関係ない返信は除外
if (note.reply && !this.following[note.userId]?.withReplies) {
if (note.reply && !this.following[note.userId]?.withReplies && !this.withReplies) {
const reply = note.reply;
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return;

View File

@@ -18,6 +18,7 @@ class LocalTimelineChannel extends Channel {
public static shouldShare = false;
public static requireCredential = false;
private withRenotes: boolean;
private withReplies: boolean;
private withFiles: boolean;
constructor(
@@ -38,6 +39,7 @@ class LocalTimelineChannel extends Channel {
if (!policies.ltlAvailable) return;
this.withRenotes = params.withRenotes ?? true;
this.withReplies = params.withReplies ?? false;
this.withFiles = params.withFiles ?? false;
// Subscribe events
@@ -69,7 +71,7 @@ class LocalTimelineChannel extends Channel {
}
// 関係ない返信は除外
if (note.reply && this.user && !this.following[note.userId]?.withReplies) {
if (note.reply && this.user && !this.following[note.userId]?.withReplies && !this.withReplies) {
const reply = note.reply;
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
if (reply.userId !== this.user.id && note.userId !== this.user.id && reply.userId !== note.userId) return;

View File

@@ -19,7 +19,7 @@ function genHost() {
}
function waitForPushToTl() {
return sleep(300);
return sleep(500);
}
let app: INestApplicationContext;
@@ -41,7 +41,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi');
@@ -57,7 +57,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -73,7 +73,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi');
@@ -90,7 +90,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -107,7 +107,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -124,7 +124,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -141,7 +141,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -159,7 +159,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
@@ -178,7 +178,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
@@ -194,12 +194,28 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
});
test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
const [alice, bob] = await Promise.all([signup(), signup()]);
await api('/following/create', { userId: bob.id }, alice);
await sleep(1000);
const aliceNote = await post(alice, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
await waitForPushToTl();
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
});
test.concurrent('自分の他人への返信が含まれる', async () => {
const [alice, bob] = await Promise.all([signup(), signup()]);
@@ -208,7 +224,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
@@ -224,7 +240,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -275,7 +291,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
});
@@ -291,7 +307,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -309,7 +325,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -324,7 +340,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
});
@@ -338,7 +354,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
});
@@ -359,7 +375,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', { withFiles: true }, alice);
const res = await api('/notes/timeline', { limit: 100, withFiles: true }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
@@ -377,7 +393,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
});
@@ -389,7 +405,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi');
@@ -404,7 +420,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi');
@@ -417,7 +433,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
});
@@ -431,7 +447,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
});
@@ -444,7 +460,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'ok');
@@ -459,7 +475,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'ok');
@@ -475,7 +491,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/timeline', {}, alice);
const res = await api('/notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
});
@@ -490,12 +506,26 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/local-timeline', {}, alice);
const res = await api('/notes/local-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
});
test.concurrent('他人の他人への返信が含まれない', async () => {
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
const carolNote = await post(carol, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
await waitForPushToTl();
const res = await api('/notes/local-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
});
test.concurrent('チャンネル投稿が含まれない', async () => {
const [alice, bob] = await Promise.all([signup(), signup()]);
@@ -504,7 +534,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/local-timeline', {}, alice);
const res = await api('/notes/local-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
});
@@ -516,7 +546,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/local-timeline', {}, alice);
const res = await api('/notes/local-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
});
@@ -532,7 +562,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/local-timeline', {}, alice);
const res = await api('/notes/local-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -548,7 +578,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/local-timeline', {}, alice);
const res = await api('/notes/local-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -565,7 +595,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/local-timeline', {}, alice);
const res = await api('/notes/local-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -583,12 +613,41 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/local-timeline', {}, alice);
const res = await api('/notes/local-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
});
test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
const [alice, bob] = await Promise.all([signup(), signup()]);
await api('/following/create', { userId: bob.id }, alice);
await sleep(1000);
const aliceNote = await post(alice, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
await waitForPushToTl();
const res = await api('/notes/local-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
});
test.concurrent('[withReplies: true] 他人の他人への返信が含まれる', async () => {
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
const carolNote = await post(carol, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
await waitForPushToTl();
const res = await api('/notes/local-timeline', { limit: 100, withReplies: true }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
});
test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => {
const [alice, bob] = await Promise.all([signup(), signup()]);
@@ -598,7 +657,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/local-timeline', { withFiles: true }, alice);
const res = await api('/notes/local-timeline', { limit: 100, withFiles: true }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
@@ -613,7 +672,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/hybrid-timeline', {}, alice);
const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
});
@@ -625,7 +684,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/hybrid-timeline', {}, alice);
const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
});
@@ -639,11 +698,41 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/hybrid-timeline', {}, alice);
const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
});
test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
const [alice, bob] = await Promise.all([signup(), signup()]);
await api('/following/create', { userId: bob.id }, alice);
await sleep(1000);
const aliceNote = await post(alice, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
await waitForPushToTl();
const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
});
test.concurrent('他人の他人への返信が含まれない', async () => {
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
const carolNote = await post(carol, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
await waitForPushToTl();
const res = await api('/notes/hybrid-timeline', { }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
});
test.concurrent('リモートユーザーのノートが含まれない', async () => {
const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
@@ -651,7 +740,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/local-timeline', {}, alice);
const res = await api('/notes/local-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
});
@@ -665,7 +754,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/hybrid-timeline', {}, alice);
const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
});
@@ -679,7 +768,20 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/hybrid-timeline', {}, alice);
const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
});
test.concurrent('[withReplies: true] 他人の他人への返信が含まれる', async () => {
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
const carolNote = await post(carol, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
await waitForPushToTl();
const res = await api('/notes/hybrid-timeline', { limit: 100, withReplies: true }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
});
@@ -693,7 +795,7 @@ describe('Timelines', () => {
await waitForPushToTl();
const res = await api('/notes/hybrid-timeline', { withFiles: true }, alice);
const res = await api('/notes/hybrid-timeline', { limit: 100, withFiles: true }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
@@ -779,6 +881,22 @@ describe('Timelines', () => {
assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
});
test.concurrent('withReplies: false でリスインしているフォローしていないユーザーからの自分への返信が含まれる', async () => {
const [alice, bob] = await Promise.all([signup(), signup()]);
const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body);
await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice);
await sleep(1000);
const aliceNote = await post(alice, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
await waitForPushToTl();
const res = await api('/notes/user-list-timeline', { listId: list.id, withReplies: false }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
});
test.concurrent('withReplies: true でリスインしているフォローしていないユーザーの他人への返信が含まれる', async () => {
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
@@ -1034,6 +1152,32 @@ describe('Timelines', () => {
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
});
test.concurrent('[withChannelNotes: true] 他人が取得した場合センシティブチャンネル投稿が含まれない', async () => {
const [alice, bob] = await Promise.all([signup(), signup()]);
const channel = await api('/channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body);
const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
await waitForPushToTl();
const res = await api('/users/notes', { userId: bob.id, withChannelNotes: true }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
});
test.concurrent('[withChannelNotes: true] 自分が取得した場合センシティブチャンネル投稿が含まれる', async () => {
const [bob] = await Promise.all([signup()]);
const channel = await api('/channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body);
const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
await waitForPushToTl();
const res = await api('/users/notes', { userId: bob.id, withChannelNotes: true }, bob);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
});
test.concurrent('ミュートしているユーザーに関連する投稿が含まれない', async () => {
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);