6379deb5a7
as @fEmber noticed, if `findOneByOrFail` throws, we would have marked the report as resolved, without logging that this way, the whole operation fails (the frontend should not send us ids of users that can't be resolved!)
140 lines
4.9 KiB
TypeScript
140 lines
4.9 KiB
TypeScript
/*
|
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
import { Inject, Injectable } from '@nestjs/common';
|
|
import { In } from 'typeorm';
|
|
import { DI } from '@/di-symbols.js';
|
|
import { bindThis } from '@/decorators.js';
|
|
import type { AbuseUserReportsRepository, MiAbuseUserReport, MiUser, UsersRepository } from '@/models/_.js';
|
|
import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
|
|
import { QueueService } from '@/core/QueueService.js';
|
|
import { InstanceActorService } from '@/core/InstanceActorService.js';
|
|
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
|
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
|
import { IdService } from './IdService.js';
|
|
|
|
@Injectable()
|
|
export class AbuseReportService {
|
|
constructor(
|
|
@Inject(DI.abuseUserReportsRepository)
|
|
private abuseUserReportsRepository: AbuseUserReportsRepository,
|
|
@Inject(DI.usersRepository)
|
|
private usersRepository: UsersRepository,
|
|
private idService: IdService,
|
|
private abuseReportNotificationService: AbuseReportNotificationService,
|
|
private queueService: QueueService,
|
|
private instanceActorService: InstanceActorService,
|
|
private apRendererService: ApRendererService,
|
|
private moderationLogService: ModerationLogService,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* ユーザからの通報をDBに記録し、その内容を下記の手段で管理者各位に通知する.
|
|
* - 管理者用Redisイベント
|
|
* - EMail(モデレータ権限所有者ユーザ+metaテーブルに設定されているメールアドレス)
|
|
* - SystemWebhook
|
|
*
|
|
* @param params 通報内容. もし複数件の通報に対応した時のために、あらかじめ複数件を処理できる前提で考える
|
|
* @see AbuseReportNotificationService.notify
|
|
*/
|
|
@bindThis
|
|
public async report(params: {
|
|
targetUserId: MiAbuseUserReport['targetUserId'],
|
|
targetUserHost: MiAbuseUserReport['targetUserHost'],
|
|
reporterId: MiAbuseUserReport['reporterId'],
|
|
reporterHost: MiAbuseUserReport['reporterHost'],
|
|
comment: string,
|
|
}[]) {
|
|
const entities = params.map(param => {
|
|
return {
|
|
id: this.idService.gen(),
|
|
targetUserId: param.targetUserId,
|
|
targetUserHost: param.targetUserHost,
|
|
reporterId: param.reporterId,
|
|
reporterHost: param.reporterHost,
|
|
comment: param.comment,
|
|
};
|
|
});
|
|
|
|
const reports = Array.of<MiAbuseUserReport>();
|
|
for (const entity of entities) {
|
|
const report = await this.abuseUserReportsRepository.insertOne(entity);
|
|
reports.push(report);
|
|
}
|
|
|
|
return Promise.all([
|
|
this.abuseReportNotificationService.notifyAdminStream(reports),
|
|
this.abuseReportNotificationService.notifySystemWebhook(reports, 'abuseReport'),
|
|
this.abuseReportNotificationService.notifyMail(reports),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 通報を解決し、その内容を下記の手段で管理者各位に通知する.
|
|
* - SystemWebhook
|
|
*
|
|
* @param params 通報内容. もし複数件の通報に対応した時のために、あらかじめ複数件を処理できる前提で考える
|
|
* @param operator 通報を処理したユーザ
|
|
* @see AbuseReportNotificationService.notify
|
|
*/
|
|
@bindThis
|
|
public async resolve(
|
|
params: {
|
|
reportId: string;
|
|
forward: boolean;
|
|
}[],
|
|
operator: MiUser,
|
|
) {
|
|
const paramsMap = new Map(params.map(it => [it.reportId, it]));
|
|
const reports = await this.abuseUserReportsRepository.findBy({
|
|
id: In(params.map(it => it.reportId)),
|
|
});
|
|
|
|
const targetUserMap = new Map();
|
|
for (const report of reports) {
|
|
const shouldForward = paramsMap.get(report.id)!.forward;
|
|
|
|
if (shouldForward && report.targetUserHost != null) {
|
|
return targetUserMap.set(report.id, await this.usersRepository.findOneByOrFail({ id: report.targetUserId }));
|
|
} else {
|
|
return targetUserMap.set(report.id, null);
|
|
}
|
|
}
|
|
|
|
for (const report of reports) {
|
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
const ps = paramsMap.get(report.id)!;
|
|
|
|
await this.abuseUserReportsRepository.update(report.id, {
|
|
resolved: true,
|
|
assigneeId: operator.id,
|
|
forwarded: ps.forward && report.targetUserHost !== null,
|
|
});
|
|
|
|
const targetUser = targetUserMap.get(report.id)!;
|
|
if (targetUser != null) {
|
|
const actor = await this.instanceActorService.getInstanceActor();
|
|
|
|
// eslint-disable-next-line
|
|
const flag = this.apRendererService.renderFlag(actor, targetUser.uri!, report.comment);
|
|
const contextAssignedFlag = this.apRendererService.addContext(flag);
|
|
this.queueService.deliver(actor, contextAssignedFlag, targetUser.inbox, false);
|
|
}
|
|
|
|
this.moderationLogService
|
|
.log(operator, 'resolveAbuseReport', {
|
|
reportId: report.id,
|
|
report: report,
|
|
forwarded: ps.forward && report.targetUserHost !== null,
|
|
})
|
|
.then();
|
|
}
|
|
|
|
return this.abuseUserReportsRepository.findBy({ id: In(reports.map(it => it.id)) })
|
|
.then(reports => this.abuseReportNotificationService.notifySystemWebhook(reports, 'abuseReportResolved'));
|
|
}
|
|
}
|