Skip to content

Commit

Permalink
feat: タイムラインから投稿を除外するオプション (#82)
Browse files Browse the repository at this point in the history
* [WIP]feat: withoutBots

* feat: withoutBot

* FTT有効時に想定の真逆の挙動をとるのを修正
  • Loading branch information
1673beta authored Jul 9, 2024
1 parent cd898e0 commit 44a66c4
Show file tree
Hide file tree
Showing 17 changed files with 114 additions and 3 deletions.
6 changes: 6 additions & 0 deletions packages/backend/src/core/FanoutTimelineEndpointService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type TimelineOptions = {
excludeReplies?: boolean;
excludePureRenotes: boolean;
withCats: boolean;
withoutBots: boolean;
dbFallback: (untilId: string | null, sinceId: string | null, limit: number) => Promise<MiNote[]>,
};

Expand Down Expand Up @@ -104,6 +105,11 @@ export class FanoutTimelineEndpointService {
filter = (note) => (note.user ? note.user.isCat : false) && parentFilter(note);
}

if (ps.withoutBots) {
const parentFilter = filter;
filter = (note) => (!note.user || !note.user.isBot) && parentFilter(note);
}

if (ps.me) {
const me = ps.me;
const [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
redisTimelines: [`channelTimeline:${channel.id}`],
excludePureRenotes: false,
withCats: false,
withoutBots: false,
dbFallback: async (untilId, sinceId, limit) => {
return await this.getFromDb({ untilId, sinceId, limit, channelId: channel.id }, me);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const paramDef = {
withFiles: { type: 'boolean', default: false },
withRenotes: { type: 'boolean', default: true },
withCats: { type: 'boolean', default: false },
withoutBots: { type: 'boolean', default: false },
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
sinceId: { type: 'string', format: 'misskey:id' },
untilId: { type: 'string', format: 'misskey:id' },
Expand Down Expand Up @@ -102,6 +103,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (ps.withCats) {
query.andWhere('(select "isCat" from "user" where id = note."userId")');
}

if (ps.withoutBots) {
query.andWhere('(SELECT "isBot" FROM "user" WHERE id = note."userId") = FALSE');
}
//#endregion

const timeline = await query.limit(ps.limit).getMany();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export const paramDef = {
withRenotes: { type: 'boolean', default: true },
withReplies: { type: 'boolean', default: false },
withCats: { type: 'boolean', default: false },
withoutBots: { type: 'boolean', default: false },
},
required: [],
} as const;
Expand Down Expand Up @@ -115,6 +116,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
withFiles: ps.withFiles,
withReplies: ps.withReplies,
withCats: ps.withCats,
withoutBots: ps.withoutBots,
}, me);

process.nextTick(() => {
Expand Down Expand Up @@ -155,6 +157,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
alwaysIncludeMyNotes: true,
excludePureRenotes: !ps.withRenotes,
withCats: ps.withCats,
withoutBots: ps.withoutBots,
dbFallback: async (untilId, sinceId, limit) => await this.getFromDb({
untilId,
sinceId,
Expand All @@ -165,6 +168,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
withFiles: ps.withFiles,
withReplies: ps.withReplies,
withCats: ps.withCats,
withoutBots: ps.withoutBots,
}, me),
});

Expand All @@ -186,6 +190,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
withFiles: boolean,
withReplies: boolean,
withCats: boolean,
withoutBots: boolean,
}, me: MiLocalUser) {
const followees = await this.userFollowingService.getFollowees(me.id);
const followingChannels = await this.channelFollowingsRepository.find({
Expand Down Expand Up @@ -276,6 +281,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (ps.withCats) {
query.andWhere('(select "isCat" from "user" where id = note."userId")');
}

if (ps.withoutBots) {
query.andWhere('(SELECT "isBot" FROM "user" WHERE id = note."userId") = FALSE');
}
//#endregion

return await query.limit(ps.limit).getMany();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const paramDef = {
withRenotes: { type: 'boolean', default: true },
withReplies: { type: 'boolean', default: false },
withCats: { type: 'boolean', default: false },
withoutBots: { type: 'boolean', default: false },
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
sinceId: { type: 'string', format: 'misskey:id' },
untilId: { type: 'string', format: 'misskey:id' },
Expand Down Expand Up @@ -100,6 +101,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
withFiles: ps.withFiles,
withReplies: ps.withReplies,
withCats: ps.withCats,
withoutBots: ps.withoutBots,
}, me);

process.nextTick(() => {
Expand All @@ -126,13 +128,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
alwaysIncludeMyNotes: true,
excludePureRenotes: !ps.withRenotes,
withCats: ps.withCats,
withoutBots: ps.withoutBots,
dbFallback: async (untilId, sinceId, limit) => await this.getFromDb({
untilId,
sinceId,
limit,
withFiles: ps.withFiles,
withReplies: ps.withReplies,
withCats: ps.withCats,
withoutBots: ps.withoutBots,
}, me),
});

Expand All @@ -153,6 +157,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
withFiles: boolean,
withReplies: boolean,
withCats: boolean,
withoutBots: boolean,
}, me: MiLocalUser | null) {
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'),
ps.sinceId, ps.untilId)
Expand Down Expand Up @@ -188,6 +193,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
query.andWhere('(select "isCat" from "user" where id = note."userId")');
}

if (ps.withoutBots) {
query.andWhere('(SELECT "isBot" FROM "user" WHERE id = note."userId") = FALSE');
}

return await query.limit(ps.limit).getMany();
}
}
21 changes: 20 additions & 1 deletion packages/backend/src/server/api/endpoints/notes/timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export const paramDef = {
withFiles: { type: 'boolean', default: false },
withRenotes: { type: 'boolean', default: true },
withCats: { type: 'boolean', default: false },
withoutBots: { type: 'boolean', default: false },
},
required: [],
} as const;
Expand Down Expand Up @@ -89,6 +90,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
withFiles: ps.withFiles,
withRenotes: ps.withRenotes,
withCats: ps.withCats,
withoutBots: ps.withoutBots,
}, me);

process.nextTick(() => {
Expand All @@ -115,6 +117,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
alwaysIncludeMyNotes: true,
excludePureRenotes: !ps.withRenotes,
withCats: ps.withCats,
withoutBots: ps.withoutBots,
noteFilter: note => {
if (note.reply && note.reply.visibility === 'followers') {
if (!Object.hasOwn(followings, note.reply.userId)) return false;
Expand All @@ -132,6 +135,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
withFiles: ps.withFiles,
withRenotes: ps.withRenotes,
withCats: ps.withCats,
withoutBots: ps.withoutBots,
}, me),
});

Expand All @@ -143,7 +147,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
});
}

private async getFromDb(ps: { untilId: string | null; sinceId: string | null; limit: number; includeMyRenotes: boolean; includeRenotedMyNotes: boolean; includeLocalRenotes: boolean; withFiles: boolean; withRenotes: boolean; withCats: boolean; }, me: MiLocalUser) {
private async getFromDb(ps: {
untilId: string | null;
sinceId: string | null;
limit: number;
includeMyRenotes: boolean;
includeRenotedMyNotes: boolean;
includeLocalRenotes: boolean;
withFiles: boolean;
withRenotes: boolean;
withCats: boolean;
withoutBots: boolean;
}, me: MiLocalUser) {
const followees = await this.userFollowingService.getFollowees(me.id);
const followingChannels = await this.channelFollowingsRepository.find({
where: {
Expand Down Expand Up @@ -249,6 +264,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (ps.withCats) {
query.andWhere('(select "isCat" from "user" where id = note."userId")');
}

if (ps.withoutBots) {
query.andWhere('(SELECT "isBot" FROM "user" WHERE id = note."userId") = FALSE');
}
//#endregion

return await query.limit(ps.limit).getMany();
Expand Down
9 changes: 9 additions & 0 deletions packages/backend/src/server/api/endpoints/users/notes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export const paramDef = {
withReplies: { type: 'boolean', default: false },
withRenotes: { type: 'boolean', default: true },
withChannelNotes: { type: 'boolean', default: false },
withoutBots: { type: 'boolean', default: false },
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
sinceId: { type: 'string', format: 'misskey:id' },
untilId: { type: 'string', format: 'misskey:id' },
Expand Down Expand Up @@ -105,6 +106,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
withFiles: ps.withFiles,
withRenotes: ps.withRenotes,
withCats: ps.withCats,
withoutBots: ps.withoutBots,
}, me);

return await this.noteEntityService.packMany(timeline, me);
Expand All @@ -130,6 +132,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
excludeNoFiles: ps.withChannelNotes && ps.withFiles, // userTimelineWithChannel may include notes without files
excludePureRenotes: !ps.withRenotes,
withCats: ps.withCats,
withoutBots: ps.withoutBots,
noteFilter: note => {
if (note.channel?.isSensitive && !isSelf) return false;
if (note.visibility === 'specified' && (!me || (me.id !== note.userId && !note.visibleUserIds.some(v => v === me.id)))) return false;
Expand All @@ -146,6 +149,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
withFiles: ps.withFiles,
withRenotes: ps.withRenotes,
withCats: ps.withCats,
withoutBots: ps.withoutBots,
}, me),
});

Expand All @@ -162,6 +166,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
withFiles: boolean,
withCats: boolean,
withRenotes: boolean,
withoutBots: boolean,
}, me: MiLocalUser | null) {
const isSelf = me && (me.id === ps.userId);

Expand Down Expand Up @@ -207,6 +212,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
query.andWhere('(select "isCat" from "user" where id = note."userId")');
}

