diff --git a/locales/en-US.yml b/locales/en-US.yml index 6f51ac0f3..52f47144e 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1287,6 +1287,10 @@ autoRemoval: "Automatic note deletion" autoRemovalDescription: "You can delete your note when it exceeds period you set." CheckedByHIBP: "In addition to ensuring your passwords are secure, HIBP scans for password leaks." changeUserName: "Change name" +normalize: "Normalize" +normalizeConfirm: "After normalization, the account will be irreversible. Are you sure you want to do this?" +normalizeDescription: "Normalization is a feature for bulk data wiping and account suspension of users, which has a similar effect to deleting an account and closes all reports after it is run. Please note that this is an irreversible action." +useNormalization: "Show the Normalize menu" _bubbleGame: howToPlay: "How to play" hold: "Hold" @@ -2578,6 +2582,7 @@ _moderationLogTypes: deleteAvatarDecoration: "Avatar decoration deleted" unsetUserAvatar: "Unset this user's avatar" unsetUserBanner: "Unset this user's banner" + normalize: "Normalization" _fileViewer: title: "File details" type: "File type" diff --git a/locales/index.d.ts b/locales/index.d.ts index b0f6f5af4..f9bec2e7f 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -5312,6 +5312,22 @@ export interface Locale extends ILocale { * 名前を変更 */ "changeUserName": string; + /** + * 正常化 + */ + "normalize": string; + /** + * 正常化すると元に戻せなくなり、これはアカウントの削除と同様の効力を持ちます。実行しますか? + */ + "normalizeConfirm": string; + /** + * 正常化は、ユーザーの一括的なデータ抹消やアカウント制裁が必要な場合に使用する機能です。アカウントを正常化した後は取り返しのつかないことに留意してください。 + */ + "normalizeDescription": string; + /** + * 正規化機能を使用する + */ + "useNormalization": string; "_bubbleGame": { /** * 遊び方 @@ -10283,6 +10299,10 @@ export interface Locale extends ILocale { * ユーザーのバナーを解除 */ "unsetUserBanner": string; + /** + * 正 常 化 + */ + "normalize": string; }; "_fileViewer": { /** diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index a8a155db1..116b565ca 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1322,6 +1322,10 @@ dangerZone: "危険区域" dangerZoneDescription: "以下の機能を利用する際は、特にご注意ください。" checkedByHIBP: "パスワードの安全性に加え、HIBPを通じてパスワードの漏洩を検査します。" changeUserName: "名前を変更" +normalize: "正常化" +normalizeConfirm: "正常化すると元に戻せなくなり、これはアカウントの削除と同様の効力を持ちます。実行しますか?" +normalizeDescription: "正常化は、ユーザーの一括的なデータ抹消やアカウント制裁が必要な場合に使用する機能です。アカウントを正常化した後は取り返しのつかないことに留意してください。" +useNormalization: "正規化機能を使用する" _bubbleGame: howToPlay: "遊び方" @@ -2717,6 +2721,7 @@ _moderationLogTypes: deleteAvatarDecoration: "アイコンデコレーションを削除" unsetUserAvatar: "ユーザーのアイコンを解除" unsetUserBanner: "ユーザーのバナーを解除" + normalize: "正 常 化" _fileViewer: title: "ファイルの詳細" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 67253bd99..c294ff98d 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -610,12 +610,12 @@ removeAllFollowingDescription: "{host} 서버의 모든 팔로잉을 해제합 userSuspended: "이 계정은 정지된 상태입니다." userLimited: "이 계정은 제한된 상태입니다." userSilenced: "이 계정은 사일런스된 상태입니다." -yourAccountSuspendedTitle: "계정이 정지되었습니다" -yourAccountSuspendedDescription: "이 계정은 서버의 이용 약관을 위반하거나, 기타 다른 이유로 인해 정지되었습니다. 자세한 사항은 관리자에게 문의해 주십시오. 계정을 새로 생성하지 마십시오." +yourAccountSuspendedTitle: "당신의 계정이 정지되었습니다" +yourAccountSuspendedDescription: "당신의 계정이 규정 위반 또는 관리자 재량에 의해 정지되었습니다. 잘못되었다고 생각할 경우 관리자에게 문의해주십시오." tokenRevoked: "유효하지 않은 토큰입니다" -tokenRevokedDescription: "로그인 토큰이 비활성화되었습니다. 다시 로그인하여 주십시오." -accountDeleted: "계정이 정지되었습니다" -accountDeletedDescription: "이 계정이 삭제되었습니다." +tokenRevokedDescription: "다른 곳에서 비밀번호를 변경했거나, 다른 모든 세션을 종료했습니다. 계속하려면 다시 로그인하세요." +accountDeleted: "당신의 계정이 정지되었습니다" +accountDeletedDescription: "당신의 계정이 일정 기간 이상 비활동 또는 관리자 재량에 의해 삭제되었습니다." menu: "메뉴" divider: "구분선" addItem: "항목 추가" @@ -1310,6 +1310,10 @@ dangerZoneDescription: "함부로 실행하면 어딘가 고장날 수 있는 checkedByHIBP: "비밀번호의 안전성과 더불어, HIBP를 통해 비밀번호 유출을 검사합니다." changeUserName: "이름 변경" pleaseSelectAccount: "사용할 계정을 선택해주십시오" +normalize: "정상화" +normalizeConfirm: "정상화 이후에는 계정을 되돌릴 수 없게 됩니다. 실행하시겠습니까?" +normalizeDescription: "정상화는 유저의 일괄적인 데이터 말소 및 계정 정지를 위한 기능으로, 계정 삭제와 비슷한 효과를 가지며, 실행 후에는 모든 신고가 닫히게 됩니다. 기능을 실행하고 나면 되돌릴 수 없는 점을 유의하시기 바랍니다." +useNormalization: "정상화 메뉴를 표시하기" _bubbleGame: howToPlay: "설명" hold: "홀드" @@ -2609,6 +2613,7 @@ _moderationLogTypes: deleteAvatarDecoration: "아바타 장식 삭제" unsetUserAvatar: "유저 아바타 제거" unsetUserBanner: "유저 배너 제거" + normalize: "정 상 화" _fileViewer: title: "파일 상세" type: "파일 유형" diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index c42e1816f..ec0489d77 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -64,6 +64,7 @@ import * as ep___admin_getTableStats from './endpoints/admin/get-table-stats.js' import * as ep___admin_getUserIps from './endpoints/admin/get-user-ips.js'; import * as ep___admin_invite_create from './endpoints/admin/invite/create.js'; import * as ep___admin_invite_list from './endpoints/admin/invite/list.js'; +import * as ep___admin_normalization from './endpoints/admin/normalization.js'; import * as ep___admin_promo_create from './endpoints/admin/promo/create.js'; import * as ep___admin_queue_clear from './endpoints/admin/queue/clear.js'; import * as ep___admin_queue_deliverDelayed from './endpoints/admin/queue/deliver-delayed.js'; @@ -462,6 +463,7 @@ const $admin_getTableStats: Provider = { provide: 'ep:admin/get-table-stats', us const $admin_getUserIps: Provider = { provide: 'ep:admin/get-user-ips', useClass: ep___admin_getUserIps.default }; const $admin_invite_create: Provider = { provide: 'ep:admin/invite/create', useClass: ep___admin_invite_create.default }; const $admin_invite_list: Provider = { provide: 'ep:admin/invite/list', useClass: ep___admin_invite_list.default }; +const $admin_normalization: Provider = { provide: 'ep:admin/normalization', useClass: ep___admin_normalization.default }; const $admin_promo_create: Provider = { provide: 'ep:admin/promo/create', useClass: ep___admin_promo_create.default }; const $admin_queue_clear: Provider = { provide: 'ep:admin/queue/clear', useClass: ep___admin_queue_clear.default }; const $admin_queue_deliverDelayed: Provider = { provide: 'ep:admin/queue/deliver-delayed', useClass: ep___admin_queue_deliverDelayed.default }; @@ -864,6 +866,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $admin_getUserIps, $admin_invite_create, $admin_invite_list, + $admin_normalization, $admin_promo_create, $admin_queue_clear, $admin_queue_deliverDelayed, @@ -1260,6 +1263,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $admin_getUserIps, $admin_invite_create, $admin_invite_list, + $admin_normalization, $admin_promo_create, $admin_queue_clear, $admin_queue_deliverDelayed, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index f1d8478a4..ba0e069ae 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -64,6 +64,7 @@ import * as ep___admin_getTableStats from './endpoints/admin/get-table-stats.js' import * as ep___admin_getUserIps from './endpoints/admin/get-user-ips.js'; import * as ep___admin_invite_create from './endpoints/admin/invite/create.js'; import * as ep___admin_invite_list from './endpoints/admin/invite/list.js'; +import * as ep___admin_normalization from './endpoints/admin/normalization.js'; import * as ep___admin_promo_create from './endpoints/admin/promo/create.js'; import * as ep___admin_queue_clear from './endpoints/admin/queue/clear.js'; import * as ep___admin_queue_deliverDelayed from './endpoints/admin/queue/deliver-delayed.js'; @@ -460,6 +461,7 @@ const eps = [ ['admin/get-user-ips', ep___admin_getUserIps], ['admin/invite/create', ep___admin_invite_create], ['admin/invite/list', ep___admin_invite_list], + ['admin/normalization', ep___admin_normalization], ['admin/promo/create', ep___admin_promo_create], ['admin/queue/clear', ep___admin_queue_clear], ['admin/queue/deliver-delayed', ep___admin_queue_deliverDelayed], diff --git a/packages/backend/src/server/api/endpoints/admin/normalization.ts b/packages/backend/src/server/api/endpoints/admin/normalization.ts new file mode 100644 index 000000000..4a313544d --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/normalization.ts @@ -0,0 +1,139 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { ApiError } from '@/server/api/error.js'; +import type { AbuseUserReportsRepository, FollowingsRepository, UsersRepository } from '@/models/_.js'; +import type { MiUser, MiLocalUser } from '@/models/User.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; +import { UserSuspendService } from '@/core/UserSuspendService.js'; +import { RoleService } from '@/core/RoleService.js'; +import { QueueService } from '@/core/QueueService.js'; +import { DeleteAccountService } from '@/core/DeleteAccountService.js'; +import { InstanceActorService } from '@/core/InstanceActorService.js'; +import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireModerator: true, + kind: 'write:admin:suspend-user', + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '7cc4f851-e2f1-4621-9633-ec9e1d00c01e', + }, + noModerator: { + message: 'Can\'t normalize user with moderator permission.', + code: 'NO_MODERATOR_NORMALIZATION', + id: '5b68a1d3-8ee3-4862-8294-6c7d2d2edd63', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + }, + required: ['userId'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.followingsRepository) + private followingsRepository: FollowingsRepository, + + @Inject(DI.abuseUserReportsRepository) + private abuseUserReportsRepository: AbuseUserReportsRepository, + + private userSuspendService: UserSuspendService, + private roleService: RoleService, + private moderationLogService: ModerationLogService, + private queueService: QueueService, + private deleteAccountService: DeleteAccountService, + private instanceActorService: InstanceActorService, + private apRendererService: ApRendererService, + ) { + super(meta, paramDef, async (ps, me) => { + const user = await this.usersRepository.findOneBy({ id: ps.userId }); + + if (user == null) { + throw new ApiError(meta.errors.noSuchUser); + } + + if (await this.roleService.isModerator(user)) { + throw new ApiError(meta.errors.noModerator); + } + + await this.usersRepository.update(user.id, { + isSuspended: true, + }); + + await this.moderationLogService.log(me, 'normalize', { + userId: user.id, + userUsername: user.username, + userHost: user.host, + }); + + await this.resolveAllReports(user, me).catch(e => {}); + await this.userSuspendService.doPostSuspend(user).catch(e => {}); + await this.unFollowAll(user).catch(e => {}); + await this.deleteAccountService.deleteAccount(user, true, me); + }); + } + + @bindThis + private async resolveAllReports(user: MiUser, me: MiLocalUser) { + const reports = await this.abuseUserReportsRepository.findBy({ targetUserId: user.id }); + + for (const report of reports) { + if (report.targetUserHost != null) { + const actor = await this.instanceActorService.getInstanceActor(); + const targetUser = await this.usersRepository.findOneByOrFail({ id: report.targetUserId }); + + this.queueService.deliver(actor, this.apRendererService.addContext(this.apRendererService.renderFlag(actor, targetUser.uri!, report.comment)), targetUser.inbox, false); + } + + await this.abuseUserReportsRepository.update(report.id, { + resolved: true, + assigneeId: me.id, + forwarded: user.host !== null, + }); + } + } + + @bindThis + private async unFollowAll(user: MiUser) { + const followings = await this.followingsRepository.findBy({ + followerId: user.id, + }); + const followers = await this.followingsRepository.findBy({ + followeeId: user.id, + }); + + const followingPairs = await Promise.all(followings.map(f => Promise.all([ + this.usersRepository.findOneByOrFail({ id: f.followerId }), + this.usersRepository.findOneByOrFail({ id: f.followeeId }), + ]).then(([from, to]) => [{ id: from.id }, { id: to.id }]))); + const followerPairs = await Promise.all(followers.map(f => Promise.all([ + this.usersRepository.findOneByOrFail({ id: f.followerId }), + this.usersRepository.findOneByOrFail({ id: f.followeeId }), + ]).then(([from, to]) => [{ id: from.id }, { id: to.id }]))); + + await this.queueService.createUnfollowJob(followingPairs.map(p => ({ from: p[0], to: p[1], silent: true }))); + await this.queueService.createUnfollowJob(followerPairs.map(p => ({ from: p[0], to: p[1], silent: true }))); + } +} diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 9e595e83e..fcf9e9ce8 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -99,6 +99,7 @@ export const moderationLogTypes = [ 'unsetUserAvatar', 'unsetUserBanner', 'unsetUserMutualLink', + 'normalize', ] as const; export type ModerationLogPayloads = { @@ -333,7 +334,12 @@ export type ModerationLogPayloads = { userId: string; userUsername: string; userMutualLinkSections: { name: string | null; mutualLinks: { id: string; url: string; fileId: string; description: string | null; imgSrc: string; }[]; }[] | [] - } + }; + normalize: { + userId: string; + userUsername: string; + userHost: string | null; + }; }; export type Serialized = { diff --git a/packages/frontend/src/pages/settings/laboratory.vue b/packages/frontend/src/pages/settings/laboratory.vue index d297c658c..f459992bc 100644 --- a/packages/frontend/src/pages/settings/laboratory.vue +++ b/packages/frontend/src/pages/settings/laboratory.vue @@ -34,12 +34,12 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.save }} - + - + @@ -131,6 +131,21 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + +
+ + {{ i18n.ts.thisIsExperimentalFeature }} + + + + {{ i18n.ts.useNormalization }} + +
+
@@ -141,6 +156,8 @@ import MkSwitch from '@/components/MkSwitch.vue'; import FormSection from '@/components/form/section.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkInfo from '@/components/MkInfo.vue'; +import MkButton from '@/components/MkButton.vue'; +import MkInput from '@/components/MkInput.vue'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { iAmModerator, signinRequired } from '@/account.js'; @@ -148,11 +165,9 @@ import { definePageMetadata } from '@/scripts/page-metadata.js'; import { defaultStore } from '@/store.js'; import * as os from '@/os.js'; import { unisonReload } from '@/scripts/unison-reload.js'; -import MkButton from "@/components/MkButton.vue"; -import MkInput from "@/components/MkInput.vue"; const $i = signinRequired(); -const isVacation = ref($i.isVacation !== null ? $i.isVacation : undefined); +const isVacation = ref($i.isVacation ?? false); const autoRemoval = ref($i.autoRemovalCondition.active); const deleteAfter = ref($i.autoRemovalCondition.deleteAfter || 7); const noPiningNotes = ref($i.autoRemovalCondition.noPiningNotes); @@ -166,6 +181,7 @@ const hideDirectMessages = computed(defaultStore.makeGetterSetter('hideDirectMes const hideDriveFileList = computed(defaultStore.makeGetterSetter('hideDriveFileList')); const hideModerationLog = computed(defaultStore.makeGetterSetter('hideModerationLog')); const hideRoleList = computed(defaultStore.makeGetterSetter('hideRoleList')); +const mapleDirectorMode = computed(defaultStore.makeGetterSetter('mapleDirectorMode')); function saveRemovalCondition() { misskeyApi('i/update-removal-condition', { @@ -204,6 +220,7 @@ watch([ hideDriveFileList, hideRoleList, hideModerationLog, + mapleDirectorMode, ], async () => { await reloadAsk(); }); diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts index 410c8a6a6..fda399712 100644 --- a/packages/frontend/src/scripts/get-user-menu.ts +++ b/packages/frontend/src/scripts/get-user-menu.ts @@ -116,7 +116,15 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter } async function userInfoUpdate() { - os.apiWithDialog('federation/update-remote-user', { + await os.apiWithDialog('federation/update-remote-user', { + userId: user.id, + }); + } + + async function immediateUserNormalization() { + if (!await getConfirmed(i18n.ts.normalizeConfirm)) return; + + await os.apiWithDialog('admin/normalization', { userId: user.id, }); } @@ -344,6 +352,14 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter }]); } + if ($i && iAmModerator && defaultStore.state.mapleDirectorMode) { + menu = menu.concat([{ type: 'divider' }, { + icon: 'ti ti-aperture', + text: i18n.ts.normalize, + action: immediateUserNormalization, + }]); + } + if (user.host !== null) { menu = menu.concat([{ type: 'divider' }, { icon: 'ti ti-refresh', diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 930cfbc8b..cb299ed50 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -216,7 +216,7 @@ export const defaultStore = markRaw(new Storage('base', { filter: { withReplies: true, withRenotes: true, - withSensitive: true, + withSensitive: false, onlyFiles: false, }, }, @@ -252,7 +252,7 @@ export const defaultStore = markRaw(new Storage('base', { }, animatedMfm: { where: 'device', - default: true, + default: !window.matchMedia('(prefers-reduced-motion)').matches, }, advancedMfm: { where: 'device', @@ -288,7 +288,7 @@ export const defaultStore = markRaw(new Storage('base', { }, emojiStyle: { where: 'device', - default: 'twemoji', // twemoji / fluentEmoji / native + default: 'fluentEmoji', // twemoji / fluentEmoji / native }, disableDrawer: { where: 'device', @@ -436,7 +436,7 @@ export const defaultStore = markRaw(new Storage('base', { }, enableCondensedLineForAcct: { where: 'device', - default: false, + default: true, }, additionalUnicodeEmojiIndexes: { where: 'device', @@ -448,7 +448,7 @@ export const defaultStore = markRaw(new Storage('base', { }, hideMutedNotes: { where: 'device', - default: false, + default: true, }, defaultWithReplies: { where: 'account', @@ -473,7 +473,7 @@ export const defaultStore = markRaw(new Storage('base', { }, enableSeasonalScreenEffect: { where: 'device', - default: false, + default: true, }, dropAndFusion: { where: 'device', @@ -488,7 +488,7 @@ export const defaultStore = markRaw(new Storage('base', { }, enableHorizontalSwipe: { where: 'device', - default: true, + default: false, }, trustedExternalWebsites: { where: 'device', @@ -504,7 +504,11 @@ export const defaultStore = markRaw(new Storage('base', { }, alwaysConfirmFollow: { where: 'device', - default: true, + default: false, + }, + mapleDirectorMode: { + where: 'deviceAccount', + default: false, }, sound_masterVolume: { diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 2e2bac0d4..5a36bfc54 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -253,6 +253,9 @@ type AdminInviteListResponse = operations['admin___invite___list']['responses'][ // @public (undocumented) type AdminMetaResponse = operations['admin___meta']['responses']['200']['content']['application/json']; +// @public (undocumented) +type AdminNormalizationRequest = operations['admin___normalization']['requestBody']['content']['application/json']; + // @public (undocumented) type AdminPromoCreateRequest = operations['admin___promo___create']['requestBody']['content']['application/json']; @@ -1291,6 +1294,7 @@ declare namespace entities { AdminInviteCreateResponse, AdminInviteListRequest, AdminInviteListResponse, + AdminNormalizationRequest, AdminPromoCreateRequest, AdminQueueDeliverDelayedResponse, AdminQueueInboxDelayedResponse, diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts index 76b539b4e..0dadb2215 100644 --- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts +++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts @@ -644,6 +644,17 @@ declare module '../api.js' { credential?: string | null, ): Promise>; + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:suspend-user* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + /** * No description provided. * diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts index d5cebbdb6..f6cdef76e 100644 --- a/packages/misskey-js/src/autogen/endpoint.ts +++ b/packages/misskey-js/src/autogen/endpoint.ts @@ -78,6 +78,7 @@ import type { AdminInviteCreateResponse, AdminInviteListRequest, AdminInviteListResponse, + AdminNormalizationRequest, AdminPromoCreateRequest, AdminQueueDeliverDelayedResponse, AdminQueueInboxDelayedResponse, @@ -649,6 +650,7 @@ export type Endpoints = { 'admin/get-user-ips': { req: AdminGetUserIpsRequest; res: AdminGetUserIpsResponse }; 'admin/invite/create': { req: AdminInviteCreateRequest; res: AdminInviteCreateResponse }; 'admin/invite/list': { req: AdminInviteListRequest; res: AdminInviteListResponse }; + 'admin/normalization': { req: AdminNormalizationRequest; res: EmptyResponse }; 'admin/promo/create': { req: AdminPromoCreateRequest; res: EmptyResponse }; 'admin/queue/clear': { req: EmptyRequest; res: EmptyResponse }; 'admin/queue/deliver-delayed': { req: EmptyRequest; res: AdminQueueDeliverDelayedResponse }; diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts index 13b4cc37e..b4fb12e12 100644 --- a/packages/misskey-js/src/autogen/entities.ts +++ b/packages/misskey-js/src/autogen/entities.ts @@ -81,6 +81,7 @@ export type AdminInviteCreateRequest = operations['admin___invite___create']['re export type AdminInviteCreateResponse = operations['admin___invite___create']['responses']['200']['content']['application/json']; export type AdminInviteListRequest = operations['admin___invite___list']['requestBody']['content']['application/json']; export type AdminInviteListResponse = operations['admin___invite___list']['responses']['200']['content']['application/json']; +export type AdminNormalizationRequest = operations['admin___normalization']['requestBody']['content']['application/json']; export type AdminPromoCreateRequest = operations['admin___promo___create']['requestBody']['content']['application/json']; export type AdminQueueDeliverDelayedResponse = operations['admin___queue___deliver-delayed']['responses']['200']['content']['application/json']; export type AdminQueueInboxDelayedResponse = operations['admin___queue___inbox-delayed']['responses']['200']['content']['application/json']; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 243d989b9..08855f360 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -537,6 +537,15 @@ export type paths = { */ post: operations['admin___invite___list']; }; + '/admin/normalization': { + /** + * admin/normalization + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:suspend-user* + */ + post: operations['admin___normalization']; + }; '/admin/promo/create': { /** * admin/promo/create @@ -8828,6 +8837,58 @@ export type operations = { }; }; }; + /** + * admin/normalization + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:admin:suspend-user* + */ + admin___normalization: { + 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/promo/create * @description No description provided.