feat: AIによるNSFW検出を無視できるポリシーを追加 (MisskeyIO#500)
* feat: AIによるNSFW検出を無視できるポリシーを追加 * refactor: skipNsfwCheckの条件を同じようにまとめた
This commit is contained in:
		
							parent
							
								
									c0dbdd78c1
								
							
						
					
					
						commit
						d624547874
					
				|  | @ -1698,6 +1698,7 @@ _role: | |||
|     canManageAvatarDecorations: "Manage avatar decorations" | ||||
|     driveCapacity: "Drive capacity" | ||||
|     alwaysMarkNsfw: "Always mark files as NSFW" | ||||
|     skipNsfwDetection: "Skip NSFW detection by AI" | ||||
|     pinMax: "Maximum number of pinned notes" | ||||
|     antennaMax: "Maximum number of antennas" | ||||
|     antennaNotesMax: "Maximum number of notes stored in antennas" | ||||
|  |  | |||
|  | @ -6622,6 +6622,10 @@ export interface Locale extends ILocale { | |||
|              * ファイルにNSFWを常に付与 | ||||
|              */ | ||||
|             "alwaysMarkNsfw": string; | ||||
|             /** | ||||
|              * AIによるNSFW検出を無視 | ||||
|              */ | ||||
|             "skipNsfwDetection": string; | ||||
|             /** | ||||
|              * ノートのピン留めの最大数 | ||||
|              */ | ||||
|  |  | |||
|  | @ -1713,6 +1713,7 @@ _role: | |||
|     canManageAvatarDecorations: "アバターデコレーションの管理" | ||||
|     driveCapacity: "ドライブ容量" | ||||
|     alwaysMarkNsfw: "ファイルにNSFWを常に付与" | ||||
|     skipNsfwDetection: "AIによるNSFW検出を無視" | ||||
|     pinMax: "ノートのピン留めの最大数" | ||||
|     antennaMax: "アンテナの作成可能数" | ||||
|     antennaNotesMax: "アンテナに保持する最大ノート数" | ||||
|  |  | |||
|  | @ -459,15 +459,14 @@ export class DriveService { | |||
| 	}: AddFileArgs): Promise<MiDriveFile> { | ||||
| 		let skipNsfwCheck = false; | ||||
| 		const instance = await this.metaService.fetch(); | ||||
| 		const userRoleNSFW = user && (await this.roleService.getUserPolicies(user.id)).alwaysMarkNsfw; | ||||
| 		if (user == null) { | ||||
| 			skipNsfwCheck = true; | ||||
| 		} else if (userRoleNSFW) { | ||||
| 			skipNsfwCheck = true; | ||||
| 		} | ||||
| 		if (instance.sensitiveMediaDetection === 'none') skipNsfwCheck = true; | ||||
| 		if (user && instance.sensitiveMediaDetection === 'local' && this.userEntityService.isRemoteUser(user)) skipNsfwCheck = true; | ||||
| 		if (user && instance.sensitiveMediaDetection === 'remote' && this.userEntityService.isLocalUser(user)) skipNsfwCheck = true; | ||||
| 		const policies = user && await this.roleService.getUserPolicies(user.id); | ||||
| 		const userRoleNSFW = policies?.alwaysMarkNsfw; | ||||
| 		skipNsfwCheck ||= user == null; | ||||
| 		skipNsfwCheck ||= !!userRoleNSFW; | ||||
| 		skipNsfwCheck ||= !!policies?.skipNsfwDetection; | ||||
| 		skipNsfwCheck ||= instance.sensitiveMediaDetection === 'none'; | ||||
| 		skipNsfwCheck ||= !!(user && instance.sensitiveMediaDetection === 'local' && this.userEntityService.isRemoteUser(user)); | ||||
| 		skipNsfwCheck ||= !!(user && instance.sensitiveMediaDetection === 'remote' && this.userEntityService.isLocalUser(user)); | ||||
| 
 | ||||
| 		const info = await this.fileInfoService.getFileInfo(path, { | ||||
| 			skipSensitiveDetection: skipNsfwCheck, | ||||
|  | @ -511,11 +510,10 @@ export class DriveService { | |||
| 		this.registerLogger.debug(`ADD DRIVE FILE: user ${user?.id ?? 'not set'}, name ${detectedName}, tmp ${path}`); | ||||
| 
 | ||||
| 		//#region Check drive usage
 | ||||
| 		if (user && !isLink) { | ||||
| 		if (user && policies && !isLink) { | ||||
| 			const usage = await this.driveFileEntityService.calcDriveUsageOf(user); | ||||
| 			const isLocalUser = this.userEntityService.isLocalUser(user); | ||||
| 
 | ||||
| 			const policies = await this.roleService.getUserPolicies(user.id); | ||||
| 			const driveCapacity = 1024 * 1024 * policies.driveCapacityMb; | ||||
| 			this.registerLogger.debug('drive capacity override applied'); | ||||
| 			this.registerLogger.debug(`overrideCap: ${driveCapacity}bytes, usage: ${usage}bytes, u+s: ${usage + info.size}bytes`); | ||||
|  |  | |||
|  | @ -55,6 +55,7 @@ export type RolePolicies = { | |||
| 	canHideAds: boolean; | ||||
| 	driveCapacityMb: number; | ||||
| 	alwaysMarkNsfw: boolean; | ||||
| 	skipNsfwDetection: boolean; | ||||
| 	pinLimit: number; | ||||
| 	antennaLimit: number; | ||||
| 	antennaNotesLimit: number; | ||||
|  | @ -91,6 +92,7 @@ export const DEFAULT_POLICIES: RolePolicies = { | |||
| 	canHideAds: false, | ||||
| 	driveCapacityMb: 100, | ||||
| 	alwaysMarkNsfw: false, | ||||
| 	skipNsfwDetection: false, | ||||
| 	pinLimit: 5, | ||||
| 	antennaLimit: 5, | ||||
| 	antennaNotesLimit: 200, | ||||
|  | @ -366,6 +368,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { | |||
| 			canHideAds: calc('canHideAds', vs => vs.some(v => v === true)), | ||||
| 			driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)), | ||||
| 			alwaysMarkNsfw: calc('alwaysMarkNsfw', vs => vs.some(v => v === true)), | ||||
| 			skipNsfwDetection: calc('skipNsfwDetection', vs => vs.some(v => v === true)), | ||||
| 			pinLimit: calc('pinLimit', vs => Math.max(...vs)), | ||||
| 			antennaLimit: calc('antennaLimit', vs => Math.max(...vs)), | ||||
| 			antennaNotesLimit: calc('antennaNotesLimit', vs => Math.max(...vs)), | ||||
|  |  | |||
|  | @ -239,6 +239,10 @@ export const packedRolePoliciesSchema = { | |||
| 			type: 'boolean', | ||||
| 			optional: false, nullable: false, | ||||
| 		}, | ||||
| 		skipNsfwDetection: { | ||||
| 			type: 'boolean', | ||||
| 			optional: false, nullable: false, | ||||
| 		}, | ||||
| 		pinLimit: { | ||||
| 			type: 'integer', | ||||
| 			optional: false, nullable: false, | ||||
|  |  | |||
|  | @ -94,6 +94,7 @@ export const ROLE_POLICIES = [ | |||
| 	'canHideAds', | ||||
| 	'driveCapacityMb', | ||||
| 	'alwaysMarkNsfw', | ||||
| 	'skipNsfwDetection', | ||||
| 	'pinLimit', | ||||
| 	'antennaLimit', | ||||
| 	'antennaNotesLimit', | ||||
|  |  | |||
|  | @ -518,6 +518,26 @@ SPDX-License-Identifier: AGPL-3.0-only | |||
| 				</div> | ||||
| 			</MkFolder> | ||||
| 
 | ||||
| 			<MkFolder v-if="matchQuery([i18n.ts._role._options.skipNsfwDetection, 'skipNsfwDetection'])"> | ||||
| 				<template #label>{{ i18n.ts._role._options.skipNsfwDetection }}</template> | ||||
| 				<template #suffix> | ||||
| 					<span v-if="role.policies.skipNsfwDetection.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> | ||||
| 					<span v-else>{{ role.policies.skipNsfwDetection.value ? i18n.ts.yes : i18n.ts.no }}</span> | ||||
| 					<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.skipNsfwDetection)"></i></span> | ||||
| 				</template> | ||||
| 				<div class="_gaps"> | ||||
| 					<MkSwitch v-model="role.policies.skipNsfwDetection.useDefault" :readonly="readonly"> | ||||
| 						<template #label>{{ i18n.ts._role.useBaseValue }}</template> | ||||
| 					</MkSwitch> | ||||
| 					<MkSwitch v-model="role.policies.skipNsfwDetection.value" :disabled="role.policies.skipNsfwDetection.useDefault" :readonly="readonly"> | ||||
| 						<template #label>{{ i18n.ts.enable }}</template> | ||||
| 					</MkSwitch> | ||||
| 					<MkRange v-model="role.policies.skipNsfwDetection.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> | ||||
| 						<template #label>{{ i18n.ts._role.priority }}</template> | ||||
| 					</MkRange> | ||||
| 				</div> | ||||
| 			</MkFolder> | ||||
| 
 | ||||
| 			<MkFolder v-if="matchQuery([i18n.ts._role._options.pinMax, 'pinLimit'])"> | ||||
| 				<template #label>{{ i18n.ts._role._options.pinMax }}</template> | ||||
| 				<template #suffix> | ||||
|  |  | |||
|  | @ -190,6 +190,14 @@ SPDX-License-Identifier: AGPL-3.0-only | |||
| 							</MkSwitch> | ||||
| 						</MkFolder> | ||||
| 
 | ||||
| 						<MkFolder v-if="matchQuery([i18n.ts._role._options.skipNsfwDetection, 'skipNsfwDetection'])"> | ||||
| 							<template #label>{{ i18n.ts._role._options.skipNsfwDetection }}</template> | ||||
| 							<template #suffix>{{ policies.skipNsfwDetection ? i18n.ts.yes : i18n.ts.no }}</template> | ||||
| 							<MkSwitch v-model="policies.skipNsfwDetection"> | ||||
| 								<template #label>{{ i18n.ts.enable }}</template> | ||||
| 							</MkSwitch> | ||||
| 						</MkFolder> | ||||
| 
 | ||||
| 						<MkFolder v-if="matchQuery([i18n.ts._role._options.pinMax, 'pinLimit'])"> | ||||
| 							<template #label>{{ i18n.ts._role._options.pinMax }}</template> | ||||
| 							<template #suffix>{{ policies.pinLimit }}</template> | ||||
|  |  | |||
|  | @ -4822,6 +4822,7 @@ export type components = { | |||
|       canHideAds: boolean; | ||||
|       driveCapacityMb: number; | ||||
|       alwaysMarkNsfw: boolean; | ||||
|       skipNsfwDetection: boolean; | ||||
|       pinLimit: number; | ||||
|       antennaLimit: number; | ||||
|       antennaNotesLimit: number; | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue