forked from syntaxbullet/aurorabot
fix(economy): improve daily cooldown message and consolidate UserError class
This commit is contained in:
28
.agent/work/completed/004-branded-discord-embeds.md
Normal file
28
.agent/work/completed/004-branded-discord-embeds.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
### Context & Goal
|
||||||
|
|
||||||
|
Enhance the user experience by standardizing the look and feel of Discord embeds. Adding consistent branding like a custom footer (with version info) and using the bot's accent color will make the bot feel more professional.
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- None
|
||||||
|
|
||||||
|
### Affected Files
|
||||||
|
|
||||||
|
- `bot/lib/embeds.ts`: Update standard embed creators.
|
||||||
|
- `shared/lib/constants.ts`: Add branding-related constants (colors, footer text).
|
||||||
|
|
||||||
|
### Technical Constraints & Strategy
|
||||||
|
|
||||||
|
- Implementation: Update `createBaseEmbed` and other helpers to automatically include footers and standard colors.
|
||||||
|
- Use info from `package.json` for versioning in the footer.
|
||||||
|
- Ensure the changes don't break existing layouts where custom colors might be needed.
|
||||||
|
|
||||||
|
### Definition of Done (Binary)
|
||||||
|
|
||||||
|
- [x] All standard embeds now include a consistent footer.
|
||||||
|
- [x] Embeds use a predefined brand color by default.
|
||||||
|
- [x] Version number is automatically pulled for the footer.
|
||||||
|
|
||||||
|
### New Test Files
|
||||||
|
|
||||||
|
- None.
|
||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
} from "discord.js";
|
} from "discord.js";
|
||||||
import { inventoryService } from "@shared/modules/inventory/inventory.service";
|
import { inventoryService } from "@shared/modules/inventory/inventory.service";
|
||||||
import { createSuccessEmbed, createErrorEmbed, createBaseEmbed } from "@lib/embeds";
|
import { createSuccessEmbed, createErrorEmbed, createBaseEmbed } from "@lib/embeds";
|
||||||
import { UserError } from "@/lib/errors";
|
import { UserError } from "@shared/lib/errors";
|
||||||
import { items } from "@db/schema";
|
import { items } from "@db/schema";
|
||||||
import { ilike, isNotNull, and } from "drizzle-orm";
|
import { ilike, isNotNull, and } from "drizzle-orm";
|
||||||
import { DrizzleClient } from "@shared/db/DrizzleClient";
|
import { DrizzleClient } from "@shared/db/DrizzleClient";
|
||||||
@@ -65,10 +65,10 @@ export const listing = createCommand({
|
|||||||
await interaction.editReply({ content: `✅ Listing for **${item.name}** posted in ${targetChannel}.` });
|
await interaction.editReply({ content: `✅ Listing for **${item.name}** posted in ${targetChannel}.` });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error instanceof UserError) {
|
if (error instanceof UserError) {
|
||||||
await interaction.reply({ embeds: [createErrorEmbed(error.message)], ephemeral: true });
|
await interaction.editReply({ embeds: [createErrorEmbed(error.message)] });
|
||||||
} else {
|
} else {
|
||||||
console.error("Error creating listing:", error);
|
console.error("Error creating listing:", error);
|
||||||
await interaction.reply({ embeds: [createErrorEmbed("An unexpected error occurred.")], ephemeral: true });
|
await interaction.editReply({ embeds: [createErrorEmbed("An unexpected error occurred.")] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ import { createCommand } from "@shared/lib/utils";
|
|||||||
import { SlashCommandBuilder } from "discord.js";
|
import { SlashCommandBuilder } from "discord.js";
|
||||||
import { economyService } from "@shared/modules/economy/economy.service";
|
import { economyService } from "@shared/modules/economy/economy.service";
|
||||||
import { createErrorEmbed, createSuccessEmbed } from "@lib/embeds";
|
import { createErrorEmbed, createSuccessEmbed } from "@lib/embeds";
|
||||||
import { UserError } from "@/lib/errors";
|
import { UserError } from "@shared/lib/errors";
|
||||||
|
|
||||||
export const daily = createCommand({
|
export const daily = createCommand({
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
.setName("daily")
|
.setName("daily")
|
||||||
.setDescription("Claim your daily reward"),
|
.setDescription("Claim your daily reward"),
|
||||||
execute: async (interaction) => {
|
execute: async (interaction) => {
|
||||||
|
await interaction.deferReply();
|
||||||
try {
|
try {
|
||||||
const result = await economyService.claimDaily(interaction.user.id);
|
const result = await economyService.claimDaily(interaction.user.id);
|
||||||
|
|
||||||
@@ -21,14 +22,14 @@ export const daily = createCommand({
|
|||||||
)
|
)
|
||||||
.setColor("Gold");
|
.setColor("Gold");
|
||||||
|
|
||||||
await interaction.reply({ embeds: [embed] });
|
await interaction.editReply({ embeds: [embed] });
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error instanceof UserError) {
|
if (error instanceof UserError) {
|
||||||
await interaction.reply({ embeds: [createErrorEmbed(error.message)], ephemeral: true });
|
await interaction.editReply({ embeds: [createErrorEmbed(error.message)] });
|
||||||
} else {
|
} else {
|
||||||
console.error("Error claiming daily:", error);
|
console.error("Error claiming daily:", error);
|
||||||
await interaction.reply({ embeds: [createErrorEmbed("An unexpected error occurred.")], ephemeral: true });
|
await interaction.editReply({ embeds: [createErrorEmbed("An unexpected error occurred.")] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { createCommand } from "@shared/lib/utils";
|
|||||||
import { SlashCommandBuilder } from "discord.js";
|
import { SlashCommandBuilder } from "discord.js";
|
||||||
import { userService } from "@shared/modules/user/user.service";
|
import { userService } from "@shared/modules/user/user.service";
|
||||||
import { createErrorEmbed, createSuccessEmbed } from "@lib/embeds";
|
import { createErrorEmbed, createSuccessEmbed } from "@lib/embeds";
|
||||||
import { UserError } from "@/lib/errors";
|
import { UserError } from "@shared/lib/errors";
|
||||||
import { userTimers, users } from "@db/schema";
|
import { userTimers, users } from "@db/schema";
|
||||||
import { eq, and, sql } from "drizzle-orm";
|
import { eq, and, sql } from "drizzle-orm";
|
||||||
import { DrizzleClient } from "@shared/db/DrizzleClient";
|
import { DrizzleClient } from "@shared/db/DrizzleClient";
|
||||||
@@ -195,10 +195,10 @@ export const exam = createCommand({
|
|||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error instanceof UserError) {
|
if (error instanceof UserError) {
|
||||||
await interaction.reply({ embeds: [createErrorEmbed(error.message)], ephemeral: true });
|
await interaction.editReply({ embeds: [createErrorEmbed(error.message)] });
|
||||||
} else {
|
} else {
|
||||||
console.error("Error in exam command:", error);
|
console.error("Error in exam command:", error);
|
||||||
await interaction.reply({ embeds: [createErrorEmbed("An unexpected error occurred.")], ephemeral: true });
|
await interaction.editReply({ embeds: [createErrorEmbed("An unexpected error occurred.")] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { economyService } from "@shared/modules/economy/economy.service";
|
|||||||
import { userService } from "@shared/modules/user/user.service";
|
import { userService } from "@shared/modules/user/user.service";
|
||||||
import { config } from "@shared/lib/config";
|
import { config } from "@shared/lib/config";
|
||||||
import { createErrorEmbed, createSuccessEmbed } from "@lib/embeds";
|
import { createErrorEmbed, createSuccessEmbed } from "@lib/embeds";
|
||||||
import { UserError } from "@/lib/errors";
|
import { UserError } from "@shared/lib/errors";
|
||||||
|
|
||||||
export const pay = createCommand({
|
export const pay = createCommand({
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { SlashCommandBuilder } from "discord.js";
|
|||||||
import { triviaService } from "@shared/modules/trivia/trivia.service";
|
import { triviaService } from "@shared/modules/trivia/trivia.service";
|
||||||
import { getTriviaQuestionView } from "@/modules/trivia/trivia.view";
|
import { getTriviaQuestionView } from "@/modules/trivia/trivia.view";
|
||||||
import { createErrorEmbed } from "@lib/embeds";
|
import { createErrorEmbed } from "@lib/embeds";
|
||||||
import { UserError } from "@/lib/errors";
|
import { UserError } from "@shared/lib/errors";
|
||||||
import { config } from "@shared/lib/config";
|
import { config } from "@shared/lib/config";
|
||||||
import { TriviaCategory } from "@shared/lib/constants";
|
import { TriviaCategory } from "@shared/lib/constants";
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { userService } from "@shared/modules/user/user.service";
|
|||||||
import { createErrorEmbed } from "@lib/embeds";
|
import { createErrorEmbed } from "@lib/embeds";
|
||||||
import { getItemUseResultEmbed } from "@/modules/inventory/inventory.view";
|
import { getItemUseResultEmbed } from "@/modules/inventory/inventory.view";
|
||||||
import type { ItemUsageData } from "@shared/lib/types";
|
import type { ItemUsageData } from "@shared/lib/types";
|
||||||
import { UserError } from "@/lib/errors";
|
import { UserError } from "@shared/lib/errors";
|
||||||
import { config } from "@shared/lib/config";
|
import { config } from "@shared/lib/config";
|
||||||
|
|
||||||
export const use = createCommand({
|
export const use = createCommand({
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
export class ApplicationError extends Error {
|
|
||||||
constructor(message: string) {
|
|
||||||
super(message);
|
|
||||||
this.name = this.constructor.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UserError extends ApplicationError {
|
|
||||||
constructor(message: string) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SystemError extends ApplicationError {
|
|
||||||
constructor(message: string) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ButtonInteraction, StringSelectMenuInteraction, ModalSubmitInteraction, MessageFlags } from "discord.js";
|
import { ButtonInteraction, StringSelectMenuInteraction, ModalSubmitInteraction, MessageFlags } from "discord.js";
|
||||||
|
|
||||||
import { UserError } from "@lib/errors";
|
import { UserError } from "@shared/lib/errors";
|
||||||
import { createErrorEmbed } from "@lib/embeds";
|
import { createErrorEmbed } from "@lib/embeds";
|
||||||
|
|
||||||
type ComponentInteraction = ButtonInteraction | StringSelectMenuInteraction | ModalSubmitInteraction;
|
type ComponentInteraction = ButtonInteraction | StringSelectMenuInteraction | ModalSubmitInteraction;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ButtonInteraction } from "discord.js";
|
import { ButtonInteraction } from "discord.js";
|
||||||
import { lootdropService } from "@shared/modules/economy/lootdrop.service";
|
import { lootdropService } from "@shared/modules/economy/lootdrop.service";
|
||||||
import { UserError } from "@/lib/errors";
|
import { UserError } from "@shared/lib/errors";
|
||||||
import { getLootdropClaimedMessage } from "./lootdrop.view";
|
import { getLootdropClaimedMessage } from "./lootdrop.view";
|
||||||
|
|
||||||
export async function handleLootdropInteraction(interaction: ButtonInteraction) {
|
export async function handleLootdropInteraction(interaction: ButtonInteraction) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ButtonInteraction, MessageFlags } from "discord.js";
|
import { ButtonInteraction, MessageFlags } from "discord.js";
|
||||||
import { inventoryService } from "@shared/modules/inventory/inventory.service";
|
import { inventoryService } from "@shared/modules/inventory/inventory.service";
|
||||||
import { userService } from "@shared/modules/user/user.service";
|
import { userService } from "@shared/modules/user/user.service";
|
||||||
import { UserError } from "@/lib/errors";
|
import { UserError } from "@shared/lib/errors";
|
||||||
|
|
||||||
export async function handleShopInteraction(interaction: ButtonInteraction) {
|
export async function handleShopInteraction(interaction: ButtonInteraction) {
|
||||||
if (!interaction.customId.startsWith("shop_buy_")) return;
|
if (!interaction.customId.startsWith("shop_buy_")) return;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { config } from "@shared/lib/config";
|
|||||||
import { AuroraClient } from "@/lib/BotClient";
|
import { AuroraClient } from "@/lib/BotClient";
|
||||||
import { buildFeedbackMessage, getFeedbackModal } from "./feedback.view";
|
import { buildFeedbackMessage, getFeedbackModal } from "./feedback.view";
|
||||||
import { FEEDBACK_CUSTOM_IDS, type FeedbackType, type FeedbackData } from "./feedback.types";
|
import { FEEDBACK_CUSTOM_IDS, type FeedbackType, type FeedbackData } from "./feedback.types";
|
||||||
import { UserError } from "@/lib/errors";
|
import { UserError } from "@shared/lib/errors";
|
||||||
|
|
||||||
export const handleFeedbackInteraction = async (interaction: Interaction) => {
|
export const handleFeedbackInteraction = async (interaction: Interaction) => {
|
||||||
// Handle select menu for choosing feedback type
|
// Handle select menu for choosing feedback type
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
import { tradeService } from "@shared/modules/trade/trade.service";
|
import { tradeService } from "@shared/modules/trade/trade.service";
|
||||||
import { inventoryService } from "@shared/modules/inventory/inventory.service";
|
import { inventoryService } from "@shared/modules/inventory/inventory.service";
|
||||||
import { createErrorEmbed, createWarningEmbed, createSuccessEmbed, createInfoEmbed } from "@lib/embeds";
|
import { createErrorEmbed, createWarningEmbed, createSuccessEmbed, createInfoEmbed } from "@lib/embeds";
|
||||||
import { UserError } from "@lib/errors";
|
import { UserError } from "@shared/lib/errors";
|
||||||
import { getTradeDashboard, getTradeMoneyModal, getItemSelectMenu, getTradeCompletedEmbed } from "./trade.view";
|
import { getTradeDashboard, getTradeMoneyModal, getItemSelectMenu, getTradeCompletedEmbed } from "./trade.view";
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ButtonInteraction } from "discord.js";
|
import { ButtonInteraction } from "discord.js";
|
||||||
import { triviaService } from "@shared/modules/trivia/trivia.service";
|
import { triviaService } from "@shared/modules/trivia/trivia.service";
|
||||||
import { getTriviaResultView, getTriviaTimeoutView } from "./trivia.view";
|
import { getTriviaResultView, getTriviaTimeoutView } from "./trivia.view";
|
||||||
import { UserError } from "@/lib/errors";
|
import { UserError } from "@shared/lib/errors";
|
||||||
|
|
||||||
export async function handleTriviaInteraction(interaction: ButtonInteraction) {
|
export async function handleTriviaInteraction(interaction: ButtonInteraction) {
|
||||||
const parts = interaction.customId.split('_');
|
const parts = interaction.customId.split('_');
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { config } from "@shared/lib/config";
|
|||||||
import { getEnrollmentSuccessMessage } from "./enrollment.view";
|
import { getEnrollmentSuccessMessage } from "./enrollment.view";
|
||||||
import { classService } from "@shared/modules/class/class.service";
|
import { classService } from "@shared/modules/class/class.service";
|
||||||
import { userService } from "@shared/modules/user/user.service";
|
import { userService } from "@shared/modules/user/user.service";
|
||||||
import { UserError } from "@/lib/errors";
|
import { UserError } from "@shared/lib/errors";
|
||||||
import { sendWebhookMessage } from "@/lib/webhookUtils";
|
import { sendWebhookMessage } from "@/lib/webhookUtils";
|
||||||
|
|
||||||
export async function handleEnrollmentInteraction(interaction: ButtonInteraction) {
|
export async function handleEnrollmentInteraction(interaction: ButtonInteraction) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "app",
|
"name": "app",
|
||||||
"version": "1.0.0",
|
"version": "1.1.3",
|
||||||
"module": "bot/index.ts",
|
"module": "bot/index.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
|||||||
@@ -88,6 +88,6 @@ export enum TriviaCategory {
|
|||||||
|
|
||||||
export const BRANDING = {
|
export const BRANDING = {
|
||||||
COLOR: 0x00d4ff as const,
|
COLOR: 0x00d4ff as const,
|
||||||
FOOTER_TEXT: 'AuroraBot' as const,
|
FOOTER_TEXT: 'Aurora' as const,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export const economyService = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (cooldown && cooldown.expiresAt > now) {
|
if (cooldown && cooldown.expiresAt > now) {
|
||||||
throw new UserError(`Daily already claimed today. Next claim <t:${Math.floor(cooldown.expiresAt.getTime() / 1000)}:F>`);
|
throw new UserError(`You have already claimed your daily reward today.\nNext claim available: <t:${Math.floor(cooldown.expiresAt.getTime() / 1000)}:F> (<t:${Math.floor(cooldown.expiresAt.getTime() / 1000)}:R>)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user for streak logic
|
// Get user for streak logic
|
||||||
|
|||||||
Reference in New Issue
Block a user