Skip to content

Commit

Permalink
Merge pull request #12 from willy-r/feat/new-commands
Browse files Browse the repository at this point in the history
feat: new commands
  • Loading branch information
willy-r authored Apr 26, 2024
2 parents 6e019a7 + f5a23e3 commit 5b2f13b
Show file tree
Hide file tree
Showing 7 changed files with 253 additions and 9 deletions.
27 changes: 20 additions & 7 deletions src/commands/birthday/add.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,43 @@
const { SlashCommandBuilder } = require('discord.js');

const { createBirthday } = require('../../repositories/birthdayRepository');
const { parseDateStringToDate, isDateInFuture } = require('../../utils/date');

module.exports = {
data: new SlashCommandBuilder()
.setName('add')
.setDescription('Add your birthday to the buddy\'s memory!')
.setDescription('Add your birthday to the Buddy\'s memory!')
.addStringOption((option) =>
option.setName('birthdate')
.setDescription('Your birthdate in the format "YYYY-MM-DD"')
.setDescription('Your birthdate in the format "DD/MM/YYYY" (our little secret)')
.setRequired(true))
.addBooleanOption(option =>
.addBooleanOption((option) =>
option.setName('show-age')
.setDescription('Whether or not the buddy should show age on message\'s birthday, defaults to false')),
.setDescription('Should Buddy show your age? Defaults to False')),

async execute(interaction) {
const hasBirthdayRole = interaction.member.roles.cache.some((role) => {
return process.env.BIRTHDAY_GUILDS_ROLES.split(',').includes(role.id);
});

if (!hasBirthdayRole) {
await interaction.reply('Sorry, but you do not have the right permissions to do that 😥');
return;
}

const birthdate = interaction.options.getString('birthdate');

const dateRegex = /^(0[1-9]|[1-2][0-9]|3[0-1])\/(0[1-9]|1[0-2])\/(\d{4})$/;
if (!dateRegex.test(birthdate)) {
await interaction.reply('Sorry, but birthday should follow the format "DD/MM/YYYY" 😥');
return;
}

const birthdateToDate = parseDateStringToDate(birthdate);
if (isDateInFuture(birthdateToDate)) {
await interaction.reply('Sorry, but birthday should not be in the future 😥');
return;
}

const showAge = interaction.options.getBoolean('show-age') ?? false;
const { id: userId, username } = interaction.user;
const { id: guildId, name: guildName } = interaction.guild;
Expand All @@ -39,10 +52,10 @@ module.exports = {
};
try {
await createBirthday(birthdayData);
await interaction.reply(`Your birthday (${birthdate}) has been added successfully! 🎉`);
await interaction.reply('Your birthday has been added successfully! 🎉');
}
catch (err) {
await interaction.reply(`Failed to add your birthday: ${err.message} 😥`);
await interaction.reply('Failed to add your birthday 😥');
}
},
};
30 changes: 30 additions & 0 deletions src/commands/birthday/remove.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const { SlashCommandBuilder } = require('discord.js');

const { deleteByUserAndGuild } = require('../../repositories/birthdayRepository');

module.exports = {
data: new SlashCommandBuilder()
.setName('remove')
.setDescription('Remove your birthday from Buddy\'s memory'),

async execute(interaction) {
const hasBirthdayRole = interaction.member.roles.cache.some((role) => {
return process.env.BIRTHDAY_GUILDS_ROLES.split(',').includes(role.id);
});
if (!hasBirthdayRole) {
await interaction.reply('Sorry, but you do not have the right permissions to do that 😥');
return;
}

const { id: userId } = interaction.user;
const { id: guildId } = interaction.guild;

try {
await deleteByUserAndGuild(userId, guildId);
await interaction.reply('From now, I can\'t remember your birthday 😥');
}
catch (err) {
await interaction.reply('Failed to remove your birthday from my memory 😥');
}
},
};
44 changes: 44 additions & 0 deletions src/commands/birthday/show.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const { SlashCommandBuilder } = require('discord.js');

const { findByUserAndGuild } = require('../../repositories/birthdayRepository');
const { timeUntilBirthday } = require('../../utils/date');

module.exports = {
data: new SlashCommandBuilder()
.setName('show')
.setDescription('Show your birthday information'),

async execute(interaction) {
const hasBirthdayRole = interaction.member.roles.cache.some((role) => {
return process.env.BIRTHDAY_GUILDS_ROLES.split(',').includes(role.id);
});
if (!hasBirthdayRole) {
await interaction.reply('Sorry, but you do not have the right permissions to do that 😥');
return;
}

const { id: userId } = interaction.user;
const { id: guildId } = interaction.guild;

try {
const birthdayData = await findByUserAndGuild(userId, guildId);
const options = {
month: 'numeric',
day: 'numeric',
};
const formattedDate = birthdayData.birthdate.toLocaleDateString('pt-BR', options);
const formattedTimeUntilBirthday = timeUntilBirthday(birthdayData.birthdate);

let message = `According to my records, your birthday is ${formattedTimeUntilBirthday} away as it falls on ${formattedDate}`;

if (birthdayData.show_age) {
message = `According to my records, your birthday is ${formattedTimeUntilBirthday} away as it falls on ${formattedDate} and you are ${birthdayData.age} years old`;
}

await interaction.reply(`${message}! 🎉`);
}
catch (err) {
await interaction.reply('Failed to show your birthday information 😥');
}
},
};
57 changes: 57 additions & 0 deletions src/commands/birthday/update.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
const { SlashCommandBuilder } = require('discord.js');

