Skip to content

Commit d8cb730

Browse files
authored
feat: 通報の強化 (#14704)
* wip * Update CHANGELOG.md * lint * Update types.ts * wip * ✌️ * Update MkAbuseReport.vue * tweak
1 parent 043fef9 commit d8cb730

29 files changed

+574
-62
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
### General
1111
- Feat: サーバー初期設定時に初期パスワードを設定できるように
12+
- Feat: 通報にモデレーションノートを残せるように
13+
- Feat: 通報の解決種別を設定できるように
14+
- Enhance: 通報の解決と転送を個別に行えるように
1215
- Enhance: セキュリティ向上のため、サインイン時もCAPTCHAを求めるようになりました
1316
- Enhance: 依存関係の更新
1417
- Enhance: l10nの更新

locales/index.d.ts

+43-12
Original file line numberDiff line numberDiff line change
@@ -1834,6 +1834,10 @@ export interface Locale extends ILocale {
18341834
* モデレーションノート
18351835
*/
18361836
"moderationNote": string;
1837+
/**
1838+
* モデレーター間でだけ共有されるメモを記入することができます。
1839+
*/
1840+
"moderationNoteDescription": string;
18371841
/**
18381842
* モデレーションノートを追加する
18391843
*/
@@ -2894,22 +2898,10 @@ export interface Locale extends ILocale {
28942898
* 通報元
28952899
*/
28962900
"reporterOrigin": string;
2897-
/**
2898-
* リモートサーバーに通報を転送する
2899-
*/
2900-
"forwardReport": string;
2901-
/**
2902-
* リモートサーバーからはあなたの情報は見れず、匿名のシステムアカウントとして表示されます。
2903-
*/
2904-
"forwardReportIsAnonymous": string;
29052901
/**
29062902
* 送信
29072903
*/
29082904
"send": string;
2909-
/**
2910-
* 対応済みにする
2911-
*/
2912-
"abuseMarkAsResolved": string;
29132905
/**
29142906
* 新しいタブで開く
29152907
*/
@@ -5170,6 +5162,37 @@ export interface Locale extends ILocale {
51705162
* フォロワーへのメッセージ
51715163
*/
51725164
"messageToFollower": string;
5165+
/**
5166+
* 対象
5167+
*/
5168+
"target": string;
5169+
"_abuseUserReport": {
5170+
/**
5171+
* 転送
5172+
*/
5173+
"forward": string;
5174+
/**
5175+
* 匿名のシステムアカウントとして、リモートサーバーに通報を転送します。
5176+
*/
5177+
"forwardDescription": string;
5178+
/**
5179+
* 解決
5180+
*/
5181+
"resolve": string;
5182+
/**
5183+
* 是認
5184+
*/
5185+
"accept": string;
5186+
/**
5187+
* 否認
5188+
*/
5189+
"reject": string;
5190+
/**
5191+
* 内容が正当である通報に対応した場合は「是認」を選択し、肯定的にケースが解決されたことをマークします。
5192+
* 内容が正当でない通報の場合は「否認」を選択し、否定的にケースが解決されたことをマークします。
5193+
*/
5194+
"resolveTutorial": string;
5195+
};
51735196
"_delivery": {
51745197
/**
51755198
* 配信状態
@@ -9785,6 +9808,14 @@ export interface Locale extends ILocale {
97859808
* 通報を解決
97869809
*/
97879810
"resolveAbuseReport": string;
9811+
/**
9812+
* 通報を転送
9813+
*/
9814+
"forwardAbuseReport": string;
9815+
/**
9816+
* 通報のモデレーションノート更新
9817+
*/
9818+
"updateAbuseReportNote": string;
97889819
/**
97899820
* 招待コードを作成
97909821
*/

locales/ja-JP.yml

+12-3
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,7 @@ totpDescription: "認証アプリを使ってワンタイムパスワードを
454454
moderator: "モデレーター"
455455
moderation: "モデレーション"
456456
moderationNote: "モデレーションノート"
457+
moderationNoteDescription: "モデレーター間でだけ共有されるメモを記入することができます。"
457458
addModerationNote: "モデレーションノートを追加する"
458459
moderationLogs: "モデログ"
459460
nUsersMentioned: "{n}人が投稿"
@@ -719,10 +720,7 @@ abuseReported: "内容が送信されました。ご報告ありがとうござ
719720
reporter: "通報者"
720721
reporteeOrigin: "通報先"
721722
reporterOrigin: "通報元"
722-
forwardReport: "リモートサーバーに通報を転送する"
723-
forwardReportIsAnonymous: "リモートサーバーからはあなたの情報は見れず、匿名のシステムアカウントとして表示されます。"
724723
send: "送信"
725-
abuseMarkAsResolved: "対応済みにする"
726724
openInNewTab: "新しいタブで開く"
727725
openInSideView: "サイドビューで開く"
728726
defaultNavigationBehaviour: "デフォルトのナビゲーション"
@@ -1288,6 +1286,15 @@ unknownWebAuthnKey: "登録されていないパスキーです。"
12881286
passkeyVerificationFailed: "パスキーの検証に失敗しました。"
12891287
passkeyVerificationSucceededButPasswordlessLoginDisabled: "パスキーの検証に成功しましたが、パスワードレスログインが無効になっています。"
12901288
messageToFollower: "フォロワーへのメッセージ"
1289+
target: "対象"
1290+
1291+
_abuseUserReport:
1292+
forward: "転送"
1293+
forwardDescription: "匿名のシステムアカウントとして、リモートサーバーに通報を転送します。"
1294+
resolve: "解決"
1295+
accept: "是認"
1296+
reject: "否認"
1297+
resolveTutorial: "内容が正当である通報に対応した場合は「是認」を選択し、肯定的にケースが解決されたことをマークします。\n内容が正当でない通報の場合は「否認」を選択し、否定的にケースが解決されたことをマークします。"
12911298

12921299
_delivery:
12931300
status: "配信状態"
@@ -2593,6 +2600,8 @@ _moderationLogTypes:
25932600
markSensitiveDriveFile: "ファイルをセンシティブ付与"
25942601
unmarkSensitiveDriveFile: "ファイルをセンシティブ解除"
25952602
resolveAbuseReport: "通報を解決"
2603+
forwardAbuseReport: "通報を転送"
2604+
updateAbuseReportNote: "通報のモデレーションノート更新"
25962605
createInvitation: "招待コードを作成"
25972606
createAd: "広告を作成"
25982607
deleteAd: "広告を削除"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* SPDX-FileCopyrightText: syuilo and misskey-project
3+
* SPDX-License-Identifier: AGPL-3.0-only
4+
*/
5+
6+
export class RefineAbuseUserReport1728085812127 {
7+
name = 'RefineAbuseUserReport1728085812127'
8+
9+
async up(queryRunner) {
10+
await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "moderationNote" character varying(8192) NOT NULL DEFAULT ''`);
11+
await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "resolvedAs" character varying(128)`);
12+
}
13+
14+
async down(queryRunner) {
15+
await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "resolvedAs"`);
16+
await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "moderationNote"`);
17+
}
18+
}

packages/backend/src/core/AbuseReportService.ts

+63-17
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ export class AbuseReportService {
2020
constructor(
2121
@Inject(DI.abuseUserReportsRepository)
2222
private abuseUserReportsRepository: AbuseUserReportsRepository,
23+
2324
@Inject(DI.usersRepository)
2425
private usersRepository: UsersRepository,
26+
2527
private idService: IdService,
2628
private abuseReportNotificationService: AbuseReportNotificationService,
2729
private queueService: QueueService,
@@ -77,16 +79,16 @@ export class AbuseReportService {
7779
* - SystemWebhook
7880
*
7981
* @param params 通報内容. もし複数件の通報に対応した時のために、あらかじめ複数件を処理できる前提で考える
80-
* @param operator 通報を処理したユーザ
82+
* @param moderator 通報を処理したユーザ
8183
* @see AbuseReportNotificationService.notify
8284
*/
8385
@bindThis
8486
public async resolve(
8587
params: {
8688
reportId: string;
87-
forward: boolean;
89+
resolvedAs: MiAbuseUserReport['resolvedAs'];
8890
}[],
89-
operator: MiUser,
91+
moderator: MiUser,
9092
) {
9193
const paramsMap = new Map(params.map(it => [it.reportId, it]));
9294
const reports = await this.abuseUserReportsRepository.findBy({
@@ -99,30 +101,74 @@ export class AbuseReportService {
99101

100102
await this.abuseUserReportsRepository.update(report.id, {
101103
resolved: true,
102-
assigneeId: operator.id,
103-
forwarded: ps.forward && report.targetUserHost !== null,
104+
assigneeId: moderator.id,
105+
resolvedAs: ps.resolvedAs,
104106
});
105107

106-
if (ps.forward && report.targetUserHost != null) {
107-
const actor = await this.instanceActorService.getInstanceActor();
108-
const targetUser = await this.usersRepository.findOneByOrFail({ id: report.targetUserId });
109-
110-
// eslint-disable-next-line
111-
const flag = this.apRendererService.renderFlag(actor, targetUser.uri!, report.comment);
112-
const contextAssignedFlag = this.apRendererService.addContext(flag);
113-
this.queueService.deliver(actor, contextAssignedFlag, targetUser.inbox, false);
114-
}
115-
116108
this.moderationLogService
117-
.log(operator, 'resolveAbuseReport', {
109+
.log(moderator, 'resolveAbuseReport', {
118110
reportId: report.id,
119111
report: report,
120-
forwarded: ps.forward && report.targetUserHost !== null,
112+
resolvedAs: ps.resolvedAs,
121113
})
122114
.then();
123115
}
124116

125117
return this.abuseUserReportsRepository.findBy({ id: In(reports.map(it => it.id)) })
126118
.then(reports => this.abuseReportNotificationService.notifySystemWebhook(reports, 'abuseReportResolved'));
127119
}
120+
121+
@bindThis
122+
public async forward(
123+
reportId: MiAbuseUserReport['id'],
124+
moderator: MiUser,
125+
) {
126+
const report = await this.abuseUserReportsRepository.findOneByOrFail({ id: reportId });
127+
128+
if (report.targetUserHost == null) {
129+
throw new Error('The target user host is null.');
130+
}
131+
132+
await this.abuseUserReportsRepository.update(report.id, {
133+
forwarded: true,
134+
});
135+
136+
const actor = await this.instanceActorService.getInstanceActor();
137+
const targetUser = await this.usersRepository.findOneByOrFail({ id: report.targetUserId });
138+
139+
const flag = this.apRendererService.renderFlag(actor, targetUser.uri!, report.comment);
140+
const contextAssignedFlag = this.apRendererService.addContext(flag);
141+
this.queueService.deliver(actor, contextAssignedFlag, targetUser.inbox, false);
142+
143+
this.moderationLogService
144+
.log(moderator, 'forwardAbuseReport', {
145+
reportId: report.id,
146+
report: report,
147+
})
148+
.then();
149+
}
150+
151+
@bindThis
152+
public async update(
153+
reportId: MiAbuseUserReport['id'],
154+
params: {
155+
moderationNote?: MiAbuseUserReport['moderationNote'];
156+
},
157+
moderator: MiUser,
158+
) {
159+
const report = await this.abuseUserReportsRepository.findOneByOrFail({ id: reportId });
160+
161+
await this.abuseUserReportsRepository.update(report.id, {
162+
moderationNote: params.moderationNote,
163+
});
164+
165+
if (params.moderationNote != null && report.moderationNote !== params.moderationNote) {
166+
this.moderationLogService.log(moderator, 'updateAbuseReportNote', {
167+
reportId: report.id,
168+
report: report,
169+
before: report.moderationNote,
170+
after: params.moderationNote,
171+
});
172+
}
173+
}
128174
}

packages/backend/src/core/WebhookTestService.ts

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ function generateAbuseReport(override?: Partial<MiAbuseUserReport>): AbuseUserRe
3535
comment: 'This is a dummy report for testing purposes.',
3636
targetUserHost: null,
3737
reporterHost: null,
38+
resolvedAs: null,
39+
moderationNote: 'foo',
3840
...override,
3941
};
4042

packages/backend/src/core/entities/AbuseUserReportEntityService.ts

+2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ export class AbuseUserReportEntityService {
5353
schema: 'UserDetailedNotMe',
5454
}) : null,
5555
forwarded: report.forwarded,
56+
resolvedAs: report.resolvedAs,
57+
moderationNote: report.moderationNote,
5658
});
5759
}
5860

packages/backend/src/models/AbuseUserReport.ts

+18
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ export class MiAbuseUserReport {
5050
})
5151
public resolved: boolean;
5252

53+
/**
54+
* リモートサーバーに転送したかどうか
55+
*/
5356
@Column('boolean', {
5457
default: false,
5558
})
@@ -60,6 +63,21 @@ export class MiAbuseUserReport {
6063
})
6164
public comment: string;
6265

66+
@Column('varchar', {
67+
length: 8192, default: '',
68+
})
69+
public moderationNote: string;
70+
71+
/**
72+
* accept 是認 ... 通報内容が正当であり、肯定的に対応された
73+
* reject 否認 ... 通報内容が正当でなく、否定的に対応された
74+
* null ... その他
75+
*/
76+
@Column('varchar', {
77+
length: 128, nullable: true,
78+
})
79+
public resolvedAs: 'accept' | 'reject' | null;
80+
6381
//#region Denormalized fields
6482
@Index()
6583
@Column('varchar', {

packages/backend/src/server/api/EndpointsModule.ts

+8
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ import * as ep___admin_relays_list from './endpoints/admin/relays/list.js';
6868
import * as ep___admin_relays_remove from './endpoints/admin/relays/remove.js';
6969
import * as ep___admin_resetPassword from './endpoints/admin/reset-password.js';
7070
import * as ep___admin_resolveAbuseUserReport from './endpoints/admin/resolve-abuse-user-report.js';
71+
import * as ep___admin_forwardAbuseUserReport from './endpoints/admin/forward-abuse-user-report.js';
72+
import * as ep___admin_updateAbuseUserReport from './endpoints/admin/update-abuse-user-report.js';
7173
import * as ep___admin_sendEmail from './endpoints/admin/send-email.js';
7274
import * as ep___admin_serverInfo from './endpoints/admin/server-info.js';
7375
import * as ep___admin_showModerationLogs from './endpoints/admin/show-moderation-logs.js';
@@ -453,6 +455,8 @@ const $admin_relays_list: Provider = { provide: 'ep:admin/relays/list', useClass
453455
const $admin_relays_remove: Provider = { provide: 'ep:admin/relays/remove', useClass: ep___admin_relays_remove.default };
454456
const $admin_resetPassword: Provider = { provide: 'ep:admin/reset-password', useClass: ep___admin_resetPassword.default };
455457
const $admin_resolveAbuseUserReport: Provider = { provide: 'ep:admin/resolve-abuse-user-report', useClass: ep___admin_resolveAbuseUserReport.default };
458+
const $admin_forwardAbuseUserReport: Provider = { provide: 'ep:admin/forward-abuse-user-report', useClass: ep___admin_forwardAbuseUserReport.default };
459+
const $admin_updateAbuseUserReport: Provider = { provide: 'ep:admin/update-abuse-user-report', useClass: ep___admin_updateAbuseUserReport.default };
456460
const $admin_sendEmail: Provider = { provide: 'ep:admin/send-email', useClass: ep___admin_sendEmail.default };
457461
const $admin_serverInfo: Provider = { provide: 'ep:admin/server-info', useClass: ep___admin_serverInfo.default };
458462
const $admin_showModerationLogs: Provider = { provide: 'ep:admin/show-moderation-logs', useClass: ep___admin_showModerationLogs.default };
@@ -842,6 +846,8 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
842846
$admin_relays_remove,
843847
$admin_resetPassword,
844848
$admin_resolveAbuseUserReport,
849+
$admin_forwardAbuseUserReport,
850+
$admin_updateAbuseUserReport,
845851
$admin_sendEmail,
846852
$admin_serverInfo,
847853
$admin_showModerationLogs,
@@ -1225,6 +1231,8 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
12251231
$admin_relays_remove,
12261232
$admin_resetPassword,
12271233
$admin_resolveAbuseUserReport,
1234+
$admin_forwardAbuseUserReport,
1235+
$admin_updateAbuseUserReport,
12281236
$admin_sendEmail,
12291237
$admin_serverInfo,
12301238
$admin_showModerationLogs,

packages/backend/src/server/api/endpoints.ts

+4
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ import * as ep___admin_relays_list from './endpoints/admin/relays/list.js';
7474
import * as ep___admin_relays_remove from './endpoints/admin/relays/remove.js';
7575
import * as ep___admin_resetPassword from './endpoints/admin/reset-password.js';
7676
import * as ep___admin_resolveAbuseUserReport from './endpoints/admin/resolve-abuse-user-report.js';
77+
import * as ep___admin_forwardAbuseUserReport from './endpoints/admin/forward-abuse-user-report.js';
78+
import * as ep___admin_updateAbuseUserReport from './endpoints/admin/update-abuse-user-report.js';
7779
import * as ep___admin_sendEmail from './endpoints/admin/send-email.js';
7880
import * as ep___admin_serverInfo from './endpoints/admin/server-info.js';
7981
import * as ep___admin_showModerationLogs from './endpoints/admin/show-moderation-logs.js';
@@ -457,6 +459,8 @@ const eps = [
457459
['admin/relays/remove', ep___admin_relays_remove],
458460
['admin/reset-password', ep___admin_resetPassword],
459461
['admin/resolve-abuse-user-report', ep___admin_resolveAbuseUserReport],
462+
['admin/forward-abuse-user-report', ep___admin_forwardAbuseUserReport],
463+
['admin/update-abuse-user-report', ep___admin_updateAbuseUserReport],
460464
['admin/send-email', ep___admin_sendEmail],
461465
['admin/server-info', ep___admin_serverInfo],
462466
['admin/show-moderation-logs', ep___admin_showModerationLogs],

0 commit comments

Comments
 (0)