improve moderation log
This commit is contained in:
parent
ed983a5baf
commit
8e5a90589d
|
@ -2256,6 +2256,8 @@ export interface Locale {
|
||||||
"suspend": string;
|
"suspend": string;
|
||||||
"unsuspend": string;
|
"unsuspend": string;
|
||||||
"addCustomEmoji": string;
|
"addCustomEmoji": string;
|
||||||
|
"updateCustomEmoji": string;
|
||||||
|
"deleteCustomEmoji": string;
|
||||||
"updateServerSettings": string;
|
"updateServerSettings": string;
|
||||||
"updateUserNote": string;
|
"updateUserNote": string;
|
||||||
"deleteDriveFile": string;
|
"deleteDriveFile": string;
|
||||||
|
|
|
@ -2169,6 +2169,8 @@ _moderationLogTypes:
|
||||||
suspend: "凍結"
|
suspend: "凍結"
|
||||||
unsuspend: "凍結解除"
|
unsuspend: "凍結解除"
|
||||||
addCustomEmoji: "カスタム絵文字追加"
|
addCustomEmoji: "カスタム絵文字追加"
|
||||||
|
updateCustomEmoji: "カスタム絵文字更新"
|
||||||
|
deleteCustomEmoji: "カスタム絵文字削除"
|
||||||
updateServerSettings: "サーバー設定更新"
|
updateServerSettings: "サーバー設定更新"
|
||||||
updateUserNote: "モデレーションノート更新"
|
updateUserNote: "モデレーションノート更新"
|
||||||
deleteDriveFile: "ファイルを削除"
|
deleteDriveFile: "ファイルを削除"
|
||||||
|
|
|
@ -12,12 +12,13 @@ import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import type { MiDriveFile } from '@/models/DriveFile.js';
|
import type { MiDriveFile } from '@/models/DriveFile.js';
|
||||||
import type { MiEmoji } from '@/models/Emoji.js';
|
import type { MiEmoji } from '@/models/Emoji.js';
|
||||||
import type { EmojisRepository, MiRole } from '@/models/_.js';
|
import type { EmojisRepository, MiRole, MiUser } from '@/models/_.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js';
|
import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js';
|
||||||
import { UtilityService } from '@/core/UtilityService.js';
|
import { UtilityService } from '@/core/UtilityService.js';
|
||||||
import { query } from '@/misc/prelude/url.js';
|
import { query } from '@/misc/prelude/url.js';
|
||||||
import type { Serialized } from '@/server/api/stream/types.js';
|
import type { Serialized } from '@/server/api/stream/types.js';
|
||||||
|
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||||
|
|
||||||
const parseEmojiStrRegexp = /^(\w+)(?:@([\w.-]+))?$/;
|
const parseEmojiStrRegexp = /^(\w+)(?:@([\w.-]+))?$/;
|
||||||
|
|
||||||
|
@ -36,6 +37,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
private utilityService: UtilityService,
|
private utilityService: UtilityService,
|
||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
private emojiEntityService: EmojiEntityService,
|
private emojiEntityService: EmojiEntityService,
|
||||||
|
private moderationLogService: ModerationLogService,
|
||||||
private globalEventService: GlobalEventService,
|
private globalEventService: GlobalEventService,
|
||||||
) {
|
) {
|
||||||
this.cache = new MemoryKVCache<MiEmoji | null>(1000 * 60 * 60 * 12);
|
this.cache = new MemoryKVCache<MiEmoji | null>(1000 * 60 * 60 * 12);
|
||||||
|
@ -66,7 +68,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
isSensitive: boolean;
|
isSensitive: boolean;
|
||||||
localOnly: boolean;
|
localOnly: boolean;
|
||||||
roleIdsThatCanBeUsedThisEmojiAsReaction: MiRole['id'][];
|
roleIdsThatCanBeUsedThisEmojiAsReaction: MiRole['id'][];
|
||||||
}): Promise<MiEmoji> {
|
}, moderator?: MiUser): Promise<MiEmoji> {
|
||||||
const emoji = await this.emojisRepository.insert({
|
const emoji = await this.emojisRepository.insert({
|
||||||
id: this.idService.genId(),
|
id: this.idService.genId(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
|
@ -89,6 +91,13 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
this.globalEventService.publishBroadcastStream('emojiAdded', {
|
this.globalEventService.publishBroadcastStream('emojiAdded', {
|
||||||
emoji: await this.emojiEntityService.packDetailed(emoji.id),
|
emoji: await this.emojiEntityService.packDetailed(emoji.id),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (moderator) {
|
||||||
|
this.moderationLogService.log(moderator, 'addCustomEmoji', {
|
||||||
|
emojiId: emoji.id,
|
||||||
|
emoji: emoji,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return emoji;
|
return emoji;
|
||||||
|
@ -104,7 +113,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
isSensitive?: boolean;
|
isSensitive?: boolean;
|
||||||
localOnly?: boolean;
|
localOnly?: boolean;
|
||||||
roleIdsThatCanBeUsedThisEmojiAsReaction?: MiRole['id'][];
|
roleIdsThatCanBeUsedThisEmojiAsReaction?: MiRole['id'][];
|
||||||
}): Promise<void> {
|
}, moderator?: MiUser): Promise<void> {
|
||||||
const emoji = await this.emojisRepository.findOneByOrFail({ id: id });
|
const emoji = await this.emojisRepository.findOneByOrFail({ id: id });
|
||||||
const sameNameEmoji = await this.emojisRepository.findOneBy({ name: data.name, host: IsNull() });
|
const sameNameEmoji = await this.emojisRepository.findOneBy({ name: data.name, host: IsNull() });
|
||||||
if (sameNameEmoji != null && sameNameEmoji.id !== id) throw new Error('name already exists');
|
if (sameNameEmoji != null && sameNameEmoji.id !== id) throw new Error('name already exists');
|
||||||
|
@ -140,6 +149,14 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
emoji: updated,
|
emoji: updated,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (moderator) {
|
||||||
|
this.moderationLogService.log(moderator, 'updateCustomEmoji', {
|
||||||
|
emojiId: emoji.id,
|
||||||
|
before: emoji,
|
||||||
|
after: updated,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
@ -231,7 +248,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async delete(id: MiEmoji['id']) {
|
public async delete(id: MiEmoji['id'], moderator?: MiUser) {
|
||||||
const emoji = await this.emojisRepository.findOneByOrFail({ id: id });
|
const emoji = await this.emojisRepository.findOneByOrFail({ id: id });
|
||||||
|
|
||||||
await this.emojisRepository.delete(emoji.id);
|
await this.emojisRepository.delete(emoji.id);
|
||||||
|
@ -241,16 +258,30 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
||||||
this.globalEventService.publishBroadcastStream('emojiDeleted', {
|
this.globalEventService.publishBroadcastStream('emojiDeleted', {
|
||||||
emojis: [await this.emojiEntityService.packDetailed(emoji)],
|
emojis: [await this.emojiEntityService.packDetailed(emoji)],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (moderator) {
|
||||||
|
this.moderationLogService.log(moderator, 'deleteCustomEmoji', {
|
||||||
|
emojiId: emoji.id,
|
||||||
|
emoji: emoji,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async deleteBulk(ids: MiEmoji['id'][]) {
|
public async deleteBulk(ids: MiEmoji['id'][], moderator?: MiUser) {
|
||||||
const emojis = await this.emojisRepository.findBy({
|
const emojis = await this.emojisRepository.findBy({
|
||||||
id: In(ids),
|
id: In(ids),
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const emoji of emojis) {
|
for (const emoji of emojis) {
|
||||||
await this.emojisRepository.delete(emoji.id);
|
await this.emojisRepository.delete(emoji.id);
|
||||||
|
|
||||||
|
if (moderator) {
|
||||||
|
this.moderationLogService.log(moderator, 'deleteCustomEmoji', {
|
||||||
|
emojiId: emoji.id,
|
||||||
|
emoji: emoji,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.localEmojisCache.refresh();
|
this.localEmojisCache.refresh();
|
||||||
|
|
|
@ -8,7 +8,6 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import type { DriveFilesRepository } from '@/models/_.js';
|
import type { DriveFilesRepository } from '@/models/_.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
||||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
|
||||||
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
|
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
|
||||||
import { ApiError } from '../../../error.js';
|
import { ApiError } from '../../../error.js';
|
||||||
|
|
||||||
|
@ -61,7 +60,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
private customEmojiService: CustomEmojiService,
|
private customEmojiService: CustomEmojiService,
|
||||||
|
|
||||||
private emojiEntityService: EmojiEntityService,
|
private emojiEntityService: EmojiEntityService,
|
||||||
private moderationLogService: ModerationLogService,
|
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
|
const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
|
||||||
|
@ -77,11 +75,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
isSensitive: ps.isSensitive ?? false,
|
isSensitive: ps.isSensitive ?? false,
|
||||||
localOnly: ps.localOnly ?? false,
|
localOnly: ps.localOnly ?? false,
|
||||||
roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [],
|
roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [],
|
||||||
});
|
}, me);
|
||||||
|
|
||||||
this.moderationLogService.log(me, 'addCustomEmoji', {
|
|
||||||
emojiId: emoji.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.emojiEntityService.packDetailed(emoji);
|
return this.emojiEntityService.packDetailed(emoji);
|
||||||
});
|
});
|
||||||
|
|
|
@ -30,7 +30,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
private customEmojiService: CustomEmojiService,
|
private customEmojiService: CustomEmojiService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
await this.customEmojiService.deleteBulk(ps.ids);
|
await this.customEmojiService.deleteBulk(ps.ids, me);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
private customEmojiService: CustomEmojiService,
|
private customEmojiService: CustomEmojiService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
await this.customEmojiService.delete(ps.id);
|
await this.customEmojiService.delete(ps.id, me);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
isSensitive: ps.isSensitive,
|
isSensitive: ps.isSensitive,
|
||||||
localOnly: ps.localOnly,
|
localOnly: ps.localOnly,
|
||||||
roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction,
|
roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction,
|
||||||
});
|
}, me);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,8 @@ export const moderationLogTypes = [
|
||||||
'unsuspend',
|
'unsuspend',
|
||||||
'updateUserNote',
|
'updateUserNote',
|
||||||
'addCustomEmoji',
|
'addCustomEmoji',
|
||||||
|
'updateCustomEmoji',
|
||||||
|
'deleteCustomEmoji',
|
||||||
'assignRole',
|
'assignRole',
|
||||||
'unassignRole',
|
'unassignRole',
|
||||||
'updateRole',
|
'updateRole',
|
||||||
|
@ -70,6 +72,16 @@ export type ModerationLogPayloads = {
|
||||||
};
|
};
|
||||||
addCustomEmoji: {
|
addCustomEmoji: {
|
||||||
emojiId: string;
|
emojiId: string;
|
||||||
|
emoji: any;
|
||||||
|
};
|
||||||
|
updateCustomEmoji: {
|
||||||
|
emojiId: string;
|
||||||
|
before: any;
|
||||||
|
after: any;
|
||||||
|
};
|
||||||
|
deleteCustomEmoji: {
|
||||||
|
emojiId: string;
|
||||||
|
emoji: any;
|
||||||
};
|
};
|
||||||
assignRole: {
|
assignRole: {
|
||||||
userId: string;
|
userId: string;
|
||||||
|
|
|
@ -2538,6 +2538,12 @@ type ModerationLog = {
|
||||||
} | {
|
} | {
|
||||||
type: 'addCustomEmoji';
|
type: 'addCustomEmoji';
|
||||||
info: ModerationLogPayloads['addCustomEmoji'];
|
info: ModerationLogPayloads['addCustomEmoji'];
|
||||||
|
} | {
|
||||||
|
type: 'updateCustomEmoji';
|
||||||
|
info: ModerationLogPayloads['updateCustomEmoji'];
|
||||||
|
} | {
|
||||||
|
type: 'deleteCustomEmoji';
|
||||||
|
info: ModerationLogPayloads['deleteCustomEmoji'];
|
||||||
} | {
|
} | {
|
||||||
type: 'assignRole';
|
type: 'assignRole';
|
||||||
info: ModerationLogPayloads['assignRole'];
|
info: ModerationLogPayloads['assignRole'];
|
||||||
|
@ -2592,7 +2598,7 @@ type ModerationLog = {
|
||||||
});
|
});
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "assignRole", "unassignRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance"];
|
export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance"];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"];
|
export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"];
|
||||||
|
|
|
@ -51,6 +51,8 @@ export const moderationLogTypes = [
|
||||||
'unsuspend',
|
'unsuspend',
|
||||||
'updateUserNote',
|
'updateUserNote',
|
||||||
'addCustomEmoji',
|
'addCustomEmoji',
|
||||||
|
'updateCustomEmoji',
|
||||||
|
'deleteCustomEmoji',
|
||||||
'assignRole',
|
'assignRole',
|
||||||
'unassignRole',
|
'unassignRole',
|
||||||
'updateRole',
|
'updateRole',
|
||||||
|
@ -88,6 +90,16 @@ export type ModerationLogPayloads = {
|
||||||
};
|
};
|
||||||
addCustomEmoji: {
|
addCustomEmoji: {
|
||||||
emojiId: string;
|
emojiId: string;
|
||||||
|
emoji: any;
|
||||||
|
};
|
||||||
|
updateCustomEmoji: {
|
||||||
|
emojiId: string;
|
||||||
|
before: any;
|
||||||
|
after: any;
|
||||||
|
};
|
||||||
|
deleteCustomEmoji: {
|
||||||
|
emojiId: string;
|
||||||
|
emoji: any;
|
||||||
};
|
};
|
||||||
assignRole: {
|
assignRole: {
|
||||||
userId: string;
|
userId: string;
|
||||||
|
|
|
@ -589,6 +589,12 @@ export type ModerationLog = {
|
||||||
} | {
|
} | {
|
||||||
type: 'addCustomEmoji';
|
type: 'addCustomEmoji';
|
||||||
info: ModerationLogPayloads['addCustomEmoji'];
|
info: ModerationLogPayloads['addCustomEmoji'];
|
||||||
|
} | {
|
||||||
|
type: 'updateCustomEmoji';
|
||||||
|
info: ModerationLogPayloads['updateCustomEmoji'];
|
||||||
|
} | {
|
||||||
|
type: 'deleteCustomEmoji';
|
||||||
|
info: ModerationLogPayloads['deleteCustomEmoji'];
|
||||||
} | {
|
} | {
|
||||||
type: 'assignRole';
|
type: 'assignRole';
|
||||||
info: ModerationLogPayloads['assignRole'];
|
info: ModerationLogPayloads['assignRole'];
|
||||||
|
|
Loading…
Reference in New Issue