mirror of
https://github.com/MisskeyIO/misskey
synced 2024-11-27 14:28:49 +09:00
feat(webhook): 通報の登録、解決、自動解決に対応 (#615)
This commit is contained in:
parent
432833747d
commit
470ce2e0f4
12
locales/index.d.ts
vendored
12
locales/index.d.ts
vendored
@ -9484,6 +9484,18 @@ export interface Locale extends ILocale {
|
||||
* メンションされたとき
|
||||
*/
|
||||
"mention": string;
|
||||
/**
|
||||
* 通報が作成されたとき
|
||||
*/
|
||||
"reportCreated": string;
|
||||
/**
|
||||
* 通報が解決されたとき
|
||||
*/
|
||||
"reportResolved": string;
|
||||
/**
|
||||
* 通報が自動解決されたとき
|
||||
*/
|
||||
"reportAutoResolved": string;
|
||||
};
|
||||
};
|
||||
"_abuse": {
|
||||
|
@ -2513,6 +2513,9 @@ _webhookSettings:
|
||||
renote: "Renoteされたとき"
|
||||
reaction: "リアクションがあったとき"
|
||||
mention: "メンションされたとき"
|
||||
reportCreated: "通報が登録されたとき"
|
||||
reportResolved: "通報が解決されたとき"
|
||||
reportAutoResolved: "通報が自動解決されたとき"
|
||||
|
||||
_abuse:
|
||||
_resolver:
|
||||
|
@ -7,7 +7,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typ
|
||||
import { id } from './util/id.js';
|
||||
import { MiUser } from './User.js';
|
||||
|
||||
export const webhookEventTypes = ['mention', 'unfollow', 'follow', 'followed', 'note', 'reply', 'renote', 'reaction'] as const;
|
||||
export const webhookEventTypes = ['mention', 'unfollow', 'follow', 'followed', 'note', 'reply', 'renote', 'reaction', 'reportCreated', 'reportResolved', 'reportAutoResolved'] as const;
|
||||
|
||||
@Entity('webhook')
|
||||
export class MiWebhook {
|
||||
|
@ -15,6 +15,7 @@ import type { AbuseReportResolversRepository, AbuseUserReportsRepository, UsersR
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
||||
import { QueueService } from '@/core/QueueService.js';
|
||||
import { WebhookService } from '@/core/WebhookService.js';
|
||||
import { QueueLoggerService } from '../QueueLoggerService.js';
|
||||
import type { DbAbuseReportJobData } from '../types.js';
|
||||
import type * as Bull from 'bullmq';
|
||||
@ -39,6 +40,7 @@ export class ReportAbuseProcessorService {
|
||||
private apRendererService: ApRendererService,
|
||||
private roleService: RoleService,
|
||||
private queueService: QueueService,
|
||||
private webhookService: WebhookService,
|
||||
) {
|
||||
this.logger = this.queueLoggerService.logger.createSubLogger('report-abuse');
|
||||
}
|
||||
@ -86,6 +88,20 @@ export class ReportAbuseProcessorService {
|
||||
forwarded: resolver.forward && job.data.targetUserHost !== null && job.data.reporterHost === null,
|
||||
});
|
||||
|
||||
const activeWebhooks = await this.webhookService.getActiveWebhooks();
|
||||
for (const webhook of activeWebhooks) {
|
||||
const webhookUser = await this.usersRepository.findOneByOrFail({
|
||||
id: webhook.userId,
|
||||
});
|
||||
const isAdmin = await this.roleService.isAdministrator(webhookUser);
|
||||
if (webhook.on.includes('reportAutoResolved') && isAdmin) {
|
||||
this.queueService.webhookDeliver(webhook, 'reportAutoResolved', {
|
||||
resolver: resolver,
|
||||
report: job.data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ import { QueueService } from '@/core/QueueService.js';
|
||||
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||
import { WebhookService } from '@/core/WebhookService.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['admin'],
|
||||
@ -44,6 +46,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
private instanceActorService: InstanceActorService,
|
||||
private apRendererService: ApRendererService,
|
||||
private moderationLogService: ModerationLogService,
|
||||
private webhookService: WebhookService,
|
||||
private roleService: RoleService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const report = await this.abuseUserReportsRepository.findOneBy({ id: ps.reportId });
|
||||
@ -59,11 +63,24 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
this.queueService.deliver(actor, this.apRendererService.addContext(this.apRendererService.renderFlag(actor, targetUser.uri!, report.comment)), targetUser.inbox, false);
|
||||
}
|
||||
|
||||
await this.abuseUserReportsRepository.update(report.id, {
|
||||
const updatedReport = await this.abuseUserReportsRepository.update(report.id, {
|
||||
resolved: true,
|
||||
assigneeId: me.id,
|
||||
forwarded: ps.forward && report.targetUserHost != null,
|
||||
}).then(() => this.abuseUserReportsRepository.findOneBy({ id: ps.reportId }));
|
||||
|
||||
const activeWebhooks = await this.webhookService.getActiveWebhooks();
|
||||
for (const webhook of activeWebhooks) {
|
||||
const webhookUser = await this.usersRepository.findOneByOrFail({
|
||||
id: webhook.userId,
|
||||
});
|
||||
const isAdmin = await this.roleService.isAdministrator(webhookUser);
|
||||
if (webhook.on.includes('reportResolved') && isAdmin) {
|
||||
this.queueService.webhookDeliver(webhook, 'reportResolved', {
|
||||
updatedReport,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.moderationLogService.log(me, 'resolveAbuseReport', {
|
||||
reportId: report.id,
|
||||
|
@ -27,6 +27,11 @@ export const meta = {
|
||||
code: 'TOO_MANY_WEBHOOKS',
|
||||
id: '87a9bb19-111e-4e37-81d3-a3e7426453b0',
|
||||
},
|
||||
youAreNotAdmin: {
|
||||
message: 'You are not an administrator.',
|
||||
code: 'YOU_ARE_NOT_ADMIN',
|
||||
id: '26601bea-079b-4782-8dac-071febe2acf9',
|
||||
},
|
||||
},
|
||||
|
||||
res: {
|
||||
@ -90,6 +95,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
throw new ApiError(meta.errors.tooManyWebhooks);
|
||||
}
|
||||
|
||||
if (ps.on.includes('reportCreated') || ps.on.includes('reportResolved') || ps.on.includes('reportAutoResolved')) {
|
||||
if (!await this.roleService.isAdministrator(me)) {
|
||||
throw new ApiError(meta.errors.youAreNotAdmin);
|
||||
}
|
||||
}
|
||||
|
||||
const webhook = await this.webhooksRepository.insert({
|
||||
id: this.idService.gen(),
|
||||
userId: me.id,
|
||||
|
@ -9,6 +9,7 @@ import type { WebhooksRepository } from '@/models/_.js';
|
||||
import { webhookEventTypes } from '@/models/Webhook.js';
|
||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { ApiError } from '../../../error.js';
|
||||
|
||||
export const meta = {
|
||||
@ -25,6 +26,11 @@ export const meta = {
|
||||
code: 'NO_SUCH_WEBHOOK',
|
||||
id: 'fb0fea69-da18-45b1-828d-bd4fd1612518',
|
||||
},
|
||||
youAreNotAdmin: {
|
||||
message: 'You are not an administrator.',
|
||||
code: 'YOU_ARE_NOT_ADMIN',
|
||||
id: 'a70c7643-1db5-4ebf-becd-ff4b4223cf23',
|
||||
},
|
||||
},
|
||||
|
||||
} as const;
|
||||
@ -53,6 +59,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
private webhooksRepository: WebhooksRepository,
|
||||
|
||||
private globalEventService: GlobalEventService,
|
||||
private roleService: RoleService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const webhook = await this.webhooksRepository.findOneBy({
|
||||
@ -64,6 +71,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
throw new ApiError(meta.errors.noSuchWebhook);
|
||||
}
|
||||
|
||||
if (ps.on.includes('reportCreated') || ps.on.includes('reportResolved') || ps.on.includes('reportAutoResolved')) {
|
||||
if (!await this.roleService.isAdministrator(me)) {
|
||||
throw new ApiError(meta.errors.youAreNotAdmin);
|
||||
}
|
||||
}
|
||||
|
||||
await this.webhooksRepository.update(webhook.id, {
|
||||
name: ps.name,
|
||||
url: ps.url,
|
||||
|
@ -5,13 +5,14 @@
|
||||
|
||||
import sanitizeHtml from 'sanitize-html';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import type { AbuseUserReportsRepository } from '@/models/_.js';
|
||||
import type { AbuseUserReportsRepository, UsersRepository } from '@/models/_.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { GetterService } from '@/server/api/GetterService.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { QueueService } from '@/core/QueueService.js';
|
||||
import { WebhookService } from '@/core/WebhookService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
@ -69,10 +70,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
@Inject(DI.abuseUserReportsRepository)
|
||||
private abuseUserReportsRepository: AbuseUserReportsRepository,
|
||||
|
||||
@Inject(DI.usersRepository)
|
||||
private usersRepository: UsersRepository,
|
||||
|
||||
private idService: IdService,
|
||||
private getterService: GetterService,
|
||||
private roleService: RoleService,
|
||||
private queueService: QueueService,
|
||||
private webhookService: WebhookService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
// Lookup user
|
||||
@ -95,6 +100,19 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
category: ps.category,
|
||||
}).then(x => this.abuseUserReportsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
|
||||
const activeWebhooks = await this.webhookService.getActiveWebhooks();
|
||||
for (const webhook of activeWebhooks) {
|
||||
const webhookUser = await this.usersRepository.findOneByOrFail({
|
||||
id: webhook.userId,
|
||||
});
|
||||
const isAdmin = await this.roleService.isAdministrator(webhookUser);
|
||||
if (webhook.on.includes('reportCreated') && isAdmin) {
|
||||
this.queueService.webhookDeliver(webhook, 'reportCreated', {
|
||||
report,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.queueService.createReportAbuseJob(report);
|
||||
});
|
||||
}
|
||||
|
@ -29,6 +29,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkSwitch v-model="event_renote">{{ i18n.ts._webhookSettings._events.renote }}</MkSwitch>
|
||||
<MkSwitch v-model="event_reaction">{{ i18n.ts._webhookSettings._events.reaction }}</MkSwitch>
|
||||
<MkSwitch v-model="event_mention">{{ i18n.ts._webhookSettings._events.mention }}</MkSwitch>
|
||||
<MkSwitch v-if="$i?.isAdmin" v-model="event_reportCreated">{{ i18n.ts._webhookSettings._events.reportCreated }}</MkSwitch>
|
||||
<MkSwitch v-if="$i?.isAdmin" v-model="event_reportResolved">{{ i18n.ts._webhookSettings._events.reportResolved }}</MkSwitch>
|
||||
<MkSwitch v-if="$i?.isAdmin" v-model="event_reportAutoResolved">{{ i18n.ts._webhookSettings._events.reportAutoResolved }}</MkSwitch>
|
||||
</div>
|
||||
</FormSection>
|
||||
|
||||
@ -52,6 +55,7 @@ import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||
import { useRouter } from '@/router/supplier.js';
|
||||
import { $i } from '@/account.js';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
@ -75,6 +79,9 @@ const event_reply = ref(webhook.on.includes('reply'));
|
||||
const event_renote = ref(webhook.on.includes('renote'));
|
||||
const event_reaction = ref(webhook.on.includes('reaction'));
|
||||
const event_mention = ref(webhook.on.includes('mention'));
|
||||
const event_reportCreated = ref(webhook.on.includes('reportCreated'));
|
||||
const event_reportResolved = ref(webhook.on.includes('reportResolved'));
|
||||
const event_reportAutoResolved = ref(webhook.on.includes('reportAutoResolved'));
|
||||
|
||||
async function save(): Promise<void> {
|
||||
const events = [];
|
||||
@ -85,6 +92,9 @@ async function save(): Promise<void> {
|
||||
if (event_renote.value) events.push('renote');
|
||||
if (event_reaction.value) events.push('reaction');
|
||||
if (event_mention.value) events.push('mention');
|
||||
if (event_reportCreated.value) events.push('reportCreated');
|
||||
if (event_reportResolved.value) events.push('reportResolved');
|
||||
if (event_reportAutoResolved.value) events.push('reportAutoResolved');
|
||||
|
||||
os.apiWithDialog('i/webhooks/update', {
|
||||
name: name.value,
|
||||
|
@ -29,6 +29,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkSwitch v-model="event_renote">{{ i18n.ts._webhookSettings._events.renote }}</MkSwitch>
|
||||
<MkSwitch v-model="event_reaction">{{ i18n.ts._webhookSettings._events.reaction }}</MkSwitch>
|
||||
<MkSwitch v-model="event_mention">{{ i18n.ts._webhookSettings._events.mention }}</MkSwitch>
|
||||
<MkSwitch v-if="$i?.isAdmin" v-model="event_reportCreated">{{ i18n.ts._webhookSettings._events.reportCreated }}</MkSwitch>
|
||||
<MkSwitch v-if="$i?.isAdmin" v-model="event_reportResolved">{{ i18n.ts._webhookSettings._events.reportResolved }}</MkSwitch>
|
||||
<MkSwitch v-if="$i?.isAdmin" v-model="event_reportAutoResolved">{{ i18n.ts._webhookSettings._events.reportAutoResolved }}</MkSwitch>
|
||||
</div>
|
||||
</FormSection>
|
||||
|
||||
@ -47,6 +50,7 @@ import MkButton from '@/components/MkButton.vue';
|
||||
import * as os from '@/os.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||
import { $i } from '@/account.js';
|
||||
|
||||
const name = ref('');
|
||||
const url = ref('');
|
||||
@ -59,6 +63,9 @@ const event_reply = ref(true);
|
||||
const event_renote = ref(true);
|
||||
const event_reaction = ref(true);
|
||||
const event_mention = ref(true);
|
||||
const event_reportCreated = ref(false);
|
||||
const event_reportResolved = ref(false);
|
||||
const event_reportAutoResolved = ref(false);
|
||||
|
||||
async function create(): Promise<void> {
|
||||
const events = [];
|
||||
@ -69,6 +76,9 @@ async function create(): Promise<void> {
|
||||
if (event_renote.value) events.push('renote');
|
||||
if (event_reaction.value) events.push('reaction');
|
||||
if (event_mention.value) events.push('mention');
|
||||
if (event_reportCreated.value) events.push('reportCreated');
|
||||
if (event_reportResolved.value) events.push('reportResolved');
|
||||
if (event_reportAutoResolved.value) events.push('reportAutoResolved');
|
||||
|
||||
os.apiWithDialog('i/webhooks/create', {
|
||||
name: name.value,
|
||||
|
Loading…
Reference in New Issue
Block a user