Initial Code
This commit is contained in:
252
src/commands/users/brithday.ts
Normal file
252
src/commands/users/brithday.ts
Normal file
@@ -0,0 +1,252 @@
|
||||
import BotCommand from "../../libs/BotCommand";
|
||||
import BotClient from "../../libs/BotClient";
|
||||
import { ChatInputCommandInteraction, MessageFlags } from "discord.js";
|
||||
|
||||
export default class BirthdayCommand extends BotCommand {
|
||||
constructor() {
|
||||
super("bday", "Birthday Command", "/bday");
|
||||
|
||||
this.data.addSubcommand(subcommand =>
|
||||
subcommand.setName("set")
|
||||
.setDescription("Set your birthday")
|
||||
.addNumberOption(option =>
|
||||
option.setName("day")
|
||||
.setDescription("Your birthday day (1-31)")
|
||||
.setRequired(true)
|
||||
)
|
||||
.addNumberOption(option =>
|
||||
option.setName("month")
|
||||
.setDescription("Your birthday month (1-12)")
|
||||
.setRequired(true)
|
||||
)
|
||||
.addStringOption(option =>
|
||||
option.setName("timezone")
|
||||
.setDescription("Your timezone (e.g., 'EST', GMT+2, etc.)")
|
||||
.setRequired(false)
|
||||
)
|
||||
);
|
||||
|
||||
this.data.addSubcommand(subcommand =>
|
||||
subcommand.setName("get")
|
||||
.setDescription("Get your birthday")
|
||||
.addUserOption(option =>
|
||||
option.setName("user")
|
||||
.setDescription("The user whose birthday you want to get")
|
||||
.setRequired(false)
|
||||
)
|
||||
);
|
||||
|
||||
this.data.addSubcommand(subcommand =>
|
||||
subcommand.setName("remove")
|
||||
.setDescription("Remove your birthday")
|
||||
);
|
||||
|
||||
this.data.addSubcommand(subcommand =>
|
||||
subcommand.setName("list")
|
||||
.setDescription("List all birthdays")
|
||||
);
|
||||
}
|
||||
|
||||
dateToEpooch(day: number, month: number, timezone?: string): number {
|
||||
const now = new Date();
|
||||
const currentYear = now.getFullYear();
|
||||
|
||||
let birthdayDate = new Date(currentYear, month - 1, day, 0, 0, 0, 0);
|
||||
|
||||
if (birthdayDate < now) {
|
||||
birthdayDate = new Date(currentYear + 1, month - 1, day, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
if (timezone) {
|
||||
const offsetHours = this.getTimezoneOffset(timezone);
|
||||
birthdayDate.setHours(birthdayDate.getHours() - offsetHours);
|
||||
}
|
||||
|
||||
return Math.floor(birthdayDate.getTime() / 1000);
|
||||
}
|
||||
|
||||
getTimezoneOffset(timezone: string): number {
|
||||
const tz = timezone.toLowerCase();
|
||||
|
||||
// Handle GMT+/-N format
|
||||
const gmtMatch = tz.match(/^gmt([+-])(\d+)$/);
|
||||
if (gmtMatch) {
|
||||
const sign = gmtMatch[1] === '+' ? 1 : -1;
|
||||
const hours = parseInt(gmtMatch[2]);
|
||||
return sign * hours;
|
||||
}
|
||||
|
||||
// Handle UTC+/-N format
|
||||
const utcMatch = tz.match(/^utc([+-])(\d+)$/);
|
||||
if (utcMatch) {
|
||||
const sign = utcMatch[1] === '+' ? 1 : -1;
|
||||
const hours = parseInt(utcMatch[2]);
|
||||
return sign * hours;
|
||||
}
|
||||
|
||||
// Predefined timezone offsets (in hours from UTC)
|
||||
const offsets: { [key: string]: number } = {
|
||||
"est": -5, // Eastern Standard Time
|
||||
"edt": -4, // Eastern Daylight Time
|
||||
"cst": -6, // Central Standard Time
|
||||
"cdt": -5, // Central Daylight Time
|
||||
"mst": -7, // Mountain Standard Time
|
||||
"mdt": -6, // Mountain Daylight Time
|
||||
"pst": -8, // Pacific Standard Time
|
||||
"pdt": -7, // Pacific Daylight Time
|
||||
"gmt": 0, // Greenwich Mean Time
|
||||
"utc": 0 // Coordinated Universal Time
|
||||
};
|
||||
|
||||
return offsets[tz] || 0; // Default to UTC if not found
|
||||
}
|
||||
|
||||
async setCommand(Discord: any, client: BotClient, interaction: ChatInputCommandInteraction): Promise<any> {
|
||||
const day = interaction.options.getNumber("day")!;
|
||||
const month = interaction.options.getNumber("month")!;
|
||||
|
||||
let timezone = interaction.options.getString("timezone");
|
||||
timezone = timezone ? timezone.trim() : null;
|
||||
timezone = timezone ? timezone.toLowerCase() : null;
|
||||
|
||||
// Validate timezone format to lowercase alphanumeric and special characters
|
||||
if (timezone && !/^[a-zA-Z0-9+_-]+$/.test(timezone)) {
|
||||
return interaction.reply({
|
||||
content: "Invalid timezone format. Please use alphanumeric characters, plus (+), underscore (_), or hyphen (-).",
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
if (isNaN(day) || isNaN(month)) {
|
||||
return interaction.reply({
|
||||
content: "Invalid date provided. Please ensure the day and month are numbers.",
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
if (day < 1 || day > 31 || month < 1 || month > 12) {
|
||||
return interaction.reply({
|
||||
content: "Invalid date provided. Please ensure the day is between 1-31 and the month is between 1-12.",
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
client.config.setBday(interaction.user.id, { day, month, timezone: timezone || undefined });
|
||||
|
||||
return interaction.reply({
|
||||
content: `Your birthday has been set to ${day}/${month} ${timezone ? `in timezone ${timezone}` : ''}.\nYour next birthday will be on <t:${this.dateToEpooch(day, month, timezone || undefined)}:D>.`,
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
async getCommand(Discord: any, client: BotClient, interaction: ChatInputCommandInteraction): Promise<any> {
|
||||
const user = interaction.options.getUser("user") || interaction.user;
|
||||
const bday = client.config.getBday(user.id);
|
||||
|
||||
if (!bday) {
|
||||
return interaction.reply({
|
||||
content: `\`${user.username}\` has not set a birthday.`,
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
const timezone = bday.timezone ? ` in timezone ${bday.timezone}` : '';
|
||||
return interaction.reply({
|
||||
content: `
|
||||
\`${user.username}\`'s birthday is set to ${bday.month}/${bday.day}${timezone}.\nYour next birthday will be on <t:${this.dateToEpooch(bday.day, bday.month, bday.timezone)}:D>.
|
||||
`,
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
async removeCommand(Discord: any, client: BotClient, interaction: ChatInputCommandInteraction): Promise<any> {
|
||||
const user = interaction.user;
|
||||
const bday = client.config.getBday(user.id);
|
||||
if (!bday) {
|
||||
return interaction.reply({
|
||||
content: `\`${user.username}\`, you have not set a birthday to remove.`,
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
client.config.removeBday(user.id);
|
||||
return interaction.reply({
|
||||
content: `\`${user.username}\`, your birthday has been removed.`,
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
async listCommand(Discord: any, client: BotClient, interaction: ChatInputCommandInteraction): Promise<any> {
|
||||
const bdays = client.config.getBdays();
|
||||
if (!bdays || Object.keys(bdays).length === 0) {
|
||||
return interaction.reply({
|
||||
content: "No birthdays have been set yet.",
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
// Upcoming birthdays
|
||||
let response = "";
|
||||
const today = new Date();
|
||||
const upcoming = Object.entries(bdays).map(([userId, bday]) => {
|
||||
const nextBirthday = new Date(today.getFullYear(), bday.month - 1, bday.day);
|
||||
if (nextBirthday < today) {
|
||||
nextBirthday.setFullYear(today.getFullYear() + 1); // Move to next year if birthday has passed
|
||||
}
|
||||
return { userId, bday, nextBirthday };
|
||||
}).sort((a, b) => a.nextBirthday.getTime() - b.nextBirthday.getTime());
|
||||
|
||||
// Filter out birthdays that are more than 30 days away
|
||||
const thirtyDaysFromNow = new Date(today);
|
||||
thirtyDaysFromNow.setDate(today.getDate() + 30);
|
||||
const filteredUpcoming = upcoming.filter(b => b.nextBirthday <= thirtyDaysFromNow);
|
||||
if (filteredUpcoming.length === 0) {
|
||||
response = "No upcoming birthdays within the next 30 days.";
|
||||
return interaction.reply({
|
||||
content: response,
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
for (const { userId, bday, nextBirthday } of upcoming) {
|
||||
const user = await client.users.fetch(userId);
|
||||
response += `\`${user.username}\`: <t:${Math.floor(nextBirthday.getTime() / 1000)}:D>\n`;
|
||||
}
|
||||
|
||||
if (upcoming.length === 0) {
|
||||
response = "No upcoming birthdays.";
|
||||
}
|
||||
|
||||
const embed = new Discord.EmbedBuilder()
|
||||
.setTitle("Upcoming Birthdays")
|
||||
.setDescription(response)
|
||||
.setColor("Blue")
|
||||
.setTimestamp();
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [embed],
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
override async execute(Discord: any, client: BotClient, interaction: ChatInputCommandInteraction): Promise<any> {
|
||||
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
|
||||
switch (subcommand) {
|
||||
case "set":
|
||||
return this.setCommand(Discord, client, interaction);
|
||||
case "get":
|
||||
return this.getCommand(Discord, client, interaction);
|
||||
case "remove":
|
||||
return this.removeCommand(Discord, client, interaction);
|
||||
case "list":
|
||||
return this.listCommand(Discord, client, interaction);
|
||||
default:
|
||||
return interaction.reply({
|
||||
content: "Unknown subcommand.",
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user