parent
							
								
									d2204fd5c8
								
							
						
					
					
						commit
						d1807ee5dc
					
				
					 6 changed files with 48 additions and 2 deletions
				
			
		|  | @ -73,8 +73,9 @@ You should also include the user name that made the change. | ||||||
| - Push notification of Antenna note @tamaina | - Push notification of Antenna note @tamaina | ||||||
| - AVIF support @tamaina | - AVIF support @tamaina | ||||||
| - Add Cloudflare Turnstile CAPTCHA support @CyberRex0 | - Add Cloudflare Turnstile CAPTCHA support @CyberRex0 | ||||||
| - 非モデレーターでも、権限を持つロールをアサインされたユーザーはインスタンスの招待コードを発行できるように | - 非モデレーターでも、権限を持つロールをアサインされたユーザーはインスタンスの招待コードを発行できるように @syuilo | ||||||
| - 非モデレーターでも、権限を持つロールをアサインされたユーザーはカスタム絵文字の追加、編集、削除を行えるように | - 非モデレーターでも、権限を持つロールをアサインされたユーザーはカスタム絵文字の追加、編集、削除を行えるように @syuilo | ||||||
|  | - ハードワードミュートの最大文字数を設定可能に @syuilo | ||||||
| - Server: signToActivityPubGet is set to true by default @syuilo | - Server: signToActivityPubGet is set to true by default @syuilo | ||||||
| - Server: improve syslog performance @syuilo | - Server: improve syslog performance @syuilo | ||||||
| - Server: Use undici instead of node-fetch and got @tamaina | - Server: Use undici instead of node-fetch and got @tamaina | ||||||
|  |  | ||||||
|  | @ -962,6 +962,7 @@ _role: | ||||||
|     canManageCustomEmojis: "カスタム絵文字の管理" |     canManageCustomEmojis: "カスタム絵文字の管理" | ||||||
|     driveCapacity: "ドライブ容量" |     driveCapacity: "ドライブ容量" | ||||||
|     antennaMax: "アンテナの作成可能数" |     antennaMax: "アンテナの作成可能数" | ||||||
|  |     wordMuteMax: "ワードミュートの最大文字数" | ||||||
|   _condition: |   _condition: | ||||||
|     isLocal: "ローカルユーザー" |     isLocal: "ローカルユーザー" | ||||||
|     isRemote: "リモートユーザー" |     isRemote: "リモートユーザー" | ||||||
|  |  | ||||||
|  | @ -20,6 +20,7 @@ export type RoleOptions = { | ||||||
| 	canManageCustomEmojis: boolean; | 	canManageCustomEmojis: boolean; | ||||||
| 	driveCapacityMb: number; | 	driveCapacityMb: number; | ||||||
| 	antennaLimit: number; | 	antennaLimit: number; | ||||||
|  | 	wordMuteLimit: number; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const DEFAULT_ROLE: RoleOptions = { | export const DEFAULT_ROLE: RoleOptions = { | ||||||
|  | @ -30,6 +31,7 @@ export const DEFAULT_ROLE: RoleOptions = { | ||||||
| 	canManageCustomEmojis: false, | 	canManageCustomEmojis: false, | ||||||
| 	driveCapacityMb: 100, | 	driveCapacityMb: 100, | ||||||
| 	antennaLimit: 5, | 	antennaLimit: 5, | ||||||
|  | 	wordMuteLimit: 200, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
|  | @ -187,6 +189,7 @@ export class RoleService implements OnApplicationShutdown { | ||||||
| 			canManageCustomEmojis: getOptionValues('canManageCustomEmojis').some(x => x === true), | 			canManageCustomEmojis: getOptionValues('canManageCustomEmojis').some(x => x === true), | ||||||
| 			driveCapacityMb: Math.max(...getOptionValues('driveCapacityMb')), | 			driveCapacityMb: Math.max(...getOptionValues('driveCapacityMb')), | ||||||
| 			antennaLimit: Math.max(...getOptionValues('antennaLimit')), | 			antennaLimit: Math.max(...getOptionValues('antennaLimit')), | ||||||
|  | 			wordMuteLimit: Math.max(...getOptionValues('wordMuteLimit')), | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ import { UserFollowingService } from '@/core/UserFollowingService.js'; | ||||||
| import { AccountUpdateService } from '@/core/AccountUpdateService.js'; | import { AccountUpdateService } from '@/core/AccountUpdateService.js'; | ||||||
| import { HashtagService } from '@/core/HashtagService.js'; | import { HashtagService } from '@/core/HashtagService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
|  | import { RoleService } from '@/core/RoleService.js'; | ||||||
| import { ApiError } from '../../error.js'; | import { ApiError } from '../../error.js'; | ||||||
| 
 | 
 | ||||||
| export const meta = { | export const meta = { | ||||||
|  | @ -62,6 +63,12 @@ export const meta = { | ||||||
| 			code: 'INVALID_REGEXP', | 			code: 'INVALID_REGEXP', | ||||||
| 			id: '0d786918-10df-41cd-8f33-8dec7d9a89a5', | 			id: '0d786918-10df-41cd-8f33-8dec7d9a89a5', | ||||||
| 		}, | 		}, | ||||||
|  | 
 | ||||||
|  | 		tooManyMutedWords: { | ||||||
|  | 			message: 'Too many muted words.', | ||||||
|  | 			code: 'TOO_MANY_MUTED_WORDS', | ||||||
|  | 			id: '010665b1-a211-42d2-bc64-8f6609d79785', | ||||||
|  | 		}, | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	res: { | 	res: { | ||||||
|  | @ -144,6 +151,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||||
| 		private userFollowingService: UserFollowingService, | 		private userFollowingService: UserFollowingService, | ||||||
| 		private accountUpdateService: AccountUpdateService, | 		private accountUpdateService: AccountUpdateService, | ||||||
| 		private hashtagService: HashtagService, | 		private hashtagService: HashtagService, | ||||||
|  | 		private roleService: RoleService, | ||||||
| 	) { | 	) { | ||||||
| 		super(meta, paramDef, async (ps, _user, token) => { | 		super(meta, paramDef, async (ps, _user, token) => { | ||||||
| 			const user = await this.usersRepository.findOneByOrFail({ id: _user.id }); | 			const user = await this.usersRepository.findOneByOrFail({ id: _user.id }); | ||||||
|  | @ -163,6 +171,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||||
| 			if (ps.avatarId !== undefined) updates.avatarId = ps.avatarId; | 			if (ps.avatarId !== undefined) updates.avatarId = ps.avatarId; | ||||||
| 			if (ps.bannerId !== undefined) updates.bannerId = ps.bannerId; | 			if (ps.bannerId !== undefined) updates.bannerId = ps.bannerId; | ||||||
| 			if (ps.mutedWords !== undefined) { | 			if (ps.mutedWords !== undefined) { | ||||||
|  | 				// TODO: ちゃんと数える
 | ||||||
|  | 				const length = JSON.stringify(ps.mutedWords).length; | ||||||
|  | 				if (length > (await this.roleService.getUserRoleOptions(user.id)).antennaLimit) { | ||||||
|  | 					throw new ApiError(meta.errors.tooManyMutedWords); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
| 				// validate regular expression syntax
 | 				// validate regular expression syntax
 | ||||||
| 				ps.mutedWords.filter(x => !Array.isArray(x)).forEach(x => { | 				ps.mutedWords.filter(x => !Array.isArray(x)).forEach(x => { | ||||||
| 					const regexp = x.match(/^\/(.+)\/(.*)$/); | 					const regexp = x.match(/^\/(.+)\/(.*)$/); | ||||||
|  |  | ||||||
|  | @ -127,6 +127,19 @@ | ||||||
| 					</MkInput> | 					</MkInput> | ||||||
| 				</div> | 				</div> | ||||||
| 			</MkFolder> | 			</MkFolder> | ||||||
|  | 
 | ||||||
|  | 			<MkFolder> | ||||||
|  | 				<template #label>{{ i18n.ts._role._options.wordMuteMax }}</template> | ||||||
|  | 				<template #suffix>{{ options_wordMuteLimit_useDefault ? i18n.ts._role.useBaseValue : (options_wordMuteLimit_value) }}</template> | ||||||
|  | 				<div class="_gaps"> | ||||||
|  | 					<MkSwitch v-model="options_wordMuteLimit_useDefault" :readonly="readonly"> | ||||||
|  | 						<template #label>{{ i18n.ts._role.useBaseValue }}</template> | ||||||
|  | 					</MkSwitch> | ||||||
|  | 					<MkInput v-model="options_wordMuteLimit_value" :disabled="options_wordMuteLimit_useDefault" type="number" :readonly="readonly"> | ||||||
|  | 						<template #suffix>chars</template> | ||||||
|  | 					</MkInput> | ||||||
|  | 				</div> | ||||||
|  | 			</MkFolder> | ||||||
| 		</div> | 		</div> | ||||||
| 	</FormSlot> | 	</FormSlot> | ||||||
| 
 | 
 | ||||||
|  | @ -194,6 +207,8 @@ let options_driveCapacityMb_useDefault = $ref(role?.options?.driveCapacityMb?.us | ||||||
| let options_driveCapacityMb_value = $ref(role?.options?.driveCapacityMb?.value ?? 0); | let options_driveCapacityMb_value = $ref(role?.options?.driveCapacityMb?.value ?? 0); | ||||||
| let options_antennaLimit_useDefault = $ref(role?.options?.antennaLimit?.useDefault ?? true); | let options_antennaLimit_useDefault = $ref(role?.options?.antennaLimit?.useDefault ?? true); | ||||||
| let options_antennaLimit_value = $ref(role?.options?.antennaLimit?.value ?? 0); | let options_antennaLimit_value = $ref(role?.options?.antennaLimit?.value ?? 0); | ||||||
|  | let options_wordMuteLimit_useDefault = $ref(role?.options?.wordMuteLimit?.useDefault ?? true); | ||||||
|  | let options_wordMuteLimit_value = $ref(role?.options?.wordMuteLimit?.value ?? 0); | ||||||
| 
 | 
 | ||||||
| if (_DEV_) { | if (_DEV_) { | ||||||
| 	watch($$(condFormula), () => { | 	watch($$(condFormula), () => { | ||||||
|  | @ -210,6 +225,7 @@ function getOptions() { | ||||||
| 		canManageCustomEmojis: { useDefault: options_canManageCustomEmojis_useDefault, value: options_canManageCustomEmojis_value }, | 		canManageCustomEmojis: { useDefault: options_canManageCustomEmojis_useDefault, value: options_canManageCustomEmojis_value }, | ||||||
| 		driveCapacityMb: { useDefault: options_driveCapacityMb_useDefault, value: options_driveCapacityMb_value }, | 		driveCapacityMb: { useDefault: options_driveCapacityMb_useDefault, value: options_driveCapacityMb_value }, | ||||||
| 		antennaLimit: { useDefault: options_antennaLimit_useDefault, value: options_antennaLimit_value }, | 		antennaLimit: { useDefault: options_antennaLimit_useDefault, value: options_antennaLimit_value }, | ||||||
|  | 		wordMuteLimit: { useDefault: options_wordMuteLimit_useDefault, value: options_wordMuteLimit_value }, | ||||||
| 	}; | 	}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -62,6 +62,15 @@ | ||||||
| 							<MkInput v-model="options_antennaLimit" type="number"> | 							<MkInput v-model="options_antennaLimit" type="number"> | ||||||
| 							</MkInput> | 							</MkInput> | ||||||
| 						</MkFolder> | 						</MkFolder> | ||||||
|  | 
 | ||||||
|  | 						<MkFolder> | ||||||
|  | 							<template #label>{{ i18n.ts._role._options.wordMuteMax }}</template> | ||||||
|  | 							<template #suffix>{{ options_wordMuteLimit }}</template> | ||||||
|  | 							<MkInput v-model="options_wordMuteLimit" type="number"> | ||||||
|  | 								<template #suffix>chars</template> | ||||||
|  | 							</MkInput> | ||||||
|  | 						</MkFolder> | ||||||
|  | 
 | ||||||
| 						<MkButton primary rounded @click="updateBaseRole">{{ i18n.ts.save }}</MkButton> | 						<MkButton primary rounded @click="updateBaseRole">{{ i18n.ts.save }}</MkButton> | ||||||
| 					</div> | 					</div> | ||||||
| 				</MkFolder> | 				</MkFolder> | ||||||
|  | @ -101,6 +110,7 @@ let options_canInvite = $ref(instance.baseRole.canInvite); | ||||||
| let options_canManageCustomEmojis = $ref(instance.baseRole.canManageCustomEmojis); | let options_canManageCustomEmojis = $ref(instance.baseRole.canManageCustomEmojis); | ||||||
| let options_driveCapacityMb = $ref(instance.baseRole.driveCapacityMb); | let options_driveCapacityMb = $ref(instance.baseRole.driveCapacityMb); | ||||||
| let options_antennaLimit = $ref(instance.baseRole.antennaLimit); | let options_antennaLimit = $ref(instance.baseRole.antennaLimit); | ||||||
|  | let options_wordMuteLimit = $ref(instance.baseRole.wordMuteLimit); | ||||||
| 
 | 
 | ||||||
| async function updateBaseRole() { | async function updateBaseRole() { | ||||||
| 	await os.apiWithDialog('admin/roles/update-default-role-override', { | 	await os.apiWithDialog('admin/roles/update-default-role-override', { | ||||||
|  | @ -112,6 +122,7 @@ async function updateBaseRole() { | ||||||
| 			canManageCustomEmojis: options_canManageCustomEmojis, | 			canManageCustomEmojis: options_canManageCustomEmojis, | ||||||
| 			driveCapacityMb: options_driveCapacityMb, | 			driveCapacityMb: options_driveCapacityMb, | ||||||
| 			antennaLimit: options_antennaLimit, | 			antennaLimit: options_antennaLimit, | ||||||
|  | 			wordMuteLimit: options_wordMuteLimit, | ||||||
| 		}, | 		}, | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue