upd: add decline endpoint and free up username on decline

This commit is contained in:
Marie 2024-10-17 20:11:10 +02:00
parent 786677b079
commit 1d9cb4fad9
No known key found for this signature in database
GPG key ID: 7ADF6C9CD9A28555
13 changed files with 170 additions and 2 deletions

View file

@ -79,6 +79,7 @@ import * as ep___admin_silenceUser from './endpoints/admin/silence-user.js';
import * as ep___admin_unsilenceUser from './endpoints/admin/unsilence-user.js'; import * as ep___admin_unsilenceUser from './endpoints/admin/unsilence-user.js';
import * as ep___admin_suspendUser from './endpoints/admin/suspend-user.js'; import * as ep___admin_suspendUser from './endpoints/admin/suspend-user.js';
import * as ep___admin_approveUser from './endpoints/admin/approve-user.js'; import * as ep___admin_approveUser from './endpoints/admin/approve-user.js';
import * as ep___admin_declineUser from './endpoints/admin/decline-user.js';
import * as ep___admin_unsuspendUser from './endpoints/admin/unsuspend-user.js'; import * as ep___admin_unsuspendUser from './endpoints/admin/unsuspend-user.js';
import * as ep___admin_updateMeta from './endpoints/admin/update-meta.js'; import * as ep___admin_updateMeta from './endpoints/admin/update-meta.js';
import * as ep___admin_deleteAccount from './endpoints/admin/delete-account.js'; import * as ep___admin_deleteAccount from './endpoints/admin/delete-account.js';
@ -477,6 +478,7 @@ const $admin_silenceUser: Provider = { provide: 'ep:admin/silence-user', useClas
const $admin_unsilenceUser: Provider = { provide: 'ep:admin/unsilence-user', useClass: ep___admin_unsilenceUser.default }; const $admin_unsilenceUser: Provider = { provide: 'ep:admin/unsilence-user', useClass: ep___admin_unsilenceUser.default };
const $admin_suspendUser: Provider = { provide: 'ep:admin/suspend-user', useClass: ep___admin_suspendUser.default }; const $admin_suspendUser: Provider = { provide: 'ep:admin/suspend-user', useClass: ep___admin_suspendUser.default };
const $admin_approveUser: Provider = { provide: 'ep:admin/approve-user', useClass: ep___admin_approveUser.default }; const $admin_approveUser: Provider = { provide: 'ep:admin/approve-user', useClass: ep___admin_approveUser.default };
const $admin_declineUser: Provider = { provide: 'ep:admin/decline-user', useClass: ep___admin_declineUser.default };
const $admin_unsuspendUser: Provider = { provide: 'ep:admin/unsuspend-user', useClass: ep___admin_unsuspendUser.default }; const $admin_unsuspendUser: Provider = { provide: 'ep:admin/unsuspend-user', useClass: ep___admin_unsuspendUser.default };
const $admin_updateMeta: Provider = { provide: 'ep:admin/update-meta', useClass: ep___admin_updateMeta.default }; const $admin_updateMeta: Provider = { provide: 'ep:admin/update-meta', useClass: ep___admin_updateMeta.default };
const $admin_deleteAccount: Provider = { provide: 'ep:admin/delete-account', useClass: ep___admin_deleteAccount.default }; const $admin_deleteAccount: Provider = { provide: 'ep:admin/delete-account', useClass: ep___admin_deleteAccount.default };
@ -879,6 +881,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
$admin_unsilenceUser, $admin_unsilenceUser,
$admin_suspendUser, $admin_suspendUser,
$admin_approveUser, $admin_approveUser,
$admin_declineUser,
$admin_unsuspendUser, $admin_unsuspendUser,
$admin_updateMeta, $admin_updateMeta,
$admin_deleteAccount, $admin_deleteAccount,
@ -1275,6 +1278,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
$admin_unsilenceUser, $admin_unsilenceUser,
$admin_suspendUser, $admin_suspendUser,
$admin_approveUser, $admin_approveUser,
$admin_declineUser,
$admin_unsuspendUser, $admin_unsuspendUser,
$admin_updateMeta, $admin_updateMeta,
$admin_deleteAccount, $admin_deleteAccount,

View file

@ -85,6 +85,7 @@ import * as ep___admin_silenceUser from './endpoints/admin/silence-user.js';
import * as ep___admin_unsilenceUser from './endpoints/admin/unsilence-user.js'; import * as ep___admin_unsilenceUser from './endpoints/admin/unsilence-user.js';
import * as ep___admin_suspendUser from './endpoints/admin/suspend-user.js'; import * as ep___admin_suspendUser from './endpoints/admin/suspend-user.js';
import * as ep___admin_approveUser from './endpoints/admin/approve-user.js'; import * as ep___admin_approveUser from './endpoints/admin/approve-user.js';
import * as ep___admin_declineUser from './endpoints/admin/decline-user.js';
import * as ep___admin_unsuspendUser from './endpoints/admin/unsuspend-user.js'; import * as ep___admin_unsuspendUser from './endpoints/admin/unsuspend-user.js';
import * as ep___admin_updateMeta from './endpoints/admin/update-meta.js'; import * as ep___admin_updateMeta from './endpoints/admin/update-meta.js';
import * as ep___admin_deleteAccount from './endpoints/admin/delete-account.js'; import * as ep___admin_deleteAccount from './endpoints/admin/delete-account.js';
@ -481,6 +482,7 @@ const eps = [
['admin/unsilence-user', ep___admin_unsilenceUser], ['admin/unsilence-user', ep___admin_unsilenceUser],
['admin/suspend-user', ep___admin_suspendUser], ['admin/suspend-user', ep___admin_suspendUser],
['admin/approve-user', ep___admin_approveUser], ['admin/approve-user', ep___admin_approveUser],
['admin/decline-user', ep___admin_declineUser],
['admin/unsuspend-user', ep___admin_unsuspendUser], ['admin/unsuspend-user', ep___admin_unsuspendUser],
['admin/update-meta', ep___admin_updateMeta], ['admin/update-meta', ep___admin_updateMeta],
['admin/delete-account', ep___admin_deleteAccount], ['admin/delete-account', ep___admin_deleteAccount],

View file

@ -0,0 +1,67 @@
import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { UsedUsernamesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
import { DI } from '@/di-symbols.js';
import { EmailService } from '@/core/EmailService.js';
import { DeleteAccountService } from '@/core/DeleteAccountService.js';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
kind: 'write:admin:decline-user',
} as const;
export const paramDef = {
type: 'object',
properties: {
userId: { type: 'string', format: 'misskey:id' },
},
required: ['userId'],
} as const;
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@Inject(DI.userProfilesRepository)
private userProfilesRepository: UserProfilesRepository,
@Inject(DI.usedUsernamesRepository)
private usedUsernamesRepository: UsedUsernamesRepository,
private moderationLogService: ModerationLogService,
private emailService: EmailService,
private deleteAccountService: DeleteAccountService,
) {
super(meta, paramDef, async (ps, me) => {
const user = await this.usersRepository.findOneBy({ id: ps.userId });
if (user == null) {
throw new Error('user not found');
}
const profile = await this.userProfilesRepository.findOneBy({ userId: ps.userId });
if (profile?.email) {
this.emailService.sendEmail(profile.email, 'Account Declined',
'Your Account has been declined!',
'Your Account has been declined!');
}
await this.usedUsernamesRepository.delete({ username: user.username });
await this.deleteAccountService.deleteAccount(user);
this.moderationLogService.log(me, 'decline', {
userId: user.id,
userUsername: user.username,
userHost: user.host,
});
});
}
}

