From 375cf97c35160250ac20944bcae2808945ece568 Mon Sep 17 00:00:00 2001 From: Esurio Date: Sun, 7 Jul 2024 14:51:40 +0000 Subject: [PATCH 1/3] [WIP]feat: withoutBots --- .../src/core/FanoutTimelineEndpointService.ts | 6 ++++++ .../api/endpoints/notes/global-timeline.ts | 5 +++++ .../server/api/endpoints/notes/timeline.ts | 21 ++++++++++++++++++- .../src/server/api/endpoints/users/notes.ts | 9 ++++++++ .../api/stream/channels/global-timeline.ts | 2 ++ packages/cherrypick-js/src/autogen/types.ts | 2 ++ .../frontend/src/components/MkTimeline.vue | 5 ++++- 7 files changed, 48 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts index e3dc94e4f8..42cad6b712 100644 --- a/packages/backend/src/core/FanoutTimelineEndpointService.ts +++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts @@ -34,6 +34,7 @@ type TimelineOptions = { excludeReplies?: boolean; excludePureRenotes: boolean; withCats: boolean; + withoutBots: boolean; dbFallback: (untilId: string | null, sinceId: string | null, limit: number) => Promise, }; @@ -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 : true) && parentFilter(note); + } + if (ps.me) { const me = ps.me; const [ diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index 5e3552377a..91feb48e03 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -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' }, @@ -102,6 +103,10 @@ export default class extends Endpoint { // 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(); diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 11a2be2723..a6b6cc9150 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -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; @@ -89,6 +90,7 @@ export default class extends Endpoint { // eslint- withFiles: ps.withFiles, withRenotes: ps.withRenotes, withCats: ps.withCats, + withoutBots: ps.withoutBots, }, me); process.nextTick(() => { @@ -115,6 +117,7 @@ export default class extends Endpoint { // 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; @@ -132,6 +135,7 @@ export default class extends Endpoint { // eslint- withFiles: ps.withFiles, withRenotes: ps.withRenotes, withCats: ps.withCats, + withoutBots: ps.withoutBots, }, me), }); @@ -143,7 +147,18 @@ export default class extends Endpoint { // 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: { @@ -249,6 +264,10 @@ export default class extends Endpoint { // 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(); diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index f1b34ced5f..e1f2a13802 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -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' }, @@ -105,6 +106,7 @@ export default class extends Endpoint { // eslint- withFiles: ps.withFiles, withRenotes: ps.withRenotes, withCats: ps.withCats, + withoutBots: ps.withoutBots, }, me); return await this.noteEntityService.packMany(timeline, me); @@ -130,6 +132,7 @@ export default class extends Endpoint { // 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; @@ -146,6 +149,7 @@ export default class extends Endpoint { // eslint- withFiles: ps.withFiles, withRenotes: ps.withRenotes, withCats: ps.withCats, + withoutBots: ps.withoutBots, }, me), }); @@ -162,6 +166,7 @@ export default class extends Endpoint { // eslint- withFiles: boolean, withCats: boolean, withRenotes: boolean, + withoutBots: boolean, }, me: MiLocalUser | null) { const isSelf = me && (me.id === ps.userId); @@ -207,6 +212,10 @@ export default class extends Endpoint { // 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(); } } diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index 17116258d8..03ac3701ed 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -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, @@ -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); diff --git a/packages/cherrypick-js/src/autogen/types.ts b/packages/cherrypick-js/src/autogen/types.ts index d401899871..27cf5bed96 100644 --- a/packages/cherrypick-js/src/autogen/types.ts +++ b/packages/cherrypick-js/src/autogen/types.ts @@ -22842,6 +22842,8 @@ export type operations = { withRenotes?: boolean; /** @default false */ withCats?: boolean; + /** @default false */ + withoutBots?: boolean; /** @default 10 */ limit?: number; /** Format: misskey:id */ diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 34e0e83126..0ffc466dd2 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -41,11 +41,13 @@ const props = withDefaults(defineProps<{ withReplies?: boolean; onlyFiles?: boolean; onlyCats?: boolean; + withoutBots?: boolean; }>(), { withRenotes: true, withReplies: false, onlyFiles: false, onlyCats: false, + withoutBots: false, }); const emit = defineEmits<{ @@ -62,6 +64,7 @@ type TimelineQueryType = { withReplies?: boolean, withFiles?: boolean, withCats?: boolean, + withoutBots?: boolean, visibility?: string, listId?: string, channelId?: string, @@ -121,7 +124,6 @@ function connectChannel() { } else if (props.src === 'media') { connection = stream.useChannel('globalTimeline', { withRenotes: props.withRenotes, - withReplies: props.withReplies, withFiles: true, withCats: props.onlyCats, }, @@ -138,6 +140,7 @@ function connectChannel() { withRenotes: props.withRenotes, withFiles: props.onlyFiles ? true : undefined, withCats: props.onlyCats, + withoutBots: props.withoutBots, }); } else if (props.src === 'mentions') { connection = stream.useChannel('main'); From 1d926ab7e6bc4d5f243c8fccab0f9ac79c03d484 Mon Sep 17 00:00:00 2001 From: Esurio Date: Tue, 9 Jul 2024 10:18:07 +0000 Subject: [PATCH 2/3] feat: withoutBot --- .../src/server/api/endpoints/channels/timeline.ts | 1 + .../server/api/endpoints/notes/hybrid-timeline.ts | 9 +++++++++ .../src/server/api/endpoints/notes/local-timeline.ts | 9 +++++++++ .../server/api/stream/channels/global-timeline.ts | 1 + .../src/server/api/stream/channels/home-timeline.ts | 3 +++ .../server/api/stream/channels/hybrid-timeline.ts | 3 +++ .../src/server/api/stream/channels/local-timeline.ts | 3 +++ packages/cherrypick-js/etc/cherrypick-js.api.md | 4 ++++ packages/cherrypick-js/src/autogen/types.ts | 8 ++++++++ packages/cherrypick-js/src/streaming.types.ts | 4 ++++ packages/frontend/src/components/MkTimeline.vue | 9 +++++++++ packages/frontend/src/pages/timeline.vue | 12 +++++++++++- packages/frontend/src/store.ts | 1 + 13 files changed, 66 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index bd6be1783f..a2de0d570b 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -102,6 +102,7 @@ export default class extends Endpoint { // 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); }, diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 6e406dc6b0..67e5ce2e6c 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -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; @@ -115,6 +116,7 @@ export default class extends Endpoint { // eslint- withFiles: ps.withFiles, withReplies: ps.withReplies, withCats: ps.withCats, + withoutBots: ps.withoutBots, }, me); process.nextTick(() => { @@ -155,6 +157,7 @@ export default class extends Endpoint { // eslint- alwaysIncludeMyNotes: true, excludePureRenotes: !ps.withRenotes, withCats: ps.withCats, + withoutBots: ps.withoutBots, dbFallback: async (untilId, sinceId, limit) => await this.getFromDb({ untilId, sinceId, @@ -165,6 +168,7 @@ export default class extends Endpoint { // eslint- withFiles: ps.withFiles, withReplies: ps.withReplies, withCats: ps.withCats, + withoutBots: ps.withoutBots, }, me), }); @@ -186,6 +190,7 @@ export default class extends Endpoint { // 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({ @@ -276,6 +281,10 @@ export default class extends Endpoint { // 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(); diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 60ba408f4d..838e8d49b3 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -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' }, @@ -100,6 +101,7 @@ export default class extends Endpoint { // eslint- withFiles: ps.withFiles, withReplies: ps.withReplies, withCats: ps.withCats, + withoutBots: ps.withoutBots, }, me); process.nextTick(() => { @@ -126,6 +128,7 @@ export default class extends Endpoint { // eslint- alwaysIncludeMyNotes: true, excludePureRenotes: !ps.withRenotes, withCats: ps.withCats, + withoutBots: ps.withoutBots, dbFallback: async (untilId, sinceId, limit) => await this.getFromDb({ untilId, sinceId, @@ -133,6 +136,7 @@ export default class extends Endpoint { // eslint- withFiles: ps.withFiles, withReplies: ps.withReplies, withCats: ps.withCats, + withoutBots: ps.withoutBots, }, me), }); @@ -153,6 +157,7 @@ export default class extends Endpoint { // 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) @@ -188,6 +193,10 @@ export default class extends Endpoint { // 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(); } } diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index 03ac3701ed..ad63f070df 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -48,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; diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index 878a3180cb..82c077da9b 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -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, @@ -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); } @@ -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; diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index 889ee0c971..65a120e9e1 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -20,6 +20,7 @@ class HybridTimelineChannel extends Channel { private withRenotes: boolean; private withReplies: boolean; private withFiles: boolean; + private withoutBots: boolean; constructor( private metaService: MetaService, @@ -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); @@ -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; // チャンネルの投稿ではなく、自分自身の投稿 または // チャンネルの投稿ではなく、その投稿のユーザーをフォローしている または diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index 442d08ae51..9fe642ec0c 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -19,6 +19,7 @@ class LocalTimelineChannel extends Channel { private withRenotes: boolean; private withReplies: boolean; private withFiles: boolean; + private withoutBots: boolean; constructor( private metaService: MetaService, @@ -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); @@ -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; diff --git a/packages/cherrypick-js/etc/cherrypick-js.api.md b/packages/cherrypick-js/etc/cherrypick-js.api.md index 00dd561bed..c9a9b8bb92 100644 --- a/packages/cherrypick-js/etc/cherrypick-js.api.md +++ b/packages/cherrypick-js/etc/cherrypick-js.api.md @@ -651,6 +651,7 @@ export type Channels = { withRenotes?: boolean; withFiles?: boolean; withCats?: boolean; + withoutBots?: boolean; }; events: { note: (payload: Note) => void; @@ -663,6 +664,7 @@ export type Channels = { withReplies?: boolean; withFiles?: boolean; withCats?: boolean; + withoutBots?: boolean; }; events: { note: (payload: Note) => void; @@ -675,6 +677,7 @@ export type Channels = { withReplies?: boolean; withFiles?: boolean; withCats?: boolean; + withoutBots?: boolean; }; events: { note: (payload: Note) => void; @@ -686,6 +689,7 @@ export type Channels = { withRenotes?: boolean; withFiles?: boolean; withCats?: boolean; + withoutBots?: boolean; }; events: { note: (payload: Note) => void; diff --git a/packages/cherrypick-js/src/autogen/types.ts b/packages/cherrypick-js/src/autogen/types.ts index 27cf5bed96..b4b320e427 100644 --- a/packages/cherrypick-js/src/autogen/types.ts +++ b/packages/cherrypick-js/src/autogen/types.ts @@ -22928,6 +22928,8 @@ export type operations = { withReplies?: boolean; /** @default false */ withCats?: boolean; + /** @default false */ + withoutBots?: boolean; }; }; }; @@ -22988,6 +22990,8 @@ export type operations = { withReplies?: boolean; /** @default false */ withCats?: boolean; + /** @default false */ + withoutBots?: boolean; /** @default 10 */ limit?: number; /** Format: misskey:id */ @@ -24095,6 +24099,8 @@ export type operations = { withRenotes?: boolean; /** @default false */ withCats?: boolean; + /** @default false */ + withoutBots?: boolean; }; }; }; @@ -28324,6 +28330,8 @@ export type operations = { withRenotes?: boolean; /** @default false */ withChannelNotes?: boolean; + /** @default false */ + withoutBots?: boolean; /** @default 10 */ limit?: number; /** Format: misskey:id */ diff --git a/packages/cherrypick-js/src/streaming.types.ts b/packages/cherrypick-js/src/streaming.types.ts index 6b997cad44..08c8732731 100644 --- a/packages/cherrypick-js/src/streaming.types.ts +++ b/packages/cherrypick-js/src/streaming.types.ts @@ -70,6 +70,7 @@ export type Channels = { withRenotes?: boolean; withFiles?: boolean; withCats?: boolean; + withoutBots?: boolean; }; events: { note: (payload: Note) => void; @@ -82,6 +83,7 @@ export type Channels = { withReplies?: boolean; withFiles?: boolean; withCats?: boolean; + withoutBots?: boolean; }; events: { note: (payload: Note) => void; @@ -94,6 +96,7 @@ export type Channels = { withReplies?: boolean; withFiles?: boolean; withCats?: boolean; + withoutBots?: boolean; }; events: { note: (payload: Note) => void; @@ -105,6 +108,7 @@ export type Channels = { withRenotes?: boolean; withFiles?: boolean; withCats?: boolean; + withoutBots?: boolean; }; events: { note: (payload: Note) => void; diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 0ffc466dd2..17a6eff066 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -112,6 +112,7 @@ function connectChannel() { withRenotes: props.withRenotes, withFiles: props.onlyFiles ? true : undefined, withCats: props.onlyCats, + withoutBots: props.withoutBots, }); connection2 = stream.useChannel('main'); } else if (props.src === 'local') { @@ -120,12 +121,14 @@ function connectChannel() { withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, withCats: props.onlyCats, + withoutBots: props.withoutBots, }); } else if (props.src === 'media') { connection = stream.useChannel('globalTimeline', { withRenotes: props.withRenotes, withFiles: true, withCats: props.onlyCats, + withoutBots: props.withoutBots, }, ); } else if (props.src === 'social') { @@ -134,6 +137,7 @@ function connectChannel() { withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, withCats: props.onlyCats, + withoutBots: props.withoutBots, }); } else if (props.src === 'global') { connection = stream.useChannel('globalTimeline', { @@ -195,6 +199,7 @@ function updatePaginationQuery() { withRenotes: props.withRenotes, withFiles: props.onlyFiles ? true : undefined, withCats: props.onlyCats, + withoutBots: props.withoutBots, }; } else if (props.src === 'local') { endpoint = 'notes/local-timeline'; @@ -203,6 +208,7 @@ function updatePaginationQuery() { withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, withCats: props.onlyCats, + withoutBots: props.withoutBots, }; } else if (props.src === 'media') { endpoint = 'notes/global-timeline'; @@ -211,6 +217,7 @@ function updatePaginationQuery() { withReplies: props.withReplies, withFiles: true, withCats: props.onlyCats, + withoutBots: props.withoutBots, }; } else if (props.src === 'social') { endpoint = 'notes/hybrid-timeline'; @@ -219,6 +226,7 @@ function updatePaginationQuery() { withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, withCats: props.onlyCats, + withoutBots: props.withoutBots, }; } else if (props.src === 'global') { endpoint = 'notes/global-timeline'; @@ -226,6 +234,7 @@ function updatePaginationQuery() { withRenotes: props.withRenotes, withFiles: props.onlyFiles ? true : undefined, withCats: props.onlyCats, + withoutBots: props.withoutBots, }; } else if (props.src === 'mentions') { endpoint = 'notes/mentions'; diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 7ecac3ac3b..654e23e732 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -39,13 +39,14 @@ SPDX-License-Identifier: AGPL-3.0-only
@@ -140,6 +141,11 @@ const onlyCats = computed({ set: (x: boolean) => saveTlFilter('onlyCats', x), }); +const withoutBots = computed({ + get: () => defaultStore.reactiveState.tl.value.filter.withoutBots, + set: (x: boolean) => saveTlFilter('withoutBots', x), +}); + watch([withReplies, onlyFiles], ([withRepliesTo, onlyFilesTo]) => { if (withRepliesTo) { localSocialTLFilterSwitchStore.value = 'withReplies'; @@ -346,6 +352,10 @@ const headerActions = computed(() => { type: 'switch', text: i18n.ts.showCatOnly, ref: onlyCats, + }, { + type: 'switch', + text: i18n.ts.antennaExcludeBots, + ref: withoutBots, }], ev.currentTarget ?? ev.target); }, }, diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 27de24d449..a5ae0e4641 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -200,6 +200,7 @@ export const defaultStore = markRaw(new Storage('base', { withSensitive: true, onlyFiles: false, onlyCats: false, + withoutBots: false, }, }, }, From bca223f7214d6472ad9e64c6c774fdbbfe9d3931 Mon Sep 17 00:00:00 2001 From: Esurio Date: Tue, 9 Jul 2024 11:10:31 +0000 Subject: [PATCH 3/3] =?UTF-8?q?FTT=E6=9C=89=E5=8A=B9=E6=99=82=E3=81=AB?= =?UTF-8?q?=E6=83=B3=E5=AE=9A=E3=81=AE=E7=9C=9F=E9=80=86=E3=81=AE=E6=8C=99?= =?UTF-8?q?=E5=8B=95=E3=82=92=E3=81=A8=E3=82=8B=E3=81=AE=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/FanoutTimelineEndpointService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts index 42cad6b712..91c471ea96 100644 --- a/packages/backend/src/core/FanoutTimelineEndpointService.ts +++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts @@ -107,7 +107,7 @@ export class FanoutTimelineEndpointService { if (ps.withoutBots) { const parentFilter = filter; - filter = (note) => (note.user ? note.user.isBot : true) && parentFilter(note); + filter = (note) => (!note.user || !note.user.isBot) && parentFilter(note); } if (ps.me) {