diff --git a/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts b/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts index 703808d279..8adf16294c 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts @@ -6,7 +6,7 @@ import { Brackets, In } from 'typeorm'; import * as Redis from 'ioredis'; import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/_.js'; +import type { NotesRepository, UsersRepository } from '@/models/_.js'; import { obsoleteNotificationTypes, notificationTypes, FilterUnionByProperty } from '@/types.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteReadService } from '@/core/NoteReadService.js'; @@ -15,6 +15,7 @@ import { NotificationService } from '@/core/NotificationService.js'; import { DI } from '@/di-symbols.js'; import { IdService } from '@/core/IdService.js'; import { MiGroupedNotification, MiNotification } from '@/models/Notification.js'; +import { CacheService } from '@/core/CacheService.js'; export const meta = { tags: ['account', 'notifications'], @@ -66,10 +67,14 @@ export default class extends Endpoint { // eslint- @Inject(DI.notesRepository) private notesRepository: NotesRepository, + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + private idService: IdService, private notificationEntityService: NotificationEntityService, private notificationService: NotificationService, private noteReadService: NoteReadService, + private cacheService: CacheService, ) { super(meta, paramDef, async (ps, me) => { const EXTRA_LIMIT = 100; @@ -105,6 +110,29 @@ export default class extends Endpoint { // eslint- notifications = notifications.filter(notification => !excludeTypes.includes(notification.type)); } + //#region Check muting + + const [ + userIdsWhoMeMuting, + userMutedInstances, + ] = await Promise.all([ + this.cacheService.userMutingsCache.fetch(me.id), + this.cacheService.userProfileCache.fetch(me.id).then(p => new Set(p.mutedInstances)), + ]); + + notifications = (await Promise.all(notifications.map(async (notification): Promise => { + if (!('notifierId' in notification)) return null; + if (userIdsWhoMeMuting.has(notification.notifierId)) return null; + + const notifier = await this.usersRepository.findOneBy({ id: notification.notifierId }); + if (notifier === null) return null; + if (notifier.host && userMutedInstances.has(notifier.host)) return null; + + return notification; + }))).filter((notification): notification is MiNotification => notification !== null); + + //#endregion Check muting + if (notifications.length === 0) { return []; } diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index 52b6749e3f..0ae8b89d2f 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -6,7 +6,7 @@ import { Brackets, In } from 'typeorm'; import * as Redis from 'ioredis'; import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/_.js'; +import type { NotesRepository, UsersRepository } from '@/models/_.js'; import { obsoleteNotificationTypes, notificationTypes, FilterUnionByProperty } from '@/types.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteReadService } from '@/core/NoteReadService.js'; @@ -15,6 +15,7 @@ import { NotificationService } from '@/core/NotificationService.js'; import { DI } from '@/di-symbols.js'; import { IdService } from '@/core/IdService.js'; import { MiNotification } from '@/models/Notification.js'; +import { CacheService } from '@/core/CacheService.js'; export const meta = { tags: ['account', 'notifications'], @@ -66,10 +67,14 @@ export default class extends Endpoint { // eslint- @Inject(DI.notesRepository) private notesRepository: NotesRepository, + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + private idService: IdService, private notificationEntityService: NotificationEntityService, private notificationService: NotificationService, private noteReadService: NoteReadService, + private cacheService: CacheService, ) { super(meta, paramDef, async (ps, me) => { // includeTypes が空の場合はクエリしない @@ -103,6 +108,29 @@ export default class extends Endpoint { // eslint- notifications = notifications.filter(notification => !excludeTypes.includes(notification.type)); } + //#region Check muting + + const [ + userIdsWhoMeMuting, + userMutedInstances, + ] = await Promise.all([ + this.cacheService.userMutingsCache.fetch(me.id), + this.cacheService.userProfileCache.fetch(me.id).then(p => new Set(p.mutedInstances)), + ]); + + notifications = (await Promise.all(notifications.map(async (notification): Promise => { + if (!('notifierId' in notification)) return null; + if (userIdsWhoMeMuting.has(notification.notifierId)) return null; + + const notifier = await this.usersRepository.findOneBy({ id: notification.notifierId }); + if (notifier === null) return null; + if (notifier.host && userMutedInstances.has(notifier.host)) return null; + + return notification; + }))).filter((notification): notification is MiNotification => notification !== null); + + //#endregion Check muting + if (notifications.length === 0) { return []; }