View file

@ -56,6 +56,7 @@ export const moderationLogTypes = [
'updateServerSettings', 'updateServerSettings',
'suspend', 'suspend',
'approve', 'approve',
'decline',
'unsuspend', 'unsuspend',
'updateUserNote', 'updateUserNote',
'addCustomEmoji', 'addCustomEmoji',

View file

@ -75,7 +75,7 @@ async function deleteAccount() {
if (typed.canceled) return; if (typed.canceled) return;
if (typed.result === props.user.username) { if (typed.result === props.user.username) {
await os.apiWithDialog('admin/delete-account', { await os.apiWithDialog('admin/decline-user', {
userId: props.user.id, userId: props.user.id,
}); });
emits('deleted', props.user.id); emits('deleted', props.user.id);

View file

@ -31,6 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
[$style.logRed]: [ [$style.logRed]: [
'suspend', 'suspend',
'approve', 'approve',
'decline',
'deleteRole', 'deleteRole',
'deleteGlobalAnnouncement', 'deleteGlobalAnnouncement',
'deleteUserAnnouncement', 'deleteUserAnnouncement',
@ -51,6 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<span v-if="log.type === 'updateUserNote'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span> <span v-if="log.type === 'updateUserNote'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
<span v-else-if="log.type === 'suspend'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span> <span v-else-if="log.type === 'suspend'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
<span v-else-if="log.type === 'approve'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span> <span v-else-if="log.type === 'approve'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
<span v-else-if="log.type === 'decline'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
<span v-else-if="log.type === 'unsuspend'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span> <span v-else-if="log.type === 'unsuspend'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
<span v-else-if="log.type === 'resetPassword'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span> <span v-else-if="log.type === 'resetPassword'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
<span v-else-if="log.type === 'assignRole'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }} <i class="ti ti-arrow-right"></i> {{ log.info.roleName }}</span> <span v-else-if="log.type === 'assignRole'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }} <i class="ti ti-arrow-right"></i> {{ log.info.roleName }}</span>

View file

@ -133,6 +133,9 @@ type AdminAvatarDecorationsListResponse = operations['admin___avatar-decorations
// @public (undocumented) // @public (undocumented)
type AdminAvatarDecorationsUpdateRequest = operations['admin___avatar-decorations___update']['requestBody']['content']['application/json']; type AdminAvatarDecorationsUpdateRequest = operations['admin___avatar-decorations___update']['requestBody']['content']['application/json'];
// @public (undocumented)
type AdminDeclineUserRequest = operations['admin___decline-user']['requestBody']['content']['application/json'];
// @public (undocumented) // @public (undocumented)
type AdminDeleteAccountRequest = operations['admin___delete-account']['requestBody']['content']['application/json']; type AdminDeleteAccountRequest = operations['admin___delete-account']['requestBody']['content']['application/json'];
@ -1319,6 +1322,7 @@ declare namespace entities {
AdminUnsilenceUserRequest, AdminUnsilenceUserRequest,
AdminSuspendUserRequest, AdminSuspendUserRequest,
AdminApproveUserRequest, AdminApproveUserRequest,
AdminDeclineUserRequest,
AdminUnsuspendUserRequest, AdminUnsuspendUserRequest,
AdminUpdateMetaRequest, AdminUpdateMetaRequest,
AdminDeleteAccountRequest, AdminDeleteAccountRequest,
@ -2421,6 +2425,9 @@ type ModerationLog = {
} | { } | {
type: 'approve'; type: 'approve';
info: ModerationLogPayloads['approve']; info: ModerationLogPayloads['approve'];
} | {
type: 'decline';
info: ModerationLogPayloads['decline'];
} | { } | {
type: 'suspend'; type: 'suspend';
info: ModerationLogPayloads['suspend']; info: ModerationLogPayloads['suspend'];
@ -2857,7 +2864,7 @@ type PartialRolePolicyOverride = Partial<{
}>; }>;
// @public (undocumented) // @public (undocumented)
export const permissions: readonly ["read:account", "write:account", "read:blocks", "write:blocks", "read:drive", "write:drive", "read:favorites", "write:favorites", "read:following", "write:following", "read:messaging", "write:messaging", "read:mutes", "write:mutes", "write:notes", "read:notifications", "write:notifications", "read:reactions", "write:reactions", "write:votes", "read:pages", "write:pages", "write:page-likes", "read:page-likes", "read:user-groups", "write:user-groups", "read:channels", "write:channels", "read:gallery", "write:gallery", "read:gallery-likes", "write:gallery-likes", "read:flash", "write:flash", "read:flash-likes", "write:flash-likes", "read:admin:abuse-user-reports", "write:admin:delete-account", "write:admin:delete-all-files-of-a-user", "read:admin:index-stats", "read:admin:table-stats", "read:admin:user-ips", "read:admin:meta", "write:admin:reset-password", "write:admin:resolve-abuse-user-report", "write:admin:send-email", "read:admin:server-info", "read:admin:show-moderation-log", "read:admin:show-user", "write:admin:suspend-user", "write:admin:approve-user", "write:admin:nsfw-user", "write:admin:unnsfw-user", "write:admin:silence-user", "write:admin:unsilence-user", "write:admin:unset-user-avatar", "write:admin:unset-user-banner", "write:admin:unsuspend-user", "write:admin:meta", "write:admin:user-note", "write:admin:roles", "read:admin:roles", "write:admin:relays", "read:admin:relays", "write:admin:invite-codes", "read:admin:invite-codes", "write:admin:announcements", "read:admin:announcements", "write:admin:avatar-decorations", "read:admin:avatar-decorations", "write:admin:federation", "write:admin:account", "read:admin:account", "write:admin:emoji", "read:admin:emoji", "write:admin:queue", "read:admin:queue", "write:admin:promo", "write:admin:drive", "read:admin:drive", "write:admin:ad", "read:admin:ad", "write:invite-codes", "read:invite-codes", "write:clip-favorite", "read:clip-favorite", "read:federation", "write:report-abuse"]; export const permissions: readonly ["read:account", "write:account", "read:blocks", "write:blocks", "read:drive", "write:drive", "read:favorites", "write:favorites", "read:following", "write:following", "read:messaging", "write:messaging", "read:mutes", "write:mutes", "write:notes", "read:notifications", "write:notifications", "read:reactions", "write:reactions", "write:votes", "read:pages", "write:pages", "write:page-likes", "read:page-likes", "read:user-groups", "write:user-groups", "read:channels", "write:channels", "read:gallery", "write:gallery", "read:gallery-likes", "write:gallery-likes", "read:flash", "write:flash", "read:flash-likes", "write:flash-likes", "read:admin:abuse-user-reports", "write:admin:delete-account", "write:admin:delete-all-files-of-a-user", "read:admin:index-stats", "read:admin:table-stats", "read:admin:user-ips", "read:admin:meta", "write:admin:reset-password", "write:admin:resolve-abuse-user-report", "write:admin:send-email", "read:admin:server-info", "read:admin:show-moderation-log", "read:admin:show-user", "write:admin:suspend-user", "write:admin:approve-user", "write:admin:decline-user", "write:admin:nsfw-user", "write:admin:unnsfw-user", "write:admin:silence-user", "write:admin:unsilence-user", "write:admin:unset-user-avatar", "write:admin:unset-user-banner", "write:admin:unsuspend-user", "write:admin:meta", "write:admin:user-note", "write:admin:roles", "read:admin:roles", "write:admin:relays", "read:admin:relays", "write:admin:invite-codes", "read:admin:invite-codes", "write:admin:announcements", "read:admin:announcements", "write:admin:avatar-decorations", "read:admin:avatar-decorations", "write:admin:federation", "write:admin:account", "read:admin:account", "write:admin:emoji", "read:admin:emoji", "write:admin:queue", "read:admin:queue", "write:admin:promo", "write:admin:drive", "read:admin:drive", "write:admin:ad", "read:admin:ad", "write:invite-codes", "read:invite-codes", "write:clip-favorite", "read:clip-favorite", "read:federation", "write:report-abuse"];
// @public (undocumented) // @public (undocumented)
type PingResponse = operations['ping']['responses']['200']['content']['application/json']; type PingResponse = operations['ping']['responses']['200']['content']['application/json'];

View file

@ -812,6 +812,17 @@ declare module '../api.js' {
credential?: string | null, credential?: string | null,
): Promise<SwitchCaseResponseType<E, P>>; ): Promise<SwitchCaseResponseType<E, P>>;
/**
* No description provided.
*
* **Credential required**: *Yes* / **Permission**: *write:admin:decline-user*
*/
request<E extends 'admin/decline-user', P extends Endpoints[E]['req']>(
endpoint: E,
params: P,
credential?: string | null,
): Promise<SwitchCaseResponseType<E, P>>;
/** /**
* No description provided. * No description provided.
* *

View file

@ -97,6 +97,7 @@ import type {
AdminUnsilenceUserRequest, AdminUnsilenceUserRequest,
AdminSuspendUserRequest, AdminSuspendUserRequest,
AdminApproveUserRequest, AdminApproveUserRequest,
AdminDeclineUserRequest,
AdminUnsuspendUserRequest, AdminUnsuspendUserRequest,
AdminUpdateMetaRequest, AdminUpdateMetaRequest,
AdminDeleteAccountRequest, AdminDeleteAccountRequest,
@ -666,6 +667,7 @@ export type Endpoints = {
'admin/unsilence-user': { req: AdminUnsilenceUserRequest; res: EmptyResponse }; 'admin/unsilence-user': { req: AdminUnsilenceUserRequest; res: EmptyResponse };
'admin/suspend-user': { req: AdminSuspendUserRequest; res: EmptyResponse }; 'admin/suspend-user': { req: AdminSuspendUserRequest; res: EmptyResponse };
'admin/approve-user': { req: AdminApproveUserRequest; res: EmptyResponse }; 'admin/approve-user': { req: AdminApproveUserRequest; res: EmptyResponse };
'admin/decline-user': { req: AdminDeclineUserRequest; res: EmptyResponse };
'admin/unsuspend-user': { req: AdminUnsuspendUserRequest; res: EmptyResponse }; 'admin/unsuspend-user': { req: AdminUnsuspendUserRequest; res: EmptyResponse };
'admin/update-meta': { req: AdminUpdateMetaRequest; res: EmptyResponse }; 'admin/update-meta': { req: AdminUpdateMetaRequest; res: EmptyResponse };
'admin/delete-account': { req: AdminDeleteAccountRequest; res: EmptyResponse }; 'admin/delete-account': { req: AdminDeleteAccountRequest; res: EmptyResponse };
@ -1063,6 +1065,7 @@ export const endpointReqTypes: Record<keyof Endpoints, 'application/json' | 'mul
'admin/unsilence-user': 'application/json', 'admin/unsilence-user': 'application/json',
'admin/suspend-user': 'application/json', 'admin/suspend-user': 'application/json',
'admin/approve-user': 'application/json', 'admin/approve-user': 'application/json',
'admin/decline-user': 'application/json',
'admin/unsuspend-user': 'application/json', 'admin/unsuspend-user': 'application/json',
'admin/update-meta': 'application/json', 'admin/update-meta': 'application/json',
'admin/delete-account': 'application/json', 'admin/delete-account': 'application/json',

View file

@ -100,6 +100,7 @@ export type AdminSilenceUserRequest = operations['admin___silence-user']['reques
export type AdminUnsilenceUserRequest = operations['admin___unsilence-user']['requestBody']['content']['application/json']; export type AdminUnsilenceUserRequest = operations['admin___unsilence-user']['requestBody']['content']['application/json'];
export type AdminSuspendUserRequest = operations['admin___suspend-user']['requestBody']['content']['application/json']; export type AdminSuspendUserRequest = operations['admin___suspend-user']['requestBody']['content']['application/json'];
export type AdminApproveUserRequest = operations['admin___approve-user']['requestBody']['content']['application/json']; export type AdminApproveUserRequest = operations['admin___approve-user']['requestBody']['content']['application/json'];
export type AdminDeclineUserRequest = operations['admin___decline-user']['requestBody']['content']['application/json'];
export type AdminUnsuspendUserRequest = operations['admin___unsuspend-user']['requestBody']['content']['application/json']; export type AdminUnsuspendUserRequest = operations['admin___unsuspend-user']['requestBody']['content']['application/json'];
export type AdminUpdateMetaRequest = operations['admin___update-meta']['requestBody']['content']['application/json']; export type AdminUpdateMetaRequest = operations['admin___update-meta']['requestBody']['content']['application/json'];
export type AdminDeleteAccountRequest = operations['admin___delete-account']['requestBody']['content']['application/json']; export type AdminDeleteAccountRequest = operations['admin___delete-account']['requestBody']['content']['application/json'];

View file

@ -675,6 +675,15 @@ export type paths = {
*/ */
post: operations['admin___approve-user']; post: operations['admin___approve-user'];
}; };
'/admin/decline-user': {
/**
* admin/decline-user
* @description No description provided.
*
* **Credential required**: *Yes* / **Permission**: *write:admin:decline-user*
*/
post: operations['admin___decline-user'];
};
'/admin/unsuspend-user': { '/admin/unsuspend-user': {
/** /**
* admin/unsuspend-user * admin/unsuspend-user
@ -9666,6 +9675,58 @@ export type operations = {
}; };
}; };
}; };
/**
* admin/decline-user
* @description No description provided.
*
* **Credential required**: *Yes* / **Permission**: *write:admin:decline-user*
*/
'admin___decline-user': {
requestBody: {
content: {
'application/json': {
/** Format: misskey:id */
userId: string;
};
};
};
responses: {
/** @description OK (without any results) */
204: {
content: never;
};
/** @description Client error */
400: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Authentication error */
401: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Forbidden error */
403: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description I'm Ai */
418: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Internal server error */
500: {
content: {
'application/json': components['schemas']['Error'];
};
};
};
};
/** /**
* admin/unsuspend-user * admin/unsuspend-user
* @description No description provided. * @description No description provided.

View file

@ -78,6 +78,7 @@ export const permissions = [
'read:admin:show-user', 'read:admin:show-user',
'write:admin:suspend-user', 'write:admin:suspend-user',
'write:admin:approve-user', 'write:admin:approve-user',
'write:admin:decline-user',
'write:admin:nsfw-user', 'write:admin:nsfw-user',
'write:admin:unnsfw-user', 'write:admin:unnsfw-user',
'write:admin:silence-user', 'write:admin:silence-user',
@ -204,6 +205,11 @@ export type ModerationLogPayloads = {
userUsername: string; userUsername: string;
userHost: string | null; userHost: string | null;
}; };
decline: {
userId: string;
userUsername: string;
userHost: string | null;
};
unsuspend: { unsuspend: {
userId: string; userId: string;
userUsername: string; userUsername: string;

View file

@ -50,6 +50,9 @@ export type ModerationLog = {
} | { } | {
type: 'approve'; type: 'approve';
info: ModerationLogPayloads['approve']; info: ModerationLogPayloads['approve'];
} | {
type: 'decline';
info: ModerationLogPayloads['decline'];
} | { } | {
type: 'suspend'; type: 'suspend';
info: ModerationLogPayloads['suspend']; info: ModerationLogPayloads['suspend'];