Enhance tag management with cooldown feature and related updates
- Added a `cooldownSeconds` field to the `Tag` model, allowing for customizable cooldown periods between tag responses. - Updated tag command handling to incorporate cooldown logic, preventing spam and improving user experience. - Enhanced the WebUI to support cooldown configuration for tags, including input validation and localization for cooldown messages. - Refactored tag-related service functions to manage cooldowns effectively, ensuring accurate tracking and enforcement. - Improved localization for new cooldown-related messages in both English and German.
This commit is contained in:
@@ -42,6 +42,7 @@ const EMPTY_DRAFT = {
|
||||
components: null as MessageComponentsV2 | null,
|
||||
responseType: 'TEXT' as TagResponseType,
|
||||
triggerWord: '',
|
||||
cooldownSeconds: 15,
|
||||
allowedRoleIds: [] as string[],
|
||||
allowedChannelIds: [] as string[]
|
||||
};
|
||||
@@ -56,6 +57,7 @@ export function TagsManager({ guildId, initialTags }: TagsManagerProps) {
|
||||
const [tags, setTags] = useState<LocalTag[]>(
|
||||
initialTags.map((tag, index) => ({
|
||||
...tag,
|
||||
cooldownSeconds: tag.cooldownSeconds ?? 15,
|
||||
embed: tag.embed ?? null,
|
||||
components: tag.components ?? null,
|
||||
clientKey: tag.id ?? `existing-${index}`
|
||||
@@ -100,6 +102,7 @@ export function TagsManager({ guildId, initialTags }: TagsManagerProps) {
|
||||
components: validated.components,
|
||||
responseType: draft.responseType,
|
||||
triggerWord: draft.triggerWord.trim() || null,
|
||||
cooldownSeconds: draft.cooldownSeconds,
|
||||
allowedRoleIds: draft.allowedRoleIds,
|
||||
allowedChannelIds: draft.allowedChannelIds
|
||||
})
|
||||
@@ -142,6 +145,7 @@ export function TagsManager({ guildId, initialTags }: TagsManagerProps) {
|
||||
components: validated.components,
|
||||
responseType: tag.responseType,
|
||||
triggerWord: tag.triggerWord,
|
||||
cooldownSeconds: tag.cooldownSeconds,
|
||||
allowedRoleIds: tag.allowedRoleIds,
|
||||
allowedChannelIds: tag.allowedChannelIds
|
||||
})
|
||||
@@ -255,6 +259,22 @@ export function TagsManager({ guildId, initialTags }: TagsManagerProps) {
|
||||
<Label>{t('modulePages.tags.triggerWord')}</Label>
|
||||
<Input value={draft.triggerWord} onChange={(event) => setDraft((prev) => ({ ...prev, triggerWord: event.target.value }))} placeholder={t('common.optional')} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>{t('modulePages.tags.cooldownSeconds')}</Label>
|
||||
<Input
|
||||
type="number"
|
||||
min={0}
|
||||
max={3600}
|
||||
value={draft.cooldownSeconds}
|
||||
onChange={(event) =>
|
||||
setDraft((prev) => ({
|
||||
...prev,
|
||||
cooldownSeconds: Math.min(3600, Math.max(0, Number(event.target.value) || 0))
|
||||
}))
|
||||
}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">{t('modulePages.tags.cooldownHint')}</p>
|
||||
</div>
|
||||
</div>
|
||||
{renderResponseEditor(
|
||||
draft.responseType,
|
||||
@@ -325,6 +345,28 @@ export function TagsManager({ guildId, initialTags }: TagsManagerProps) {
|
||||
<Label>{t('modulePages.tags.triggerWord')}</Label>
|
||||
<Input value={tag.triggerWord ?? ''} onChange={(event) => setTags((prev) => prev.map((entry) => (entry.clientKey === tag.clientKey ? { ...entry, triggerWord: event.target.value || null } : entry)))} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>{t('modulePages.tags.cooldownSeconds')}</Label>
|
||||
<Input
|
||||
type="number"
|
||||
min={0}
|
||||
max={3600}
|
||||
value={tag.cooldownSeconds}
|
||||
onChange={(event) =>
|
||||
setTags((prev) =>
|
||||
prev.map((entry) =>
|
||||
entry.clientKey === tag.clientKey
|
||||
? {
|
||||
...entry,
|
||||
cooldownSeconds: Math.min(3600, Math.max(0, Number(event.target.value) || 0))
|
||||
}
|
||||
: entry
|
||||
)
|
||||
)
|
||||
}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">{t('modulePages.tags.cooldownHint')}</p>
|
||||
</div>
|
||||
</div>
|
||||
{renderResponseEditor(
|
||||
tag.responseType,
|
||||
|
||||
Reference in New Issue
Block a user