if (ps.withoutBots) {
query.andWhere('(SELECT "isBot" FROM "user" WHERE id = note."userId") = FALSE');
}

return await query.limit(ps.limit).getMany();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class GlobalTimelineChannel extends Channel {
public static requireCredential = false as const;
private withRenotes: boolean;
private withFiles: boolean;
private withoutBots: boolean;

constructor(
private metaService: MetaService,
Expand All @@ -38,6 +39,7 @@ class GlobalTimelineChannel extends Channel {

this.withRenotes = params.withRenotes ?? true;
this.withFiles = params.withFiles ?? false;
this.withoutBots = params.withoutBots ?? false;

// Subscribe events
this.subscriber.on('notesStream', this.onNote);
Expand All @@ -46,6 +48,7 @@ class GlobalTimelineChannel extends Channel {
@bindThis
private async onNote(note: Packed<'Note'>) {
if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return;
if (this.withoutBots && note.user.isBot) return;

if (note.visibility !== 'public') return;
if (note.channelId != null) return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class HomeTimelineChannel extends Channel {
public static kind = 'read:account';
private withRenotes: boolean;
private withFiles: boolean;
private withoutBots: boolean;

constructor(
private noteEntityService: NoteEntityService,
Expand All @@ -32,6 +33,7 @@ class HomeTimelineChannel extends Channel {
public async init(params: any) {
this.withRenotes = params.withRenotes ?? true;
this.withFiles = params.withFiles ?? false;
this.withoutBots = params.withoutBots ?? false;

this.subscriber.on('notesStream', this.onNote);
}
Expand All @@ -41,6 +43,7 @@ class HomeTimelineChannel extends Channel {
const isMe = this.user!.id === note.userId;

if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return;
if (this.withoutBots && note.user.isBot) return;

if (note.channelId) {
if (!this.followingChannels.has(note.channelId)) return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class HybridTimelineChannel extends Channel {
private withRenotes: boolean;
private withReplies: boolean;
private withFiles: boolean;
private withoutBots: boolean;

constructor(
private metaService: MetaService,
Expand All @@ -41,6 +42,7 @@ class HybridTimelineChannel extends Channel {
this.withRenotes = params.withRenotes ?? true;
this.withReplies = params.withReplies ?? false;
this.withFiles = params.withFiles ?? false;
this.withoutBots = params.withoutBots ?? false;

// Subscribe events
this.subscriber.on('notesStream', this.onNote);
Expand All @@ -51,6 +53,7 @@ class HybridTimelineChannel extends Channel {
const isMe = this.user!.id === note.userId;

if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return;
if (this.withoutBots && note.user.isBot) return;

// チャンネルの投稿ではなく、自分自身の投稿 または
// チャンネルの投稿ではなく、その投稿のユーザーをフォローしている または
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class LocalTimelineChannel extends Channel {
private withRenotes: boolean;
private withReplies: boolean;
private withFiles: boolean;
private withoutBots: boolean;

constructor(
private metaService: MetaService,
Expand All @@ -40,6 +41,7 @@ class LocalTimelineChannel extends Channel {
this.withRenotes = params.withRenotes ?? true;
this.withReplies = params.withReplies ?? false;
this.withFiles = params.withFiles ?? false;
this.withoutBots = params.withoutBots ?? false;

// Subscribe events
this.subscriber.on('notesStream', this.onNote);
Expand All @@ -48,6 +50,7 @@ class LocalTimelineChannel extends Channel {
@bindThis
private async onNote(note: Packed<'Note'>) {
if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return;
if (this.withoutBots && note.user.isBot) return;

if (note.user.host !== null) return;
if (note.visibility !== 'public') return;
Expand Down
4 changes: 4 additions & 0 deletions packages/cherrypick-js/etc/cherrypick-js.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,7 @@ export type Channels = {
withRenotes?: boolean;
withFiles?: boolean;
withCats?: boolean;
withoutBots?: boolean;
};
events: {
note: (payload: Note) => void;
Expand All @@ -664,6 +665,7 @@ export type Channels = {
withReplies?: boolean;
withFiles?: boolean;
withCats?: boolean;
withoutBots?: boolean;
};
events: {
note: (payload: Note) => void;
Expand All @@ -676,6 +678,7 @@ export type Channels = {
withReplies?: boolean;
withFiles?: boolean;
withCats?: boolean;
withoutBots?: boolean;
};
events: {
note: (payload: Note) => void;
Expand All @@ -687,6 +690,7 @@ export type Channels = {
withRenotes?: boolean;
withFiles?: boolean;
withCats?: boolean;
withoutBots?: boolean;
};
events: {
note: (payload: Note) => void;
Expand Down
Loading

0 comments on commit 44a66c4

Please sign in to comment.