diff options
| author | Mar0xy <marie@kaifa.ch> | 2023-12-04 02:10:51 +0100 |
|---|---|---|
| committer | Mar0xy <marie@kaifa.ch> | 2023-12-04 02:10:51 +0100 |
| commit | 2f99c7e9dc2e5e3ca06c9672a6ab4887eb094310 (patch) | |
| tree | aa9801e261ed978d553cfc8cd80fee524d6496a6 /packages/backend/src/server/api/stream | |
| parent | upd: add additional check to visibility selector for boost (diff) | |
| download | sharkey-2f99c7e9dc2e5e3ca06c9672a6ab4887eb094310.tar.gz sharkey-2f99c7e9dc2e5e3ca06c9672a6ab4887eb094310.tar.bz2 sharkey-2f99c7e9dc2e5e3ca06c9672a6ab4887eb094310.zip | |
add: Bubble timeline
Closes transfem-org/Sharkey#154
Diffstat (limited to 'packages/backend/src/server/api/stream')
| -rw-r--r-- | packages/backend/src/server/api/stream/ChannelsService.ts | 3 | ||||
| -rw-r--r-- | packages/backend/src/server/api/stream/channels/bubble-timeline.ts | 124 |
2 files changed, 127 insertions, 0 deletions
diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts index 8fd106c10c..f9f2f15aff 100644 --- a/packages/backend/src/server/api/stream/ChannelsService.ts +++ b/packages/backend/src/server/api/stream/ChannelsService.ts @@ -8,6 +8,7 @@ import { bindThis } from '@/decorators.js'; import { HybridTimelineChannelService } from './channels/hybrid-timeline.js'; import { LocalTimelineChannelService } from './channels/local-timeline.js'; import { HomeTimelineChannelService } from './channels/home-timeline.js'; +import { BubbleTimelineChannelService } from './channels/bubble-timeline.js'; import { GlobalTimelineChannelService } from './channels/global-timeline.js'; import { MainChannelService } from './channels/main.js'; import { ChannelChannelService } from './channels/channel.js'; @@ -28,6 +29,7 @@ export class ChannelsService { private localTimelineChannelService: LocalTimelineChannelService, private hybridTimelineChannelService: HybridTimelineChannelService, private globalTimelineChannelService: GlobalTimelineChannelService, + private bubbleTimelineChannelService: BubbleTimelineChannelService, private userListChannelService: UserListChannelService, private hashtagChannelService: HashtagChannelService, private roleTimelineChannelService: RoleTimelineChannelService, @@ -48,6 +50,7 @@ export class ChannelsService { case 'localTimeline': return this.localTimelineChannelService; case 'hybridTimeline': return this.hybridTimelineChannelService; case 'globalTimeline': return this.globalTimelineChannelService; + case 'bubbleTimeline': return this.bubbleTimelineChannelService; case 'userList': return this.userListChannelService; case 'hashtag': return this.hashtagChannelService; case 'roleTimeline': return this.roleTimelineChannelService; diff --git a/packages/backend/src/server/api/stream/channels/bubble-timeline.ts b/packages/backend/src/server/api/stream/channels/bubble-timeline.ts new file mode 100644 index 0000000000..74d5c3ea4e --- /dev/null +++ b/packages/backend/src/server/api/stream/channels/bubble-timeline.ts @@ -0,0 +1,124 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { checkWordMute } from '@/misc/check-word-mute.js'; +import { isInstanceMuted } from '@/misc/is-instance-muted.js'; +import { isUserRelated } from '@/misc/is-user-related.js'; +import type { Packed } from '@/misc/json-schema.js'; +import { MetaService } from '@/core/MetaService.js'; +import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { bindThis } from '@/decorators.js'; +import { RoleService } from '@/core/RoleService.js'; +import type { MiMeta } from '@/models/Meta.js'; +import Channel from '../channel.js'; + +class BubbleTimelineChannel extends Channel { + public readonly chName = 'bubbleTimeline'; + public static shouldShare = false; + public static requireCredential = false; + private withRenotes: boolean; + private withFiles: boolean; + private withBots: boolean; + private instance: MiMeta; + + constructor( + private metaService: MetaService, + private roleService: RoleService, + private noteEntityService: NoteEntityService, + + id: string, + connection: Channel['connection'], + ) { + super(id, connection); + //this.onNote = this.onNote.bind(this); + } + + @bindThis + public async init(params: any) { + const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); + if (!policies.btlAvailable) return; + + this.withRenotes = params.withRenotes ?? true; + this.withFiles = params.withFiles ?? false; + this.withBots = params.withBots ?? true; + this.instance = await this.metaService.fetch(); + + // Subscribe events + this.subscriber.on('notesStream', this.onNote); + } + + @bindThis + private async onNote(note: Packed<'Note'>) { + if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return; + if (!this.withBots && note.user.isBot) return; + + if (!(note.user.host != null && this.instance.bubbleInstances.includes(note.user.host) && note.visibility === 'public' )) return; + + if (note.channelId != null) return; + + // 関係ない返信は除外 + if (note.reply && !this.following[note.userId]?.withReplies) { + const reply = note.reply; + // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 + if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; + } + + if (note.user.isSilenced && !this.following[note.userId] && note.userId !== this.user!.id) return; + + if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return; + + // Ignore notes from instances the user has muted + if (isInstanceMuted(note, new Set<string>(this.userProfile?.mutedInstances ?? []))) return; + + // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する + if (isUserRelated(note, this.userIdsWhoMeMuting)) return; + // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する + if (isUserRelated(note, this.userIdsWhoBlockingMe)) return; + + if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; + + if (this.user && note.renoteId && !note.text) { + if (note.renote && Object.keys(note.renote.reactions).length > 0) { + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id); + note.renote.myReaction = myRenoteReaction; + } + } + + this.connection.cacheNote(note); + + this.send('note', note); + } + + @bindThis + public dispose() { + // Unsubscribe events + this.subscriber.off('notesStream', this.onNote); + } +} + +@Injectable() +export class BubbleTimelineChannelService { + public readonly shouldShare = BubbleTimelineChannel.shouldShare; + public readonly requireCredential = BubbleTimelineChannel.requireCredential; + + constructor( + private metaService: MetaService, + private roleService: RoleService, + private noteEntityService: NoteEntityService, + ) { + } + + @bindThis + public create(id: string, connection: Channel['connection']): BubbleTimelineChannel { + return new BubbleTimelineChannel( + this.metaService, + this.roleService, + this.noteEntityService, + id, + connection, + ); + } +} |