diff --git a/packages/backend/src/misc/is-filtered.ts b/packages/backend/src/misc/is-filtered.ts index 460e8be3b..b4002757c 100644 --- a/packages/backend/src/misc/is-filtered.ts +++ b/packages/backend/src/misc/is-filtered.ts @@ -5,12 +5,20 @@ import { getWordHardMute } from "@/misc/check-word-mute.js"; import { Cache } from "@/misc/cache.js"; import { unique } from "@/prelude/array.js"; import config from "@/config/index.js"; +import { UserProfiles } from "@/models/index.js"; const filteredNoteCache = new Cache("filteredNote", config.wordMuteCache?.ttlSeconds ?? 60 * 60 * 24); +const mutedWordsCache = new Cache("mutedWords", 60 * 5); -export function isFiltered(note: Note, user: { id: User["id"] } | null | undefined, profile: { mutedWords: UserProfile["mutedWords"] } | null): boolean | Promise { - if (!user || !profile) return false; - if (profile.mutedWords.length < 1) return false; - return filteredNoteCache.fetch(`${note.id}:${(note.updatedAt ?? note.createdAt).getTime()}:${user.id}`, - () => getWordHardMute(note, user, unique(profile.mutedWords))); +export async function isFiltered(note: Note, user: { id: User["id"] } | null | undefined, profile?: { mutedWords: UserProfile["mutedWords"] } | null): Promise { + if (!user) return false; + if (profile === undefined) + profile = { mutedWords: await mutedWordsCache.fetch(user.id, async () => + UserProfiles.findOneBy({ userId: user.id }).then(p => p?.mutedWords ?? [])) }; + + if (!profile || profile.mutedWords.length < 1) return false; + const ts = (note.updatedAt ?? note.createdAt) as Date | string; + const identifier = (typeof ts === "string" ? new Date(ts) : ts)?.getTime() ?? '0'; + return filteredNoteCache.fetch(`${note.id}:${ts}:${user.id}`, + () => getWordHardMute(note, user, unique(profile!.mutedWords))); } diff --git a/packages/backend/src/models/repositories/note.ts b/packages/backend/src/models/repositories/note.ts index e43374173..2a3022380 100644 --- a/packages/backend/src/models/repositories/note.ts +++ b/packages/backend/src/models/repositories/note.ts @@ -33,8 +33,6 @@ import { isFiltered } from "@/misc/is-filtered.js"; import { UserProfile } from "@/models/entities/user-profile.js"; import { Cache } from "@/misc/cache.js"; -const mutedWordsCache = new Cache("mutedWords", 60 * 5); - export async function populatePoll(note: Note, meId: User["id"] | null) { const poll = await Polls.findOneByOrFail({ noteId: note.id }); const choices = poll.choices.map((c) => ({ @@ -180,7 +178,6 @@ export const NoteRepository = db.getRepository(Note).extend({ }; }, userCache: PackedUserCache = Users.getFreshPackedUserCache(), - profile?: { mutedWords: UserProfile["mutedWords"] } | null, ): Promise> { const opts = Object.assign( { @@ -193,11 +190,6 @@ export const NoteRepository = db.getRepository(Note).extend({ const note = typeof src === "object" ? src : await this.findOneByOrFail({ id: src }); const host = note.userHost; - const meProfile = profile !== undefined - ? profile - : meId !== null - ? { mutedWords: await mutedWordsCache.fetch(meId, async () => UserProfiles.findOneBy({ userId: meId }).then(p => p?.mutedWords ?? [])) } - : null; if (!(await this.isVisibleForMe(note, meId))) { throw new IdentifiableError( @@ -269,7 +261,7 @@ export const NoteRepository = db.getRepository(Note).extend({ ? { myReaction: populateMyReaction(note, meId, options?._hint_), isRenoted: populateIsRenoted(note, meId, options?._hint_), - isFiltered: isFiltered(note, me, await meProfile), + isFiltered: isFiltered(note, me), } : {}), @@ -338,7 +330,6 @@ export const NoteRepository = db.getRepository(Note).extend({ options?: { detail?: boolean; }, - profile?: { mutedWords: UserProfile["mutedWords"] } | null, ) { if (notes.length === 0) return []; @@ -374,10 +365,6 @@ export const NoteRepository = db.getRepository(Note).extend({ !!myRenotes.find(p => p.renoteId == target), ); } - - profile = profile !== undefined - ? profile - : { mutedWords: await mutedWordsCache.fetch(meId, async () => UserProfiles.findOneBy({ userId: meId }).then(p => p?.mutedWords ?? [])) }; } await prefetchEmojis(aggregateNoteEmojis(notes)); @@ -390,7 +377,7 @@ export const NoteRepository = db.getRepository(Note).extend({ myReactions: myReactionsMap, myRenotes: myRenotesMap }, - }, undefined, profile), + }), ), ); diff --git a/packages/backend/src/server/api/mastodon/converters/note.ts b/packages/backend/src/server/api/mastodon/converters/note.ts index 6951bee5a..bc2b53ff3 100644 --- a/packages/backend/src/server/api/mastodon/converters/note.ts +++ b/packages/backend/src/server/api/mastodon/converters/note.ts @@ -33,6 +33,7 @@ import { unique } from "@/prelude/array.js"; import { NoteReaction } from "@/models/entities/note-reaction.js"; import { Cache } from "@/misc/cache.js"; import { HtmlNoteCacheEntry } from "@/models/entities/html-note-cache-entry.js"; +import { isFiltered } from "@/misc/is-filtered.js"; export class NoteConverter { private static noteContentHtmlCache = new Cache('html:note:content', config.htmlCache?.ttlSeconds ?? 60 * 60); @@ -138,6 +139,21 @@ export class NoteConverter { const reblog = Promise.resolve(renote).then(renote => recurseCounter > 0 && renote ? this.encode(renote, ctx, isQuote(renote) && !isQuote(note) ? --recurseCounter : 0) : null); + const filtered = isFiltered(note, user).then(res => { + if (!res || ctx.filterContext == null || !['home', 'public'].includes(ctx.filterContext)) return null; + return [{ + filter: { + id: '0', + title: 'Hard word mutes', + context: ['home', 'public'], + expires_at: null, + filter_action: 'hide', + keywords: [], + statuses: [], + } + } as MastodonEntity.FilterResult]; + }); + // noinspection ES6MissingAwait return await awaitAll({ id: note.id, @@ -172,7 +188,8 @@ export class NoteConverter { reactions: populated.then(populated => Promise.resolve(reaction).then(reaction => this.encodeReactions(note.reactions, reaction?.reaction, populated))), bookmarked: isBookmarked, quote: reblog.then(reblog => isQuote(note) ? reblog : null), - edited_at: note.updatedAt?.toISOString() ?? null + edited_at: note.updatedAt?.toISOString() ?? null, + filtered: filtered, }); } @@ -293,8 +310,8 @@ export class NoteConverter { }).filter(r => r.count > 0); } - public static async encodeEvent(note: Note, user: ILocalUser | undefined): Promise { - const ctx = getStubMastoContext(user); + public static async encodeEvent(note: Note, user: ILocalUser | undefined, filterContext?: string): Promise { + const ctx = getStubMastoContext(user, filterContext); NoteHelpers.fixupEventNote(note); return NoteConverter.encode(note, ctx); } diff --git a/packages/backend/src/server/api/mastodon/converters/notification.ts b/packages/backend/src/server/api/mastodon/converters/notification.ts index e0c600cf5..fa0235e76 100644 --- a/packages/backend/src/server/api/mastodon/converters/notification.ts +++ b/packages/backend/src/server/api/mastodon/converters/notification.ts @@ -95,8 +95,8 @@ export class NotificationConverter { } } - public static async encodeEvent(target: Notification["id"], user: ILocalUser): Promise { - const ctx = getStubMastoContext(user); + public static async encodeEvent(target: Notification["id"], user: ILocalUser, filterContext?: string): Promise { + const ctx = getStubMastoContext(user, filterContext); const notification = await Notifications.findOneByOrFail({ id: target }); return this.encode(notification, ctx).catch(_ => null); } diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts index 74d7fbbfd..ae642857a 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/account.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/account.ts @@ -6,6 +6,7 @@ import { UserHelpers } from "@/server/api/mastodon/helpers/user.js"; import { ListHelpers } from "@/server/api/mastodon/helpers/list.js"; import { auth } from "@/server/api/mastodon/middleware/auth.js"; import { SearchHelpers } from "@/server/api/mastodon/helpers/search.js"; +import { filterContext } from "@/server/api/mastodon/middleware/filter-context.js"; export function setupEndpointsAccount(router: Router): void { router.get("/v1/accounts/verify_credentials", @@ -52,6 +53,7 @@ export function setupEndpointsAccount(router: Router): void { router.get<{ Params: { id: string } }>( "/v1/accounts/:id/statuses", auth(false, ["read:statuses"]), + filterContext('account'), async (ctx) => { const query = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx); const args = normalizeUrlQuery(argsToBools(limitToInt(ctx.query))); diff --git a/packages/backend/src/server/api/mastodon/endpoints/misc.ts b/packages/backend/src/server/api/mastodon/endpoints/misc.ts index d141b8f65..54e018e11 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/misc.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/misc.ts @@ -4,6 +4,7 @@ import { argsToBools, limitToInt } from "@/server/api/mastodon/endpoints/timelin import { Announcements } from "@/models/index.js"; import { auth } from "@/server/api/mastodon/middleware/auth.js"; import { MastoApiError } from "@/server/api/mastodon/middleware/catch-errors.js"; +import { filterContext } from "@/server/api/mastodon/middleware/filter-context.js"; export function setupEndpointsMisc(router: Router): void { router.get("/v1/custom_emojis", @@ -48,6 +49,7 @@ export function setupEndpointsMisc(router: Router): void { ); router.get("/v1/trends/statuses", + filterContext('public'), async (ctx) => { const args = limitToInt(ctx.query); ctx.body = await MiscHelpers.getTrendingStatuses(args.limit, args.offset, ctx); diff --git a/packages/backend/src/server/api/mastodon/endpoints/notifications.ts b/packages/backend/src/server/api/mastodon/endpoints/notifications.ts index 21aa3b95f..49e791465 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/notifications.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/notifications.ts @@ -3,10 +3,12 @@ import { limitToInt, normalizeUrlQuery } from "./timeline.js"; import { NotificationHelpers } from "@/server/api/mastodon/helpers/notification.js"; import { NotificationConverter } from "@/server/api/mastodon/converters/notification.js"; import { auth } from "@/server/api/mastodon/middleware/auth.js"; +import { filterContext } from "@/server/api/mastodon/middleware/filter-context.js"; export function setupEndpointsNotifications(router: Router): void { router.get("/v1/notifications", auth(true, ['read:notifications']), + filterContext('notifications'), async (ctx) => { const args = normalizeUrlQuery(limitToInt(ctx.query), ['types[]', 'exclude_types[]']); const res = await NotificationHelpers.getNotifications(args.max_id, args.since_id, args.min_id, args.limit, args['types[]'], args['exclude_types[]'], args.account_id, ctx); @@ -16,6 +18,7 @@ export function setupEndpointsNotifications(router: Router): void { router.get("/v1/notifications/:id", auth(true, ['read:notifications']), + filterContext('notifications'), async (ctx) => { const notification = await NotificationHelpers.getNotificationOr404(ctx.params.id, ctx); ctx.body = await NotificationConverter.encode(notification, ctx); diff --git a/packages/backend/src/server/api/mastodon/endpoints/status.ts b/packages/backend/src/server/api/mastodon/endpoints/status.ts index e508f8f7f..8379b0817 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/status.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/status.ts @@ -7,6 +7,7 @@ import { PollHelpers } from "@/server/api/mastodon/helpers/poll.js"; import { toArray } from "@/prelude/array.js"; import { auth } from "@/server/api/mastodon/middleware/auth.js"; import { MastoApiError } from "@/server/api/mastodon/middleware/catch-errors.js"; +import { filterContext } from "@/server/api/mastodon/middleware/filter-context.js"; export function setupEndpointsStatus(router: Router): void { router.post("/v1/statuses", @@ -40,6 +41,7 @@ export function setupEndpointsStatus(router: Router): void { ); router.get<{ Params: { id: string } }>("/v1/statuses/:id", auth(false, ["read:statuses"]), + filterContext('thread'), async (ctx) => { const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx); @@ -57,6 +59,7 @@ export function setupEndpointsStatus(router: Router): void { router.get<{ Params: { id: string } }>( "/v1/statuses/:id/context", auth(false, ["read:statuses"]), + filterContext('thread'), async (ctx) => { //FIXME: determine final limits within helper functions instead of here const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx); diff --git a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts b/packages/backend/src/server/api/mastodon/endpoints/timeline.ts index d0915858a..9849cf8c0 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/timeline.ts @@ -5,6 +5,7 @@ import { NoteConverter } from "@/server/api/mastodon/converters/note.js"; import { UserLists } from "@/models/index.js"; import { auth } from "@/server/api/mastodon/middleware/auth.js"; import { MastoApiError } from "@/server/api/mastodon/middleware/catch-errors.js"; +import { filterContext } from "@/server/api/mastodon/middleware/filter-context.js"; //TODO: Move helper functions to a helper class export function limitToInt(q: ParsedUrlQuery, additional: string[] = []) { @@ -53,6 +54,7 @@ export function normalizeUrlQuery(q: ParsedUrlQuery, arrayKeys: string[] = []): export function setupEndpointsTimeline(router: Router): void { router.get("/v1/timelines/public", auth(true, ['read:statuses']), + filterContext('public'), async (ctx, reply) => { const args = normalizeUrlQuery(argsToBools(limitToInt(ctx.query))); const res = await TimelineHelpers.getPublicTimeline(args.max_id, args.since_id, args.min_id, args.limit, args.only_media, args.local, args.remote, ctx); @@ -61,6 +63,7 @@ export function setupEndpointsTimeline(router: Router): void { router.get<{ Params: { hashtag: string } }>( "/v1/timelines/tag/:hashtag", auth(false, ['read:statuses']), + filterContext('public'), async (ctx, reply) => { const tag = (ctx.params.hashtag ?? '').trim().toLowerCase(); const args = normalizeUrlQuery(argsToBools(limitToInt(ctx.query)), ['any[]', 'all[]', 'none[]']); @@ -70,6 +73,7 @@ export function setupEndpointsTimeline(router: Router): void { ); router.get("/v1/timelines/home", auth(true, ['read:statuses']), + filterContext('home'), async (ctx, reply) => { const args = normalizeUrlQuery(limitToInt(ctx.query)); const res = await TimelineHelpers.getHomeTimeline(args.max_id, args.since_id, args.min_id, args.limit, ctx); @@ -78,6 +82,7 @@ export function setupEndpointsTimeline(router: Router): void { router.get<{ Params: { listId: string } }>( "/v1/timelines/list/:listId", auth(true, ['read:lists']), + filterContext('home'), async (ctx, reply) => { const list = await UserLists.findOneBy({ userId: ctx.user.id, id: ctx.params.listId }); if (!list) throw new MastoApiError(404); diff --git a/packages/backend/src/server/api/mastodon/entities/filter.ts b/packages/backend/src/server/api/mastodon/entities/filter.ts index d2f7d573a..3d76c2c3f 100644 --- a/packages/backend/src/server/api/mastodon/entities/filter.ts +++ b/packages/backend/src/server/api/mastodon/entities/filter.ts @@ -1,12 +1,13 @@ namespace MastodonEntity { export type Filter = { id: string; - phrase: string; + title: string; context: Array; expires_at: string | null; - irreversible: boolean; - whole_word: boolean; + filter_action: 'warn' | 'hide'; + keywords: FilterKeyword[]; + statuses: FilterStatus[]; }; - export type FilterContext = string; + export type FilterContext = 'home' | 'notifications' | 'public' | 'thread' | 'account'; } diff --git a/packages/backend/src/server/api/mastodon/entities/filter_keyword.ts b/packages/backend/src/server/api/mastodon/entities/filter_keyword.ts new file mode 100644 index 000000000..558026e54 --- /dev/null +++ b/packages/backend/src/server/api/mastodon/entities/filter_keyword.ts @@ -0,0 +1,7 @@ +namespace MastodonEntity { + export type FilterKeyword = { + id: string; + keyword: string; + whole_word: boolean; + }; +} diff --git a/packages/backend/src/server/api/mastodon/entities/filter_result.ts b/packages/backend/src/server/api/mastodon/entities/filter_result.ts new file mode 100644 index 000000000..c3f39c4b3 --- /dev/null +++ b/packages/backend/src/server/api/mastodon/entities/filter_result.ts @@ -0,0 +1,7 @@ +namespace MastodonEntity { + export type FilterResult = { + filter: Filter; + keyword_matches?: string[]; + status_matches?: string[]; + }; +} diff --git a/packages/backend/src/server/api/mastodon/entities/filter_status.ts b/packages/backend/src/server/api/mastodon/entities/filter_status.ts new file mode 100644 index 000000000..bb585ccd4 --- /dev/null +++ b/packages/backend/src/server/api/mastodon/entities/filter_status.ts @@ -0,0 +1,6 @@ +namespace MastodonEntity { + export type FilterStatus = { + id: string; + status_id: string; + }; +} diff --git a/packages/backend/src/server/api/mastodon/entities/status.ts b/packages/backend/src/server/api/mastodon/entities/status.ts index 2df5a6f88..a6c2a1b74 100644 --- a/packages/backend/src/server/api/mastodon/entities/status.ts +++ b/packages/backend/src/server/api/mastodon/entities/status.ts @@ -43,6 +43,7 @@ namespace MastodonEntity { quote: Status | null; bookmarked: boolean; edited_at: string | null; + filtered: Array | null; }; export type StatusCreationRequest = { diff --git a/packages/backend/src/server/api/mastodon/index.ts b/packages/backend/src/server/api/mastodon/index.ts index 46993038e..365e6abad 100644 --- a/packages/backend/src/server/api/mastodon/index.ts +++ b/packages/backend/src/server/api/mastodon/index.ts @@ -50,9 +50,10 @@ function setupMiddleware(router: Router): void { router.use(CacheMiddleware); } -export function getStubMastoContext(user: ILocalUser | null | undefined): any { +export function getStubMastoContext(user: ILocalUser | null | undefined, filterContext?: string): any { return { user: user ?? null, - cache: UserHelpers.getFreshAccountCache() + cache: UserHelpers.getFreshAccountCache(), + filterContext: filterContext, }; -} \ No newline at end of file +} diff --git a/packages/backend/src/server/api/mastodon/middleware/filter-context.ts b/packages/backend/src/server/api/mastodon/middleware/filter-context.ts new file mode 100644 index 000000000..b8719aa9e --- /dev/null +++ b/packages/backend/src/server/api/mastodon/middleware/filter-context.ts @@ -0,0 +1,8 @@ +import { MastoContext } from "@/server/api/mastodon/index.js"; + +export function filterContext(context: string) { + return async function filterContext(ctx: MastoContext, next: () => Promise) { + ctx.filterContext = context; + await next(); + }; +} diff --git a/packages/backend/src/server/api/mastodon/streaming/channels/list.ts b/packages/backend/src/server/api/mastodon/streaming/channels/list.ts index e550193a1..20fe7061a 100644 --- a/packages/backend/src/server/api/mastodon/streaming/channels/list.ts +++ b/packages/backend/src/server/api/mastodon/streaming/channels/list.ts @@ -5,7 +5,6 @@ import { StreamMessages } from "@/server/api/stream/types.js"; import { Packed } from "@/misc/schema.js"; import { User } from "@/models/entities/user.js"; import { UserListJoinings } from "@/models/index.js"; -import { isFiltered } from "@/misc/is-filtered.js"; export class MastodonStreamList extends MastodonStream { public static shouldShare = false; @@ -50,7 +49,7 @@ export class MastodonStreamList extends MastodonStream { private async onNote(note: Note) { if (!await this.shouldProcessNote(note)) return; - const encoded = await NoteConverter.encodeEvent(note, this.user) + const encoded = await NoteConverter.encodeEvent(note, this.user, 'home') this.connection.send(this.chName, "update", encoded); } @@ -60,7 +59,7 @@ export class MastodonStreamList extends MastodonStream { switch (data.type) { case "updated": - const encoded = await NoteConverter.encodeEvent(note, this.user); + const encoded = await NoteConverter.encodeEvent(note, this.user, 'home'); this.connection.send(this.chName, "status.update", encoded); break; case "deleted": @@ -75,7 +74,6 @@ export class MastodonStreamList extends MastodonStream { if (!this.listUsers.includes(note.userId)) return false; if (note.channelId) return false; if (note.renoteId !== null && !note.text && this.renoteMuting.has(note.userId)) return false; - if (this.userProfile && (await isFiltered(note as Note, this.user, this.userProfile))) return false; if (note.visibility === "specified") return !!note.visibleUserIds?.includes(this.user.id); if (note.visibility === "followers") return this.following.has(note.userId); return true; diff --git a/packages/backend/src/server/api/mastodon/streaming/channels/public.ts b/packages/backend/src/server/api/mastodon/streaming/channels/public.ts index 58bcc0c6e..2ee82ca19 100644 --- a/packages/backend/src/server/api/mastodon/streaming/channels/public.ts +++ b/packages/backend/src/server/api/mastodon/streaming/channels/public.ts @@ -6,7 +6,6 @@ import { NoteConverter } from "@/server/api/mastodon/converters/note.js"; import { StreamMessages } from "@/server/api/stream/types.js"; import { fetchMeta } from "@/misc/fetch-meta.js"; import isQuote from "@/misc/is-quote.js"; -import { isFiltered } from "@/misc/is-filtered.js"; export class MastodonStreamPublic extends MastodonStream { public static shouldShare = true; @@ -40,7 +39,7 @@ export class MastodonStreamPublic extends MastodonStream { private async onNote(note: Note) { if (!await this.shouldProcessNote(note)) return; - const encoded = await NoteConverter.encodeEvent(note, this.user) + const encoded = await NoteConverter.encodeEvent(note, this.user, 'public') this.connection.send(this.chName, "update", encoded); } @@ -50,7 +49,7 @@ export class MastodonStreamPublic extends MastodonStream { switch (data.type) { case "updated": - const encoded = await NoteConverter.encodeEvent(note, this.user); + const encoded = await NoteConverter.encodeEvent(note, this.user, 'public'); this.connection.send(this.chName, "status.update", encoded); break; case "deleted": @@ -72,7 +71,6 @@ export class MastodonStreamPublic extends MastodonStream { if (isUserRelated(note, this.muting)) return false; if (isUserRelated(note, this.blocking)) return false; if (note.renoteId !== null && !isQuote(note) && this.renoteMuting.has(note.userId)) return false; - if (this.userProfile && (await isFiltered(note as Note, this.user, this.userProfile))) return false; return true; } diff --git a/packages/backend/src/server/api/mastodon/streaming/channels/tag.ts b/packages/backend/src/server/api/mastodon/streaming/channels/tag.ts index 29462c755..7780ea5c7 100644 --- a/packages/backend/src/server/api/mastodon/streaming/channels/tag.ts +++ b/packages/backend/src/server/api/mastodon/streaming/channels/tag.ts @@ -5,7 +5,6 @@ import { Note } from "@/models/entities/note.js"; import { NoteConverter } from "@/server/api/mastodon/converters/note.js"; import { StreamMessages } from "@/server/api/stream/types.js"; import isQuote from "@/misc/is-quote.js"; -import { isFiltered } from "@/misc/is-filtered.js"; export class MastodonStreamTag extends MastodonStream { public static shouldShare = false; @@ -34,7 +33,7 @@ export class MastodonStreamTag extends MastodonStream { private async onNote(note: Note) { if (!await this.shouldProcessNote(note)) return; - const encoded = await NoteConverter.encodeEvent(note, this.user) + const encoded = await NoteConverter.encodeEvent(note, this.user, 'public') this.connection.send(this.chName, "update", encoded); } @@ -44,7 +43,7 @@ export class MastodonStreamTag extends MastodonStream { switch (data.type) { case "updated": - const encoded = await NoteConverter.encodeEvent(note, this.user); + const encoded = await NoteConverter.encodeEvent(note, this.user, 'public'); this.connection.send(this.chName, "status.update", encoded); break; case "deleted": @@ -64,7 +63,6 @@ export class MastodonStreamTag extends MastodonStream { if (isUserRelated(note, this.muting)) return false; if (isUserRelated(note, this.blocking)) return false; if (note.renoteId !== null && !isQuote(note) && this.renoteMuting.has(note.userId)) return false; - if (this.userProfile && (await isFiltered(note as Note, this.user, this.userProfile))) return false; return true; } diff --git a/packages/backend/src/server/api/mastodon/streaming/channels/user.ts b/packages/backend/src/server/api/mastodon/streaming/channels/user.ts index acc4be83e..ea632d73f 100644 --- a/packages/backend/src/server/api/mastodon/streaming/channels/user.ts +++ b/packages/backend/src/server/api/mastodon/streaming/channels/user.ts @@ -7,7 +7,6 @@ import { StreamMessages } from "@/server/api/stream/types.js"; import { NotificationConverter } from "@/server/api/mastodon/converters/notification.js"; import { AnnouncementConverter } from "@/server/api/mastodon/converters/announcement.js"; import isQuote from "@/misc/is-quote.js"; -import { isFiltered } from "@/misc/is-filtered.js"; export class MastodonStreamUser extends MastodonStream { public static shouldShare = true; @@ -40,7 +39,7 @@ export class MastodonStreamUser extends MastodonStream { private async onNote(note: Note) { if (!await this.shouldProcessNote(note)) return; - const encoded = await NoteConverter.encodeEvent(note, this.user) + const encoded = await NoteConverter.encodeEvent(note, this.user, 'home') this.connection.send(this.chName, "update", encoded); } @@ -50,7 +49,7 @@ export class MastodonStreamUser extends MastodonStream { switch (data.type) { case "updated": - const encoded = await NoteConverter.encodeEvent(note, this.user); + const encoded = await NoteConverter.encodeEvent(note, this.user, 'home'); this.connection.send(this.chName, "status.update", encoded); break; case "deleted": @@ -64,7 +63,7 @@ export class MastodonStreamUser extends MastodonStream { private async onUserEvent(data: StreamMessages["main"]["payload"]) { switch (data.type) { case "notification": - const encoded = await NotificationConverter.encodeEvent(data.body.id, this.user); + const encoded = await NotificationConverter.encodeEvent(data.body.id, this.user, 'notifications'); if (encoded) this.connection.send(this.chName, "notification", encoded); break; default: @@ -99,7 +98,6 @@ export class MastodonStreamUser extends MastodonStream { if (isUserRelated(note, this.blocking)) return false; if (isUserRelated(note, this.hidden)) return false; if (note.renoteId !== null && !isQuote(note) && this.renoteMuting.has(note.userId)) return false; - if (this.userProfile && (await isFiltered(note as Note, this.user, this.userProfile))) return false; return true; }