const { updateByUserAndGuild } = require('../../repositories/birthdayRepository');
const { parseDateStringToDate, isDateInFuture } = require('../../utils/date');

module.exports = {
data: new SlashCommandBuilder()
.setName('update')
.setDescription('Update your birthday on the Buddy\'s memory!')
.addStringOption((option) =>
option.setName('birthdate')
.setDescription('Your birthdate in the format "DD/MM/YYYY" (our little secret)')
.setRequired(true))
.addBooleanOption((option) =>
option.setName('show-age')
.setDescription('Should Buddy show your age by now? Defaults to False')),

async execute(interaction) {
const hasBirthdayRole = interaction.member.roles.cache.some((role) => {
return process.env.BIRTHDAY_GUILDS_ROLES.split(',').includes(role.id);
});
if (!hasBirthdayRole) {
await interaction.reply('Sorry, but you do not have the right permissions to do that 😥');
return;
}

const birthdate = interaction.options.getString('birthdate');

const dateRegex = /^(0[1-9]|[1-2][0-9]|3[0-1])\/(0[1-9]|1[0-2])\/(\d{4})$/;
if (!dateRegex.test(birthdate)) {
await interaction.reply('Sorry, but birthday should follow the format "DD/MM/YYYY" 😥');
return;
}

const birthdateToDate = parseDateStringToDate(birthdate);
if (isDateInFuture(birthdateToDate)) {
await interaction.reply('Sorry, but birthday should not be in the future 😥');
return;
}

const showAge = interaction.options.getBoolean('show-age') ?? false;
const { id: userId } = interaction.user;
const { id: guildId } = interaction.guild;

const birthdayData = {
show_age: showAge,
birthdate,
};
try {
await updateByUserAndGuild(userId, guildId, birthdayData);
await interaction.reply('Your birthday has been updated successfully! 🎉');
}
catch (err) {
await interaction.reply('Failed to update your birthday 😥');
}
},
};
3 changes: 2 additions & 1 deletion src/commands/utility/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module.exports = {
.setDescription('Provides information about the user'),

async execute(interaction) {
await interaction.reply(`This command was run by ${interaction.user.username}, who joined on ${interaction.member.joinedAt}.`);
const formattedDate = interaction.member.joinedAt.toLocaleString('pt-BR');
await interaction.reply(`This command was run by ${interaction.user.username}, who joined on ${formattedDate}.`);
},
};
49 changes: 48 additions & 1 deletion src/repositories/birthdayRepository.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,21 @@ async function findAllTodayBirthDays(day, month) {
}
}

async function findByUserAndGuild(userId, guildId) {
try {
return await Birthday.findOne({
where: {
userId,
guildId,
},
});
}
catch (err) {
console.error(err);
throw new Error('Failed to show birthday by user and guild');
}
}

async function updateAgeById(birthdayId, byAge) {
try {
return await Birthday.increment(
Expand All @@ -38,12 +53,44 @@ async function updateAgeById(birthdayId, byAge) {
}
catch (err) {
console.error(err);
throw new Error('Failed to update age by id birthday');
throw new Error('Failed to update age by id');
}
}

async function updateByUserAndGuild(userId, guildId, birthdayData) {
try {
return await Birthday.update(
birthdayData,
{ where: { userId, guildId } },
);
}
catch (err) {
console.error(err);
throw new Error('Failed to update birthday by user and guild');
}
}

async function deleteByUserAndGuild(userId, guildId) {
try {
return await Birthday.destroy({
where: {
userId,
guildId,
},
});
}
catch (err) {
console.error(err);
throw new Error('Failed to delete birthday by user and guild');
}
}


module.exports = {
createBirthday,
findAllTodayBirthDays,
findByUserAndGuild,
updateAgeById,
deleteByUserAndGuild,
updateByUserAndGuild,
};
52 changes: 52 additions & 0 deletions src/utils/date.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
function isDateInFuture(date) {
const now = new Date();
return date > now;
}

function parseDateStringToDate(dateString) {
const parts = dateString.split('/');
const year = parseInt(parts[2], 10);
const month = parseInt(parts[1], 10) - 1;
const day = parseInt(parts[0], 10);
return new Date(year, month, day);
}

function timeUntilBirthday(birthday) {
const now = new Date();
const birthdayDate = birthday;

// Check if the birthday has already occurred this year.
if (now.getMonth() > birthdayDate.getMonth() ||
(now.getMonth() === birthdayDate.getMonth() && now.getDate() > birthdayDate.getDate())) {
// If so, set the next birthday to next year.
birthdayDate.setFullYear(now.getFullYear() + 1);
}

const timeDiff = birthdayDate.getTime() - now.getTime();

const months = Math.floor(timeDiff / (1000 * 60 * 60 * 24 * 30));
const days = Math.floor((timeDiff % (1000 * 60 * 60 * 24 * 30)) / (1000 * 60 * 60 * 24));
const hours = Math.floor((timeDiff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));

let result = '';
if (months > 0) {
result += `${months} month${months > 1 ? 's' : ''}, `;
}
if (days > 0) {
result += `${days} day${days > 1 ? 's' : ''}, `;
}
if (hours > 0) {
result += `${hours} hour${hours > 1 ? 's' : ''}`;
}

// Trim any trailing comma and space.
result = result.replace(/,\s*$/, '');

return result;
}

module.exports = {
isDateInFuture,
parseDateStringToDate,
timeUntilBirthday,
};

0 comments on commit 5b2f13b

Please sign in to comment.