3
0
Fork 0
mirror of https://github.com/ZeppelinBot/Zeppelin.git synced 2025-03-18 15:00:00 +00:00

refactor: don't create 'name' property in config dynamically

This commit is contained in:
Dragory 2024-11-10 13:47:40 +02:00
parent 0810dd3f0e
commit 395a750e9d
No known key found for this signature in database
19 changed files with 86 additions and 154 deletions

View file

@ -2,8 +2,8 @@ import { GuildPluginData } from "knub";
import { convertDelayStringToMS } from "../../../utils.js"; import { convertDelayStringToMS } from "../../../utils.js";
import { AutomodContext, AutomodPluginType, TRule } from "../types.js"; import { AutomodContext, AutomodPluginType, TRule } from "../types.js";
export function applyCooldown(pluginData: GuildPluginData<AutomodPluginType>, rule: TRule, context: AutomodContext) { export function applyCooldown(pluginData: GuildPluginData<AutomodPluginType>, rule: TRule, ruleName: string, context: AutomodContext) {
const cooldownKey = `${rule.name}-${context.user?.id}`; const cooldownKey = `${ruleName}-${context.user?.id}`;
const cooldownTime = convertDelayStringToMS(rule.cooldown, "s"); const cooldownTime = convertDelayStringToMS(rule.cooldown, "s");
if (cooldownTime) pluginData.state.cooldownManager.setCooldown(cooldownKey, cooldownTime); if (cooldownTime) pluginData.state.cooldownManager.setCooldown(cooldownKey, cooldownTime);

View file

@ -1,8 +1,8 @@
import { GuildPluginData } from "knub"; import { GuildPluginData } from "knub";
import { AutomodContext, AutomodPluginType, TRule } from "../types.js"; import { AutomodContext, AutomodPluginType, TRule } from "../types.js";
export function checkCooldown(pluginData: GuildPluginData<AutomodPluginType>, rule: TRule, context: AutomodContext) { export function checkCooldown(pluginData: GuildPluginData<AutomodPluginType>, rule: TRule, ruleName: string, context: AutomodContext) {
const cooldownKey = `${rule.name}-${context.user?.id}`; const cooldownKey = `${ruleName}-${context.user?.id}`;
return pluginData.state.cooldownManager.isOnCooldown(cooldownKey); return pluginData.state.cooldownManager.isOnCooldown(cooldownKey);
} }

View file

@ -49,7 +49,7 @@ export async function runAutomod(pluginData: GuildPluginData<AutomodPluginType>,
} }
if (!rule.affects_self && userId && userId === pluginData.client.user?.id) continue; if (!rule.affects_self && userId && userId === pluginData.client.user?.id) continue;
if (rule.cooldown && checkCooldown(pluginData, rule, context)) { if (rule.cooldown && checkCooldown(pluginData, rule, ruleName, context)) {
continue; continue;
} }
@ -87,7 +87,7 @@ export async function runAutomod(pluginData: GuildPluginData<AutomodPluginType>,
} }
if (matchResult) { if (matchResult) {
if (rule.cooldown) applyCooldown(pluginData, rule, context); if (rule.cooldown) applyCooldown(pluginData, rule, ruleName, context);
contexts = [context, ...(matchResult.extraContexts || [])]; contexts = [context, ...(matchResult.extraContexts || [])];

View file

@ -9,30 +9,23 @@ interface MatchResultType {
mode: "blacklist" | "whitelist"; mode: "blacklist" | "whitelist";
} }
const configSchema = z const baseConfig = z.strictObject({
.strictObject({
filetype_blacklist: z.array(z.string().max(32)).max(255).default([]), filetype_blacklist: z.array(z.string().max(32)).max(255).default([]),
blacklist_enabled: z.boolean().default(false),
filetype_whitelist: z.array(z.string().max(32)).max(255).default([]), filetype_whitelist: z.array(z.string().max(32)).max(255).default([]),
whitelist_enabled: z.boolean().default(false), });
}) const configWithWhitelist = baseConfig.merge(z.strictObject({
.transform((parsed, ctx) => { whitelist_enabled: z.literal(true),
if (parsed.blacklist_enabled && parsed.whitelist_enabled) { blacklist_enabled: z.literal(false).default(false),
ctx.addIssue({ }));
code: z.ZodIssueCode.custom, const configWithBlacklist = baseConfig.merge(z.strictObject({
message: "Cannot have both blacklist and whitelist enabled", blacklist_enabled: z.literal(true),
}); whitelist_enabled: z.literal(false).default(false),
return z.NEVER; }));
}
if (!parsed.blacklist_enabled && !parsed.whitelist_enabled) { const configSchema = z.union([
ctx.addIssue({ configWithWhitelist,
code: z.ZodIssueCode.custom, configWithBlacklist,
message: "Must have either blacklist or whitelist enabled", ]);
});
return z.NEVER;
}
return parsed;
});
export const MatchAttachmentTypeTrigger = automodTrigger<MatchResultType>()({ export const MatchAttachmentTypeTrigger = automodTrigger<MatchResultType>()({
configSchema, configSchema,

View file

@ -8,30 +8,23 @@ interface MatchResultType {
mode: "blacklist" | "whitelist"; mode: "blacklist" | "whitelist";
} }
const configSchema = z const baseConfig = z.strictObject({
.strictObject({ mime_type_blacklist: z.array(z.string().max(32)).max(255).default([]),
mime_type_blacklist: z.array(z.string().max(255)).max(255).default([]), mime_type_whitelist: z.array(z.string().max(32)).max(255).default([]),
blacklist_enabled: z.boolean().default(false), });
mime_type_whitelist: z.array(z.string().max(255)).max(255).default([]), const configWithWhitelist = baseConfig.merge(z.strictObject({
whitelist_enabled: z.boolean().default(false), whitelist_enabled: z.literal(true),
}) blacklist_enabled: z.literal(false).default(false),
.transform((parsed, ctx) => { }));
if (parsed.blacklist_enabled && parsed.whitelist_enabled) { const configWithBlacklist = baseConfig.merge(z.strictObject({
ctx.addIssue({ blacklist_enabled: z.literal(true),
code: z.ZodIssueCode.custom, whitelist_enabled: z.literal(false).default(false),
message: "Cannot have both blacklist and whitelist enabled", }));
});
return z.NEVER; const configSchema = z.union([
} configWithWhitelist,
if (!parsed.blacklist_enabled && !parsed.whitelist_enabled) { configWithBlacklist,
ctx.addIssue({ ]);
code: z.ZodIssueCode.custom,
message: "Must have either blacklist or whitelist enabled",
});
return z.NEVER;
}
return parsed;
});
export const MatchMimeTypeTrigger = automodTrigger<MatchResultType>()({ export const MatchMimeTypeTrigger = automodTrigger<MatchResultType>()({
configSchema, configSchema,

View file

@ -46,22 +46,6 @@ const zActionsMap = z
const zRule = z.strictObject({ const zRule = z.strictObject({
enabled: z.boolean().default(true), enabled: z.boolean().default(true),
// Typed as "never" because you are not expected to supply this directly.
// The transform instead picks it up from the property key and the output type is a string.
name: z
.never()
.optional()
.transform((_, ctx) => {
const ruleName = String(ctx.path[ctx.path.length - 2]).trim();
if (!ruleName) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Automod rules must have names",
});
return z.NEVER;
}
return ruleName;
}),
pretty_name: z.string().optional(), pretty_name: z.string().optional(),
presets: z.array(z.string().max(100)).max(25).default([]), presets: z.array(z.string().max(100)).max(25).default([]),
affects_bots: z.boolean().default(false), affects_bots: z.boolean().default(false),
@ -69,9 +53,7 @@ const zRule = z.strictObject({
cooldown: zDelayString.nullable().default(null), cooldown: zDelayString.nullable().default(null),
allow_further_rules: z.boolean().default(false), allow_further_rules: z.boolean().default(false),
triggers: z.array(zTriggersMap), triggers: z.array(zTriggersMap),
actions: zActionsMap.refine((v) => !(v.clean && v.start_thread), { actions: zActionsMap,
message: "Cannot have both clean and start_thread active at the same time",
}),
}); });
export type TRule = z.infer<typeof zRule>; export type TRule = z.infer<typeof zRule>;

View file

@ -97,20 +97,20 @@ export const CountersPlugin = guildPlugin<CountersPluginType>()({
// Initialize and store the IDs of each of the counters internally // Initialize and store the IDs of each of the counters internally
state.counterIds = {}; state.counterIds = {};
const config = pluginData.config.get(); const config = pluginData.config.get();
for (const counter of Object.values(config.counters)) { for (const [counterName, counter] of Object.entries(config.counters)) {
const dbCounter = await state.counters.findOrCreateCounter(counter.name, counter.per_channel, counter.per_user); const dbCounter = await state.counters.findOrCreateCounter(counterName, counter.per_channel, counter.per_user);
state.counterIds[counter.name] = dbCounter.id; state.counterIds[counterName] = dbCounter.id;
const thisCounterTriggers: CounterTrigger[] = []; const thisCounterTriggers: CounterTrigger[] = [];
state.counterTriggersByCounterId.set(dbCounter.id, thisCounterTriggers); state.counterTriggersByCounterId.set(dbCounter.id, thisCounterTriggers);
// Initialize triggers // Initialize triggers
for (const trigger of values(counter.triggers)) { for (const [triggerName, trigger] of Object.entries(counter.triggers)) {
const parsedCondition = parseCounterConditionString(trigger.condition)!; const parsedCondition = parseCounterConditionString(trigger.condition)!;
const parsedReverseCondition = parseCounterConditionString(trigger.reverse_condition)!; const parsedReverseCondition = parseCounterConditionString(trigger.reverse_condition)!;
const counterTrigger = await state.counters.initCounterTrigger( const counterTrigger = await state.counters.initCounterTrigger(
dbCounter.id, dbCounter.id,
trigger.name, triggerName,
parsedCondition[0], parsedCondition[0],
parsedCondition[1], parsedCondition[1],
parsedReverseCondition[0], parsedReverseCondition[0],

View file

@ -119,18 +119,17 @@ export const AddCounterCmd = guildPluginMessageCommand<CountersPluginType>()({
await changeCounterValue(pluginData, args.counterName, channel?.id ?? null, user?.id ?? null, amount); await changeCounterValue(pluginData, args.counterName, channel?.id ?? null, user?.id ?? null, amount);
const newValue = await pluginData.state.counters.getCurrentValue(counterId, channel?.id ?? null, user?.id ?? null); const newValue = await pluginData.state.counters.getCurrentValue(counterId, channel?.id ?? null, user?.id ?? null);
const counterName = counter.name || args.counterName;
if (channel && user) { if (channel && user) {
message.channel.send( message.channel.send(
`Added ${amount} to **${counterName}** for <@!${user.id}> in <#${channel.id}>. The value is now ${newValue}.`, `Added ${amount} to **${args.counterName}** for <@!${user.id}> in <#${channel.id}>. The value is now ${newValue}.`,
); );
} else if (channel) { } else if (channel) {
message.channel.send(`Added ${amount} to **${counterName}** in <#${channel.id}>. The value is now ${newValue}.`); message.channel.send(`Added ${amount} to **${args.counterName}** in <#${channel.id}>. The value is now ${newValue}.`);
} else if (user) { } else if (user) {
message.channel.send(`Added ${amount} to **${counterName}** for <@!${user.id}>. The value is now ${newValue}.`); message.channel.send(`Added ${amount} to **${args.counterName}** for <@!${user.id}>. The value is now ${newValue}.`);
} else { } else {
message.channel.send(`Added ${amount} to **${counterName}**. The value is now ${newValue}.`); message.channel.send(`Added ${amount} to **${args.counterName}**. The value is now ${newValue}.`);
} }
}, },
}); });

View file

@ -12,14 +12,14 @@ export const CountersListCmd = guildPluginMessageCommand<CountersPluginType>()({
async run({ pluginData, message }) { async run({ pluginData, message }) {
const config = await pluginData.config.getForMessage(message); const config = await pluginData.config.getForMessage(message);
const countersToShow = Array.from(Object.values(config.counters)).filter((c) => c.can_view !== false); const countersToShow = Object.entries(config.counters).filter(([, c]) => c.can_view !== false);
if (!countersToShow.length) { if (!countersToShow.length) {
void pluginData.state.common.sendErrorMessage(message, "No counters are configured for this server"); void pluginData.state.common.sendErrorMessage(message, "No counters are configured for this server");
return; return;
} }
const counterLines = countersToShow.map((counter) => { const counterLines = countersToShow.map(([counterName, counter]) => {
const title = counter.pretty_name ? `**${counter.pretty_name}** (\`${counter.name}\`)` : `\`${counter.name}\``; const title = counter.pretty_name ? `**${counter.pretty_name}** (\`${counterName}\`)` : `\`${counterName}\``;
const types: string[] = []; const types: string[] = [];
if (counter.per_user) types.push("per user"); if (counter.per_user) types.push("per user");

View file

@ -29,10 +29,9 @@ export const ResetAllCounterValuesCmd = guildPluginMessageCommand<CountersPlugin
return; return;
} }
const counterName = counter.name || args.counterName;
const confirmed = await confirm(message, message.author.id, { const confirmed = await confirm(message, message.author.id, {
content: trimMultilineString(` content: trimMultilineString(`
Do you want to reset **ALL** values for counter **${counterName}**? Do you want to reset **ALL** values for counter **${args.counterName}**?
This will reset the counter for **all** users and channels. This will reset the counter for **all** users and channels.
**Note:** This will *not* trigger any triggers or counter triggers. **Note:** This will *not* trigger any triggers or counter triggers.
`), `),
@ -43,7 +42,7 @@ export const ResetAllCounterValuesCmd = guildPluginMessageCommand<CountersPlugin
} }
const loadingMessage = await message.channel const loadingMessage = await message.channel
.send(`Resetting counter **${counterName}**. This might take a while. Please don't reload the config.`) .send(`Resetting counter **${args.counterName}**. This might take a while. Please don't reload the config.`)
.catch(() => null); .catch(() => null);
await resetAllCounterValues(pluginData, args.counterName); await resetAllCounterValues(pluginData, args.counterName);
@ -51,7 +50,7 @@ export const ResetAllCounterValuesCmd = guildPluginMessageCommand<CountersPlugin
loadingMessage?.delete().catch(noop); loadingMessage?.delete().catch(noop);
void pluginData.state.common.sendSuccessMessage( void pluginData.state.common.sendSuccessMessage(
message, message,
`All counter values for **${counterName}** have been reset`, `All counter values for **${args.counterName}** have been reset`,
); );
pluginData.getKnubInstance().reloadGuild(pluginData.guild.id); pluginData.getKnubInstance().reloadGuild(pluginData.guild.id);

View file

@ -95,16 +95,15 @@ export const ResetCounterCmd = guildPluginMessageCommand<CountersPluginType>()({
} }
await setCounterValue(pluginData, args.counterName, channel?.id ?? null, user?.id ?? null, counter.initial_value); await setCounterValue(pluginData, args.counterName, channel?.id ?? null, user?.id ?? null, counter.initial_value);
const counterName = counter.name || args.counterName;
if (channel && user) { if (channel && user) {
message.channel.send(`Reset **${counterName}** for <@!${user.id}> in <#${channel.id}>`); message.channel.send(`Reset **${args.counterName}** for <@!${user.id}> in <#${channel.id}>`);
} else if (channel) { } else if (channel) {
message.channel.send(`Reset **${counterName}** in <#${channel.id}>`); message.channel.send(`Reset **${args.counterName}** in <#${channel.id}>`);
} else if (user) { } else if (user) {
message.channel.send(`Reset **${counterName}** for <@!${user.id}>`); message.channel.send(`Reset **${args.counterName}** for <@!${user.id}>`);
} else { } else {
message.channel.send(`Reset **${counterName}**`); message.channel.send(`Reset **${args.counterName}**`);
} }
}, },
}); });

View file

@ -123,16 +123,15 @@ export const SetCounterCmd = guildPluginMessageCommand<CountersPluginType>()({
} }
await setCounterValue(pluginData, args.counterName, channel?.id ?? null, user?.id ?? null, value); await setCounterValue(pluginData, args.counterName, channel?.id ?? null, user?.id ?? null, value);
const counterName = counter.name || args.counterName;
if (channel && user) { if (channel && user) {
message.channel.send(`Set **${counterName}** for <@!${user.id}> in <#${channel.id}> to ${value}`); message.channel.send(`Set **${args.counterName}** for <@!${user.id}> in <#${channel.id}> to ${value}`);
} else if (channel) { } else if (channel) {
message.channel.send(`Set **${counterName}** in <#${channel.id}> to ${value}`); message.channel.send(`Set **${args.counterName}** in <#${channel.id}> to ${value}`);
} else if (user) { } else if (user) {
message.channel.send(`Set **${counterName}** for <@!${user.id}> to ${value}`); message.channel.send(`Set **${args.counterName}** for <@!${user.id}> to ${value}`);
} else { } else {
message.channel.send(`Set **${counterName}** to ${value}`); message.channel.send(`Set **${args.counterName}** to ${value}`);
} }
}, },
}); });

View file

@ -95,16 +95,15 @@ export const ViewCounterCmd = guildPluginMessageCommand<CountersPluginType>()({
const value = await pluginData.state.counters.getCurrentValue(counterId, channel?.id ?? null, user?.id ?? null); const value = await pluginData.state.counters.getCurrentValue(counterId, channel?.id ?? null, user?.id ?? null);
const finalValue = value ?? counter.initial_value; const finalValue = value ?? counter.initial_value;
const counterName = counter.name || args.counterName;
if (channel && user) { if (channel && user) {
message.channel.send(`**${counterName}** for <@!${user.id}> in <#${channel.id}> is ${finalValue}`); message.channel.send(`**${args.counterName}** for <@!${user.id}> in <#${channel.id}> is ${finalValue}`);
} else if (channel) { } else if (channel) {
message.channel.send(`**${counterName}** in <#${channel.id}> is ${finalValue}`); message.channel.send(`**${args.counterName}** in <#${channel.id}> is ${finalValue}`);
} else if (user) { } else if (user) {
message.channel.send(`**${counterName}** for <@!${user.id}> is ${finalValue}`); message.channel.send(`**${args.counterName}** for <@!${user.id}> is ${finalValue}`);
} else { } else {
message.channel.send(`**${counterName}** is ${finalValue}`); message.channel.send(`**${args.counterName}** is ${finalValue}`);
} }
}, },
}); });

View file

@ -4,5 +4,5 @@ import { CountersPluginType } from "../types.js";
export function getPrettyNameForCounter(pluginData: GuildPluginData<CountersPluginType>, counterName: string) { export function getPrettyNameForCounter(pluginData: GuildPluginData<CountersPluginType>, counterName: string) {
const config = pluginData.config.get(); const config = pluginData.config.get();
const counter = config.counters[counterName]; const counter = config.counters[counterName];
return counter ? counter.pretty_name || counter.name : "Unknown Counter"; return counter ? counter.pretty_name || counterName : "Unknown Counter";
} }

View file

@ -75,22 +75,6 @@ const zTriggerFromString = zBoundedCharacters(0, 100).transform((val, ctx) => {
const zTriggerInput = z.union([zTrigger, zTriggerFromString]); const zTriggerInput = z.union([zTrigger, zTriggerFromString]);
export const zCounter = z.strictObject({ export const zCounter = z.strictObject({
// Typed as "never" because you are not expected to supply this directly.
// The transform instead picks it up from the property key and the output type is a string.
name: z
.never()
.optional()
.transform((_, ctx) => {
const ruleName = String(ctx.path[ctx.path.length - 2]).trim();
if (!ruleName) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Counters must have names",
});
return z.NEVER;
}
return ruleName;
}),
pretty_name: zBoundedCharacters(0, 100).nullable().default(null), pretty_name: zBoundedCharacters(0, 100).nullable().default(null),
per_channel: z.boolean().default(false), per_channel: z.boolean().default(false),
per_user: z.boolean().default(false), per_user: z.boolean().default(false),

View file

@ -6,26 +6,26 @@ import { applyRoleButtons } from "./applyRoleButtons.js";
export async function applyAllRoleButtons(pluginData: GuildPluginData<RoleButtonsPluginType>) { export async function applyAllRoleButtons(pluginData: GuildPluginData<RoleButtonsPluginType>) {
const savedRoleButtons = await pluginData.state.roleButtons.getSavedRoleButtons(); const savedRoleButtons = await pluginData.state.roleButtons.getSavedRoleButtons();
const config = pluginData.config.get(); const config = pluginData.config.get();
for (const buttons of Object.values(config.buttons)) { for (const [configName, configItem] of Object.entries(config.buttons)) {
// Use the hash of the config to quickly check if we need to update buttons // Use the hash of the config to quickly check if we need to update buttons
const hash = createHash("md5").update(JSON.stringify(buttons)).digest("hex"); const hash = createHash("md5").update(JSON.stringify(configItem)).digest("hex");
const savedButtonsItem = savedRoleButtons.find((bt) => bt.name === buttons.name); const savedButtonsItem = savedRoleButtons.find((bt) => bt.name === configName);
if (savedButtonsItem?.hash === hash) { if (savedButtonsItem?.hash === hash) {
// No changes // No changes
continue; continue;
} }
if (savedButtonsItem) { if (savedButtonsItem) {
await pluginData.state.roleButtons.deleteRoleButtonItem(buttons.name); await pluginData.state.roleButtons.deleteRoleButtonItem(configName);
} }
const applyResult = await applyRoleButtons(pluginData, buttons, savedButtonsItem ?? null); const applyResult = await applyRoleButtons(pluginData, configItem, configName, savedButtonsItem ?? null);
if (!applyResult) { if (!applyResult) {
return; return;
} }
await pluginData.state.roleButtons.saveRoleButtonItem( await pluginData.state.roleButtons.saveRoleButtonItem(
buttons.name, configName,
applyResult.channel_id, applyResult.channel_id,
applyResult.message_id, applyResult.message_id,
hash, hash,

View file

@ -8,6 +8,7 @@ import { createButtonComponents } from "./createButtonComponents.js";
export async function applyRoleButtons( export async function applyRoleButtons(
pluginData: GuildPluginData<RoleButtonsPluginType>, pluginData: GuildPluginData<RoleButtonsPluginType>,
configItem: TRoleButtonsConfigItem, configItem: TRoleButtonsConfigItem,
configName: string,
existingSavedButtons: RoleButtonsItem | null, existingSavedButtons: RoleButtonsItem | null,
): Promise<{ channel_id: string; message_id: string } | null> { ): Promise<{ channel_id: string; message_id: string } | null> {
let message: Message; let message: Message;
@ -32,7 +33,7 @@ export async function applyRoleButtons(
channel.messages.fetch(configItem.message.message_id).catch(() => null)); channel.messages.fetch(configItem.message.message_id).catch(() => null));
if (!messageCandidate) { if (!messageCandidate) {
pluginData.getPlugin(LogsPlugin).logBotAlert({ pluginData.getPlugin(LogsPlugin).logBotAlert({
body: `Message not found for role_buttons/${configItem.name}`, body: `Message not found for role_buttons/${configName}`,
}); });
return null; return null;
} }
@ -45,7 +46,7 @@ export async function applyRoleButtons(
: Boolean(configItem.message.content.content?.trim()) || configItem.message.content.embeds?.length; : Boolean(configItem.message.content.content?.trim()) || configItem.message.content.embeds?.length;
if (!contentIsValid) { if (!contentIsValid) {
pluginData.getPlugin(LogsPlugin).logBotAlert({ pluginData.getPlugin(LogsPlugin).logBotAlert({
body: `Invalid message content for role_buttons/${configItem.name}`, body: `Invalid message content for role_buttons/${configName}`,
}); });
return null; return null;
} }
@ -58,7 +59,7 @@ export async function applyRoleButtons(
} }
if (!channel || !channel?.isTextBased()) { if (!channel || !channel?.isTextBased()) {
pluginData.getPlugin(LogsPlugin).logBotAlert({ pluginData.getPlugin(LogsPlugin).logBotAlert({
body: `Text channel not found for role_buttons/${configItem.name}`, body: `Text channel not found for role_buttons/${configName}`,
}); });
return null; return null;
} }
@ -89,7 +90,7 @@ export async function applyRoleButtons(
candidateMessage = await channel.send(configItem.message.content as string | MessageCreateOptions); candidateMessage = await channel.send(configItem.message.content as string | MessageCreateOptions);
} catch (err) { } catch (err) {
pluginData.getPlugin(LogsPlugin).logBotAlert({ pluginData.getPlugin(LogsPlugin).logBotAlert({
body: `Error while posting message for role_buttons/${configItem.name}: ${String(err)}`, body: `Error while posting message for role_buttons/${configName}: ${String(err)}`,
}); });
return null; return null;
} }
@ -100,16 +101,16 @@ export async function applyRoleButtons(
if (message.author.id !== pluginData.client.user?.id) { if (message.author.id !== pluginData.client.user?.id) {
pluginData.getPlugin(LogsPlugin).logBotAlert({ pluginData.getPlugin(LogsPlugin).logBotAlert({
body: `Error applying role buttons for role_buttons/${configItem.name}: target message must be posted by Zeppelin`, body: `Error applying role buttons for role_buttons/${configName}: target message must be posted by Zeppelin`,
}); });
return null; return null;
} }
// Apply role buttons // Apply role buttons
const components = createButtonComponents(configItem); const components = createButtonComponents(configItem, configName);
await message.edit({ components }).catch((err) => { await message.edit({ components }).catch((err) => {
pluginData.getPlugin(LogsPlugin).logBotAlert({ pluginData.getPlugin(LogsPlugin).logBotAlert({
body: `Error applying role buttons for role_buttons/${configItem.name}: ${String(err)}`, body: `Error applying role buttons for role_buttons/${configName}: ${String(err)}`,
}); });
return null; return null;
}); });

View file

@ -4,7 +4,7 @@ import { TRoleButtonsConfigItem } from "../types.js";
import { TooManyComponentsError } from "./TooManyComponentsError.js"; import { TooManyComponentsError } from "./TooManyComponentsError.js";
import { convertButtonStyleStringToEnum } from "./convertButtonStyleStringToEnum.js"; import { convertButtonStyleStringToEnum } from "./convertButtonStyleStringToEnum.js";
export function createButtonComponents(configItem: TRoleButtonsConfigItem): Array<ActionRowBuilder<ButtonBuilder>> { export function createButtonComponents(configItem: TRoleButtonsConfigItem, configName: string): Array<ActionRowBuilder<ButtonBuilder>> {
const rows: Array<ActionRowBuilder<ButtonBuilder>> = []; const rows: Array<ActionRowBuilder<ButtonBuilder>> = [];
let currentRow = new ActionRowBuilder<ButtonBuilder>(); let currentRow = new ActionRowBuilder<ButtonBuilder>();
@ -17,7 +17,7 @@ export function createButtonComponents(configItem: TRoleButtonsConfigItem): Arra
const button = new ButtonBuilder() const button = new ButtonBuilder()
.setLabel(option.label ?? "") .setLabel(option.label ?? "")
.setStyle(convertButtonStyleStringToEnum(option.style) ?? ButtonStyle.Primary) .setStyle(convertButtonStyleStringToEnum(option.style) ?? ButtonStyle.Primary)
.setCustomId(buildCustomId("roleButtons", { name: configItem.name, index })); .setCustomId(buildCustomId("roleButtons", { name: configName, index }));
if (option.emoji) { if (option.emoji) {
button.setEmoji(option.emoji); button.setEmoji(option.emoji);

View file

@ -34,22 +34,6 @@ export type TRoleButtonOption = z.infer<typeof zRoleButtonOption>;
const zRoleButtonsConfigItem = z const zRoleButtonsConfigItem = z
.strictObject({ .strictObject({
// Typed as "never" because you are not expected to supply this directly.
// The transform instead picks it up from the property key and the output type is a string.
name: z
.never()
.optional()
.transform((_, ctx) => {
const ruleName = String(ctx.path[ctx.path.length - 2]).trim();
if (!ruleName) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Role buttons must have names",
});
return z.NEVER;
}
return ruleName;
}),
message: z.union([ message: z.union([
z.strictObject({ z.strictObject({
channel_id: zSnowflake, channel_id: zSnowflake,
@ -66,7 +50,7 @@ const zRoleButtonsConfigItem = z
.refine( .refine(
(parsed) => { (parsed) => {
try { try {
createButtonComponents(parsed); createButtonComponents(parsed, "test"); // We can use any configName here
} catch (err) { } catch (err) {
if (err instanceof TooManyComponentsError) { if (err instanceof TooManyComponentsError) {
return false; return false;