forked from syntaxbullet/AuroraBot-discord
feat: Add color role item effect with role swapping and implement item consumption toggle.
This commit is contained in:
@@ -8,6 +8,7 @@ import { eq, and, like } from "drizzle-orm";
|
||||
import { DrizzleClient } from "@/lib/DrizzleClient";
|
||||
import type { ItemUsageData } from "@/lib/types";
|
||||
import { UserError } from "@/lib/errors";
|
||||
import { config } from "@/lib/config";
|
||||
|
||||
export const use = createCommand({
|
||||
data: new SlashCommandBuilder()
|
||||
@@ -31,11 +32,18 @@ export const use = createCommand({
|
||||
const usageData = result.usageData;
|
||||
if (usageData) {
|
||||
for (const effect of usageData.effects) {
|
||||
if (effect.type === 'TEMP_ROLE') {
|
||||
if (effect.type === 'TEMP_ROLE' || effect.type === 'COLOR_ROLE') {
|
||||
try {
|
||||
const member = await interaction.guild?.members.fetch(user.id);
|
||||
if (member) {
|
||||
await member.roles.add(effect.roleId);
|
||||
if (effect.type === 'TEMP_ROLE') {
|
||||
await member.roles.add(effect.roleId);
|
||||
} else if (effect.type === 'COLOR_ROLE') {
|
||||
// Remove existing color roles
|
||||
const rolesToRemove = config.colorRoles.filter(r => member.roles.cache.has(r));
|
||||
if (rolesToRemove.length > 0) await member.roles.remove(rolesToRemove);
|
||||
await member.roles.add(effect.roleId);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to assign role in /use command:", e);
|
||||
|
||||
@@ -48,6 +48,7 @@ export interface GameConfigType {
|
||||
};
|
||||
studentRole: string;
|
||||
visitorRole: string;
|
||||
colorRoles: string[];
|
||||
welcomeChannelId?: string;
|
||||
welcomeMessage?: string;
|
||||
}
|
||||
@@ -111,6 +112,7 @@ const configSchema = z.object({
|
||||
}),
|
||||
studentRole: z.string(),
|
||||
visitorRole: z.string(),
|
||||
colorRoles: z.array(z.string()).default([]),
|
||||
welcomeChannelId: z.string().optional(),
|
||||
welcomeMessage: z.string().optional()
|
||||
});
|
||||
|
||||
@@ -18,7 +18,8 @@ export type ItemEffect =
|
||||
| { type: 'ADD_BALANCE'; amount: number }
|
||||
| { type: 'XP_BOOST'; multiplier: number; durationSeconds?: number; durationMinutes?: number; durationHours?: number }
|
||||
| { type: 'TEMP_ROLE'; roleId: string; durationSeconds?: number; durationMinutes?: number; durationHours?: number }
|
||||
| { type: 'REPLY_MESSAGE'; message: string };
|
||||
| { type: 'REPLY_MESSAGE'; message: string }
|
||||
| { type: 'COLOR_ROLE'; roleId: string };
|
||||
|
||||
export interface ItemUsageData {
|
||||
consume: boolean;
|
||||
|
||||
@@ -44,6 +44,7 @@ const getEffectTypeOptions = () => [
|
||||
{ label: "Reply Message", value: "REPLY_MESSAGE", description: "Bot replies with a message" },
|
||||
{ label: "XP Boost", value: "XP_BOOST", description: "Temporarily boosts XP gain" },
|
||||
{ label: "Temp Role", value: "TEMP_ROLE", description: "Gives a temporary role" },
|
||||
{ label: "Color Role", value: "COLOR_ROLE", description: "Equips a permanent color role (swaps)" },
|
||||
];
|
||||
|
||||
// --- Render ---
|
||||
@@ -72,6 +73,7 @@ export const renderWizard = (userId: string, isDraft = true) => {
|
||||
{ name: "General", value: `**Type:** ${draft.type}\n**Rarity:** ${draft.rarity}\n**Desc:** ${draft.description}`, inline: true },
|
||||
{ name: "Economy", value: `**Price:** ${draft.price ? `${draft.price} 🪙` : "Not for sale"}`, inline: true },
|
||||
{ name: "Visuals", value: `**Icon:** ${draft.iconUrl ? "✅ Set" : "❌"}\n**Image:** ${draft.imageUrl ? "✅ Set" : "❌"}`, inline: true },
|
||||
{ name: "Usage", value: `**Consume:** ${draft.usageData.consume ? "✅ Yes" : "❌ No"}`, inline: true },
|
||||
);
|
||||
|
||||
// Effects Display
|
||||
@@ -97,6 +99,7 @@ export const renderWizard = (userId: string, isDraft = true) => {
|
||||
const row2 = new ActionRowBuilder<MessageActionRowComponentBuilder>()
|
||||
.addComponents(
|
||||
new ButtonBuilder().setCustomId("createitem_addeffect_start").setLabel("Add Effect").setStyle(ButtonStyle.Primary).setEmoji("✨"),
|
||||
new ButtonBuilder().setCustomId("createitem_toggle_consume").setLabel(`Consume: ${draft.usageData.consume ? "ON" : "OFF"}`).setStyle(ButtonStyle.Secondary).setEmoji("🔄"),
|
||||
new ButtonBuilder().setCustomId("createitem_save").setLabel("Save Item").setStyle(ButtonStyle.Success).setEmoji("💾"),
|
||||
new ButtonBuilder().setCustomId("createitem_cancel").setLabel("Cancel").setStyle(ButtonStyle.Danger).setEmoji("✖️")
|
||||
);
|
||||
@@ -241,12 +244,25 @@ export const handleItemWizardInteraction = async (interaction: Interaction) => {
|
||||
new ActionRowBuilder<TextInputBuilder>().addComponents(new TextInputBuilder().setCustomId("role_id").setLabel("Role ID").setStyle(TextInputStyle.Short).setRequired(true)),
|
||||
new ActionRowBuilder<TextInputBuilder>().addComponents(new TextInputBuilder().setCustomId("duration").setLabel("Duration (Seconds)").setStyle(TextInputStyle.Short).setRequired(true).setValue("3600"))
|
||||
);
|
||||
} else if (effectType === "COLOR_ROLE") {
|
||||
modal.addComponents(
|
||||
new ActionRowBuilder<TextInputBuilder>().addComponents(new TextInputBuilder().setCustomId("role_id").setLabel("Role ID").setStyle(TextInputStyle.Short).setRequired(true))
|
||||
);
|
||||
}
|
||||
|
||||
await interaction.showModal(modal);
|
||||
return;
|
||||
}
|
||||
|
||||
// Toggle Consume
|
||||
if (interaction.customId === "createitem_toggle_consume") {
|
||||
if (!interaction.isButton()) return;
|
||||
draft.usageData.consume = !draft.usageData.consume;
|
||||
const payload = renderWizard(userId);
|
||||
await interaction.update(payload);
|
||||
return;
|
||||
}
|
||||
|
||||
// 6. Handle Modal Submits
|
||||
if (interaction.isModalSubmit()) {
|
||||
if (interaction.customId === "createitem_modal_details") {
|
||||
@@ -284,6 +300,10 @@ export const handleItemWizardInteraction = async (interaction: Interaction) => {
|
||||
const duration = parseInt(interaction.fields.getTextInputValue("duration"));
|
||||
if (roleId && !isNaN(duration)) effect = { type: "TEMP_ROLE", roleId: roleId, durationSeconds: duration };
|
||||
}
|
||||
else if (type === "COLOR_ROLE") {
|
||||
const roleId = interaction.fields.getTextInputValue("role_id");
|
||||
if (roleId) effect = { type: "COLOR_ROLE", roleId: roleId };
|
||||
}
|
||||
|
||||
if (effect) {
|
||||
draft.usageData.effects.push(effect);
|
||||
|
||||
@@ -209,6 +209,9 @@ export const inventoryService = {
|
||||
// Actual role assignment happens in the Command layer
|
||||
results.push(`Temporary Role granted for ${Math.floor(roleDuration / 60)}m`);
|
||||
break;
|
||||
case 'COLOR_ROLE':
|
||||
results.push("Color Role Equipped");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user