/* * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import type { MutingsRepository, MiMuting } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import type { MiUser } from '@/models/User.js'; import type { MiMeta } from '@/models/Meta.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import { CacheService } from '@/core/CacheService.js'; @Injectable() export class UserMutingService { constructor( @Inject(DI.meta) private serverSettings: MiMeta, @Inject(DI.mutingsRepository) private mutingsRepository: MutingsRepository, private roleService: RoleService, private idService: IdService, private cacheService: CacheService, ) { } @bindThis public async mute(user: MiUser, target: MiUser, expiresAt: Date | null = null): Promise { // フォロー解除できない(=ミュートもできない)ユーザーの場合 if ( this.serverSettings.forciblyFollowedUsers.includes(target.id) && !await this.roleService.isModerator(user) ) { throw new IdentifiableError('15273a89-374d-49fa-8df6-8bb3feeea455', 'You cannot mute that user due to server policy.'); } await this.mutingsRepository.insert({ id: this.idService.gen(), expiresAt: expiresAt ?? null, muterId: user.id, muteeId: target.id, }); this.cacheService.userMutingsCache.refresh(user.id); } @bindThis public async unmute(mutings: MiMuting[]): Promise { if (mutings.length === 0) return; await this.mutingsRepository.delete({ id: In(mutings.map(m => m.id)), }); const muterIds = [...new Set(mutings.map(m => m.muterId))]; for (const muterId of muterIds) { this.cacheService.userMutingsCache.refresh(muterId); } } }