From 306b654e75e4829e45a7b0dac848b3632a69f2b0 Mon Sep 17 00:00:00 2001 From: Luca Montaigut Date: Sat, 23 Mar 2024 17:44:05 +0100 Subject: [PATCH 01/11] refactor: mutualized channel logic --- src/core/cache.ts | 1 + src/helpers/channels.ts | 66 +++++++++++++++++++ .../cookieHunter/cookieHunter.module.ts | 38 +++++++++++ src/modules/quoiFeur/quoiFeur.helpers.ts | 58 +--------------- src/modules/quoiFeur/quoiFeur.module.ts | 15 +++-- 5 files changed, 114 insertions(+), 64 deletions(-) create mode 100644 src/helpers/channels.ts create mode 100644 src/modules/cookieHunter/cookieHunter.module.ts diff --git a/src/core/cache.ts b/src/core/cache.ts index f84ecadf..ddfddf67 100644 --- a/src/core/cache.ts +++ b/src/core/cache.ts @@ -24,6 +24,7 @@ interface CacheEntries { lobbyIds: string[]; onDemandChannels: string[]; quoiFeurChannels: string[]; + cookieHunterChannels: string[]; recurringMessages: { id: string; channelId: string; frequency: Frequency; message: string }[]; } diff --git a/src/helpers/channels.ts b/src/helpers/channels.ts new file mode 100644 index 00000000..18be81fb --- /dev/null +++ b/src/helpers/channels.ts @@ -0,0 +1,66 @@ +import { + ChannelType, + type ChatInputCommandInteraction, + type DMChannel, + type NonThreadGuildBasedChannel, +} from 'discord.js'; +import { cache } from '../core/cache'; + +export const addChannelInCache = async ( + interaction: ChatInputCommandInteraction, + featureName: string, + cacheKey: 'quoiFeurChannels' | 'cookieHunterChannels', +) => { + const { channel } = interaction; + if (!channel || !channel.isTextBased() || channel.type !== ChannelType.GuildText) return; + + const channels = await cache.get(cacheKey, []); + if (channels.includes(channel.id)) { + await interaction.reply({ + content: `${featureName} is already enabled in this channel`, + ephemeral: true, + }); + return; + } + + await cache.set(cacheKey, [...channels, channel.id]); + await interaction.reply({ content: `${featureName} enabled in this channel`, ephemeral: true }); +}; + +export const removeChannelFromChache = async ( + interaction: ChatInputCommandInteraction, + featureName: string, + cacheKey: 'quoiFeurChannels' | 'cookieHunterChannels', +) => { + const { channel } = interaction; + if (!channel || !channel.isTextBased() || channel.type !== ChannelType.GuildText) return; + + const channels = await cache.get(cacheKey, []); + if (!channels.includes(channel.id)) { + await interaction.reply({ + content: `${featureName} is not enabled in this channel`, + ephemeral: true, + }); + return; + } + + await cache.set( + cacheKey, + channels.filter((channelId) => channelId !== channel.id), + ); + await interaction.reply({ content: `${featureName} disabled in this channel`, ephemeral: true }); +}; + +export const cleanCacheOnChannelDelete = async ( + channel: DMChannel | NonThreadGuildBasedChannel, + cacheKey: 'quoiFeurChannels' | 'cookieHunterChannels', +) => { + const { id } = channel; + const channels = await cache.get(cacheKey, []); + if (!channels.includes(id)) return; + + await cache.set( + cacheKey, + channels.filter((channelId) => channelId !== id), + ); +}; diff --git a/src/modules/cookieHunter/cookieHunter.module.ts b/src/modules/cookieHunter/cookieHunter.module.ts new file mode 100644 index 00000000..01e28f2e --- /dev/null +++ b/src/modules/cookieHunter/cookieHunter.module.ts @@ -0,0 +1,38 @@ +import { PermissionFlagsBits, SlashCommandBuilder } from 'discord.js'; + +import { createModule } from '../../core/createModule'; +import { + addChannelInCache, + cleanCacheOnChannelDelete, + removeChannelFromChache, +} from '../../helpers/channels'; + +export const cookieHunter = createModule({ + name: 'cookieHunter', + slashCommands: () => [ + { + schema: new SlashCommandBuilder() + .setName('cookie-hunter') + .setDescription('Cookie hunting game for the server') + .addSubcommand((subcommand) => + subcommand.setName('enable').setDescription('Enable the cookie hunt in the channel'), + ) + .addSubcommand((subcommand) => + subcommand.setName('disable').setDescription('Disable the cookie hunt in the channel'), + ) + .setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages) + .toJSON(), + handler: { + enable: (interaction) => + addChannelInCache(interaction, 'Cookie Hunter', 'cookieHunterChannels'), + disable: (interaction) => + removeChannelFromChache(interaction, 'Cookie Hunter', 'cookieHunterChannels'), + }, + }, + ], + eventHandlers: () => ({ + // ready: startHunt, + channelDelete: (channel) => cleanCacheOnChannelDelete(channel, 'cookieHunterChannels'), + }), + intents: ['Guilds'], +}); diff --git a/src/modules/quoiFeur/quoiFeur.helpers.ts b/src/modules/quoiFeur/quoiFeur.helpers.ts index 417c7bbc..1d05f8af 100644 --- a/src/modules/quoiFeur/quoiFeur.helpers.ts +++ b/src/modules/quoiFeur/quoiFeur.helpers.ts @@ -1,10 +1,4 @@ -import { - ChannelType, - type ChatInputCommandInteraction, - DMChannel, - type Message, - type NonThreadGuildBasedChannel, -} from 'discord.js'; +import { ChannelType, type Message } from 'discord.js'; import { cache } from '../../core/cache'; import type { Emoji } from '../../helpers/emoji'; @@ -61,53 +55,3 @@ export const reactOnEndWithQuoi = async (message: Message) => { await reactWithFeur(message); }; - -export const addQuoiFeurToChannel = async (interaction: ChatInputCommandInteraction) => { - const { channel } = interaction; - if (!channel || !channel.isTextBased() || channel.type !== ChannelType.GuildText) return; - - const channels = await cache.get('quoiFeurChannels', []); - if (channels.includes(channel.id)) { - await interaction.reply({ - content: 'Quoi-feur is already enabled in this channel', - ephemeral: true, - }); - return; - } - - await cache.set('quoiFeurChannels', [...channels, channel.id]); - await interaction.reply({ content: 'Quoi-feur enabled in this channel', ephemeral: true }); -}; - -export const removeQuoiFeurFromChannel = async (interaction: ChatInputCommandInteraction) => { - const { channel } = interaction; - if (!channel || !channel.isTextBased() || channel.type !== ChannelType.GuildText) return; - - const channels = await cache.get('quoiFeurChannels', []); - if (!channels.includes(channel.id)) { - await interaction.reply({ - content: 'Quoi-feur is not enabled in this channel', - ephemeral: true, - }); - return; - } - - await cache.set( - 'quoiFeurChannels', - channels.filter((channelId) => channelId !== channel.id), - ); - await interaction.reply({ content: 'Quoi-feur disabled in this channel', ephemeral: true }); -}; - -export const cleanCacheOnChannelDelete = async ( - channel: DMChannel | NonThreadGuildBasedChannel, -) => { - const { id } = channel; - const channels = await cache.get('quoiFeurChannels', []); - if (!channels.includes(id)) return; - - await cache.set( - 'quoiFeurChannels', - channels.filter((channelId) => channelId !== id), - ); -}; diff --git a/src/modules/quoiFeur/quoiFeur.module.ts b/src/modules/quoiFeur/quoiFeur.module.ts index 4274f3ad..dfeaa985 100644 --- a/src/modules/quoiFeur/quoiFeur.module.ts +++ b/src/modules/quoiFeur/quoiFeur.module.ts @@ -1,12 +1,12 @@ import { PermissionFlagsBits, SlashCommandBuilder } from 'discord.js'; import { createModule } from '../../core/createModule'; +import { reactOnEndWithQuoi } from './quoiFeur.helpers'; import { - addQuoiFeurToChannel, + addChannelInCache, cleanCacheOnChannelDelete, - reactOnEndWithQuoi, - removeQuoiFeurFromChannel, -} from './quoiFeur.helpers'; + removeChannelFromChache, +} from '../../helpers/channels'; export const quoiFeur = createModule({ name: 'quoiFeur', @@ -24,14 +24,15 @@ export const quoiFeur = createModule({ .setDefaultMemberPermissions(PermissionFlagsBits.ManageChannels) .toJSON(), handler: { - enable: addQuoiFeurToChannel, - disable: removeQuoiFeurFromChannel, + enable: (interaction) => addChannelInCache(interaction, 'Quoi-Feur', 'quoiFeurChannels'), + disable: (interaction) => + removeChannelFromChache(interaction, 'Quoi-Feur', 'quoiFeurChannels'), }, }, ], eventHandlers: () => ({ messageCreate: reactOnEndWithQuoi, - channelDelete: cleanCacheOnChannelDelete, + channelDelete: (channel) => cleanCacheOnChannelDelete(channel, 'quoiFeurChannels'), }), intents: ['Guilds', 'GuildMessages', 'MessageContent', 'GuildMessageReactions'], }); From 2cab2df53ff532a6f44db5e09f6efb94e4f4c9f5 Mon Sep 17 00:00:00 2001 From: Luca Montaigut Date: Sat, 23 Mar 2024 18:12:19 +0100 Subject: [PATCH 02/11] feat: lauch cookie hunter --- src/core/cache.ts | 1 + src/main.ts | 2 + .../cookieHunter/cookieHunter.helpers.ts | 39 +++++++++++++++++++ .../cookieHunter/cookieHunter.module.ts | 3 +- 4 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/modules/cookieHunter/cookieHunter.helpers.ts diff --git a/src/core/cache.ts b/src/core/cache.ts index ddfddf67..783bfcc3 100644 --- a/src/core/cache.ts +++ b/src/core/cache.ts @@ -25,6 +25,7 @@ interface CacheEntries { onDemandChannels: string[]; quoiFeurChannels: string[]; cookieHunterChannels: string[]; + currentHuntMessageId: string; recurringMessages: { id: string; channelId: string; frequency: Frequency; message: string }[]; } diff --git a/src/main.ts b/src/main.ts index 38851607..0a491c5c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,6 +10,7 @@ import { fixEmbedTwitterVideo } from './modules/fixEmbedTwitterVideo/fixEmbedTwi import { quoiFeur } from './modules/quoiFeur/quoiFeur.module'; import { recurringMessage } from './modules/recurringMessage/recurringMessage.module'; import { voiceOnDemand } from './modules/voiceOnDemand/voiceOnDemand.module'; +import { cookieHunter } from './modules/cookieHunter/cookieHunter.module'; const modules = [ fart, @@ -18,6 +19,7 @@ const modules = [ quoiFeur, recurringMessage, fixEmbedTwitterVideo, + cookieHunter, ]; const createdModules = await createAllModules(modules); diff --git a/src/modules/cookieHunter/cookieHunter.helpers.ts b/src/modules/cookieHunter/cookieHunter.helpers.ts new file mode 100644 index 00000000..e612bd5e --- /dev/null +++ b/src/modules/cookieHunter/cookieHunter.helpers.ts @@ -0,0 +1,39 @@ +import { CronJob } from 'cron'; +import { cache } from '../../core/cache'; +import type { Client } from 'discord.js'; + +const IT_IS_SNACK_TIME = '0 30 16 * * *'; // 4:30pm every day + +let jobCurrentlyRunning: CronJob | null = null; + +export const startHunting = async (client: Client) => { + console.log('Cookie hunter started'); + if (jobCurrentlyRunning !== null) { + // needed in case that the bot fire multiple ready event + jobCurrentlyRunning.stop(); + } + jobCurrentlyRunning = new CronJob( + IT_IS_SNACK_TIME, + () => sendMessageInRandomChannel(client), + null, + true, + 'Europe/Paris', + ); +}; + +const sendMessageInRandomChannel = async (client: Client) => { + const channel = await cache.get('cookieHunterChannels', []); + if (!channel.length) return; + + const randomChannel = channel[Math.floor(Math.random() * channel.length)]; + if (!randomChannel) return; + + const channelToSend = await client.channels.fetch(randomChannel); + + if (!channelToSend || !channelToSend.isTextBased()) return; + + const cookieMessage = await channelToSend.send('👵 Qui veut des cookies ? 🍪'); + await cache.set('currentHuntMessageId', cookieMessage.id); + cookieMessage.react('🥛'); + cookieMessage.react('🍪'); +}; diff --git a/src/modules/cookieHunter/cookieHunter.module.ts b/src/modules/cookieHunter/cookieHunter.module.ts index 01e28f2e..1a3d45e2 100644 --- a/src/modules/cookieHunter/cookieHunter.module.ts +++ b/src/modules/cookieHunter/cookieHunter.module.ts @@ -6,6 +6,7 @@ import { cleanCacheOnChannelDelete, removeChannelFromChache, } from '../../helpers/channels'; +import { startHunting } from './cookieHunter.helpers'; export const cookieHunter = createModule({ name: 'cookieHunter', @@ -31,7 +32,7 @@ export const cookieHunter = createModule({ }, ], eventHandlers: () => ({ - // ready: startHunt, + ready: startHunting, channelDelete: (channel) => cleanCacheOnChannelDelete(channel, 'cookieHunterChannels'), }), intents: ['Guilds'], From a9b06c44fa9eea328ed76cf0e258c17662ed3cd9 Mon Sep 17 00:00:00 2001 From: Luca Montaigut Date: Sat, 23 Mar 2024 18:45:18 +0100 Subject: [PATCH 03/11] feat: count collected cookies by user --- src/core/cache.ts | 1 + src/helpers/timeConstants.ts | 1 + .../cookieHunter/cookieHunter.helpers.ts | 46 +++++++++++++++++-- .../cookieHunter/cookieHunter.module.ts | 3 +- src/modules/quoiFeur/quoiFeur.helpers.ts | 3 +- 5 files changed, 47 insertions(+), 7 deletions(-) create mode 100644 src/helpers/timeConstants.ts diff --git a/src/core/cache.ts b/src/core/cache.ts index 783bfcc3..025fef12 100644 --- a/src/core/cache.ts +++ b/src/core/cache.ts @@ -26,6 +26,7 @@ interface CacheEntries { quoiFeurChannels: string[]; cookieHunterChannels: string[]; currentHuntMessageId: string; + cookieHunterDailyCount: Record; recurringMessages: { id: string; channelId: string; frequency: Frequency; message: string }[]; } diff --git a/src/helpers/timeConstants.ts b/src/helpers/timeConstants.ts new file mode 100644 index 00000000..abf5ee18 --- /dev/null +++ b/src/helpers/timeConstants.ts @@ -0,0 +1 @@ +export const ONE_MINUTE = 1 * 60 * 1000; diff --git a/src/modules/cookieHunter/cookieHunter.helpers.ts b/src/modules/cookieHunter/cookieHunter.helpers.ts index e612bd5e..1a4e7c7e 100644 --- a/src/modules/cookieHunter/cookieHunter.helpers.ts +++ b/src/modules/cookieHunter/cookieHunter.helpers.ts @@ -1,6 +1,13 @@ import { CronJob } from 'cron'; import { cache } from '../../core/cache'; -import type { Client } from 'discord.js'; +import type { + Client, + MessageReaction, + PartialMessageReaction, + PartialUser, + User, +} from 'discord.js'; +import { ONE_MINUTE } from '../../helpers/timeConstants'; const IT_IS_SNACK_TIME = '0 30 16 * * *'; // 4:30pm every day @@ -31,9 +38,40 @@ const sendMessageInRandomChannel = async (client: Client) => { const channelToSend = await client.channels.fetch(randomChannel); if (!channelToSend || !channelToSend.isTextBased()) return; - - const cookieMessage = await channelToSend.send('👵 Qui veut des cookies ? 🍪'); + const cookieMessage = await channelToSend.send('**👵 Qui veut des cookies ?**'); await cache.set('currentHuntMessageId', cookieMessage.id); + await cache.set('cookieHunterDailyCount', {}); cookieMessage.react('🥛'); - cookieMessage.react('🍪'); + cookieMessage.react('🍪'); // 1 point for grandma here, she beats everyone who doesn't find her + + setTimeout(() => { + cookieMessage.delete(); + logDailyCount(); + // TODO : add the daily count into the global scoreboard + }, ONE_MINUTE); +}; + +export const countCookies = async ( + reaction: MessageReaction | PartialMessageReaction, + user: User | PartialUser, +) => { + const currentHuntMessageId = await cache.get('currentHuntMessageId'); + if ( + !currentHuntMessageId || + reaction.message.id !== currentHuntMessageId || + reaction.emoji.name !== '🍪' + ) + return; + + const cookieHunterDailyCount = await cache.get('cookieHunterDailyCount', {}); + const userDailyCount = cookieHunterDailyCount[user.id] || 0; + const newDailyCount = { ...cookieHunterDailyCount, [user.id]: userDailyCount + 1 }; + await cache.set('cookieHunterDailyCount', newDailyCount); +}; + +const logDailyCount = async () => { + const cookieHunterDailyCount = await cache.get('cookieHunterDailyCount', {}); + console.log(cookieHunterDailyCount); + // TODO : command to log the daily count in a channel in order to keep track of + // where and when the message is sent, and who reacted to it }; diff --git a/src/modules/cookieHunter/cookieHunter.module.ts b/src/modules/cookieHunter/cookieHunter.module.ts index 1a3d45e2..48aebad4 100644 --- a/src/modules/cookieHunter/cookieHunter.module.ts +++ b/src/modules/cookieHunter/cookieHunter.module.ts @@ -6,7 +6,7 @@ import { cleanCacheOnChannelDelete, removeChannelFromChache, } from '../../helpers/channels'; -import { startHunting } from './cookieHunter.helpers'; +import { countCookies, startHunting } from './cookieHunter.helpers'; export const cookieHunter = createModule({ name: 'cookieHunter', @@ -33,6 +33,7 @@ export const cookieHunter = createModule({ ], eventHandlers: () => ({ ready: startHunting, + messageReactionAdd: countCookies, channelDelete: (channel) => cleanCacheOnChannelDelete(channel, 'cookieHunterChannels'), }), intents: ['Guilds'], diff --git a/src/modules/quoiFeur/quoiFeur.helpers.ts b/src/modules/quoiFeur/quoiFeur.helpers.ts index 1d05f8af..f3906bc1 100644 --- a/src/modules/quoiFeur/quoiFeur.helpers.ts +++ b/src/modules/quoiFeur/quoiFeur.helpers.ts @@ -9,8 +9,7 @@ import { removeNonASCII, removePunctuation, } from '../../helpers/regex.helper'; - -const ONE_MINUTE = 1 * 60 * 1000; +import { ONE_MINUTE } from '../../helpers/timeConstants'; const quoiDetectorRegex = /\bquoi\s*$/i; const endWithQuoi = (text: string) => From 12410cfe1e3f1764ec757bb6626fcff9bbf55a8f Mon Sep 17 00:00:00 2001 From: Luca Montaigut Date: Sat, 23 Mar 2024 19:12:59 +0100 Subject: [PATCH 04/11] feat: add start command --- src/modules/cookieHunter/cookieHunter.module.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/cookieHunter/cookieHunter.module.ts b/src/modules/cookieHunter/cookieHunter.module.ts index 48aebad4..0ea30abc 100644 --- a/src/modules/cookieHunter/cookieHunter.module.ts +++ b/src/modules/cookieHunter/cookieHunter.module.ts @@ -15,6 +15,9 @@ export const cookieHunter = createModule({ schema: new SlashCommandBuilder() .setName('cookie-hunter') .setDescription('Cookie hunting game for the server') + .addSubcommand((subcommand) => + subcommand.setName('start').setDescription('Start the cookie hunt'), + ) .addSubcommand((subcommand) => subcommand.setName('enable').setDescription('Enable the cookie hunt in the channel'), ) @@ -24,6 +27,7 @@ export const cookieHunter = createModule({ .setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages) .toJSON(), handler: { + start: (interaction) => startHunting(interaction.client), enable: (interaction) => addChannelInCache(interaction, 'Cookie Hunter', 'cookieHunterChannels'), disable: (interaction) => From 9f6f7d223689cbb0426497ff9e6d07e05ed4cdb7 Mon Sep 17 00:00:00 2001 From: Luca Montaigut Date: Sat, 23 Mar 2024 19:43:32 +0100 Subject: [PATCH 05/11] feat: add daily log command --- src/core/cache.ts | 3 +- src/helpers/channels.ts | 13 +++-- .../cookieHunter/cookieHunter.helpers.ts | 48 +++++++++++++++++-- .../cookieHunter/cookieHunter.module.ts | 29 +++++++++-- 4 files changed, 79 insertions(+), 14 deletions(-) diff --git a/src/core/cache.ts b/src/core/cache.ts index 025fef12..26c0c2af 100644 --- a/src/core/cache.ts +++ b/src/core/cache.ts @@ -20,13 +20,14 @@ interface Cache> { clear: () => Promise; } -interface CacheEntries { +export interface CacheEntries { lobbyIds: string[]; onDemandChannels: string[]; quoiFeurChannels: string[]; cookieHunterChannels: string[]; currentHuntMessageId: string; cookieHunterDailyCount: Record; + cookieHunterDailyLogChannels: string[]; recurringMessages: { id: string; channelId: string; frequency: Frequency; message: string }[]; } diff --git a/src/helpers/channels.ts b/src/helpers/channels.ts index 18be81fb..840f6c1c 100644 --- a/src/helpers/channels.ts +++ b/src/helpers/channels.ts @@ -4,12 +4,17 @@ import { type DMChannel, type NonThreadGuildBasedChannel, } from 'discord.js'; -import { cache } from '../core/cache'; +import { cache, type CacheEntries } from '../core/cache'; + +type ChannelArrayCacheKey = Pick< + CacheEntries, + 'quoiFeurChannels' | 'cookieHunterChannels' | 'cookieHunterDailyLogChannels' +>; export const addChannelInCache = async ( interaction: ChatInputCommandInteraction, featureName: string, - cacheKey: 'quoiFeurChannels' | 'cookieHunterChannels', + cacheKey: keyof ChannelArrayCacheKey, ) => { const { channel } = interaction; if (!channel || !channel.isTextBased() || channel.type !== ChannelType.GuildText) return; @@ -30,7 +35,7 @@ export const addChannelInCache = async ( export const removeChannelFromChache = async ( interaction: ChatInputCommandInteraction, featureName: string, - cacheKey: 'quoiFeurChannels' | 'cookieHunterChannels', + cacheKey: keyof ChannelArrayCacheKey, ) => { const { channel } = interaction; if (!channel || !channel.isTextBased() || channel.type !== ChannelType.GuildText) return; @@ -53,7 +58,7 @@ export const removeChannelFromChache = async ( export const cleanCacheOnChannelDelete = async ( channel: DMChannel | NonThreadGuildBasedChannel, - cacheKey: 'quoiFeurChannels' | 'cookieHunterChannels', + cacheKey: keyof ChannelArrayCacheKey, ) => { const { id } = channel; const channels = await cache.get(cacheKey, []); diff --git a/src/modules/cookieHunter/cookieHunter.helpers.ts b/src/modules/cookieHunter/cookieHunter.helpers.ts index 1a4e7c7e..8d6b1b1d 100644 --- a/src/modules/cookieHunter/cookieHunter.helpers.ts +++ b/src/modules/cookieHunter/cookieHunter.helpers.ts @@ -46,7 +46,7 @@ const sendMessageInRandomChannel = async (client: Client) => { setTimeout(() => { cookieMessage.delete(); - logDailyCount(); + logDailyCount(client); // TODO : add the daily count into the global scoreboard }, ONE_MINUTE); }; @@ -69,9 +69,47 @@ export const countCookies = async ( await cache.set('cookieHunterDailyCount', newDailyCount); }; -const logDailyCount = async () => { +const logDailyCount = async (client: Client) => { + const dailyLogChannels = await cache.get('cookieHunterDailyLogChannels', []); + if (!dailyLogChannels.length) return; + + const currentHuntMessageId = await cache.get('currentHuntMessageId'); + if (!currentHuntMessageId) + throw new Error('Lost the hunt message id before logging the daily count'); + const cookieHunterDailyCount = await cache.get('cookieHunterDailyCount', {}); - console.log(cookieHunterDailyCount); - // TODO : command to log the daily count in a channel in order to keep track of - // where and when the message is sent, and who reacted to it + const hunterCount = Object.keys(cookieHunterDailyCount).length - 1; // grandma is not a hunter + + const resume = `**🍪 Résumé de la chasse aux cookies du jour**\n`; + const where = `Mamie a servi des cookies dans <#${currentHuntMessageId}>\n`; + const baseMessage = `${resume}${where}`; + + const message = + hunterCount > 0 + ? getHuntersFoundGrandmaMessage(baseMessage, cookieHunterDailyCount) + : `${baseMessage}**🍪 Personne n'a trouvé Mamie !**\nFaut dire qu'elle se cache bien (et que vous êtes nazes) !`; + + for (const channelId of dailyLogChannels) { + const channel = await client.channels.fetch(channelId); + if (!channel || !channel.isTextBased()) return; + await channel.send(message); + } +}; + +const getHuntersFoundGrandmaMessage = ( + baseMessage: string, + cookieHunterDailyCount: Record, +) => { + const cookieEatenCount = Object.values(cookieHunterDailyCount).reduce( + (acc, count) => acc + count, + 0, + ); + const dailyRank = Object.entries(cookieHunterDailyCount).sort((a, b) => b[1] - a[1]); + + const totalEaten = `Nombre de cookies total mangés : ${cookieEatenCount}\n`; + const ranking = `**Classement des chasseurs de cookies du jour**\n`; + const usersRanking = dailyRank.map(([userId, count]) => `<@${userId}>: ${count}\n`).join('\n'); + const lastWords = `Sacré bande de gourmands !`; + + return `${baseMessage}${totalEaten}${ranking}${usersRanking}${lastWords}`; }; diff --git a/src/modules/cookieHunter/cookieHunter.module.ts b/src/modules/cookieHunter/cookieHunter.module.ts index 0ea30abc..ccc9c418 100644 --- a/src/modules/cookieHunter/cookieHunter.module.ts +++ b/src/modules/cookieHunter/cookieHunter.module.ts @@ -24,21 +24,42 @@ export const cookieHunter = createModule({ .addSubcommand((subcommand) => subcommand.setName('disable').setDescription('Disable the cookie hunt in the channel'), ) + .addSubcommand((subcommand) => + subcommand.setName('add-daily-log').setDescription('Add daily log to the channel'), + ) + .addSubcommand((subcommand) => + subcommand.setName('remove-daily-log').setDescription('Add daily log to the channel'), + ) .setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages) .toJSON(), handler: { - start: (interaction) => startHunting(interaction.client), - enable: (interaction) => + 'start': (interaction) => startHunting(interaction.client), + 'enable': (interaction) => addChannelInCache(interaction, 'Cookie Hunter', 'cookieHunterChannels'), - disable: (interaction) => + 'disable': (interaction) => removeChannelFromChache(interaction, 'Cookie Hunter', 'cookieHunterChannels'), + 'add-daily-log': (interaction) => + addChannelInCache( + interaction, + 'Cookie Hunter Daily logs', + 'cookieHunterDailyLogChannels', + ), + 'remove-daily-log': (interaction) => + removeChannelFromChache( + interaction, + 'Cookie Hunter Daily logs', + 'cookieHunterDailyLogChannels', + ), }, }, ], eventHandlers: () => ({ ready: startHunting, messageReactionAdd: countCookies, - channelDelete: (channel) => cleanCacheOnChannelDelete(channel, 'cookieHunterChannels'), + channelDelete: async (channel) => { + cleanCacheOnChannelDelete(channel, 'cookieHunterChannels'); + cleanCacheOnChannelDelete(channel, 'cookieHunterDailyLogChannels'); + }, }), intents: ['Guilds'], }); From 5e323f99d360f7f8f649d433b4dcd9ff22f91a1f Mon Sep 17 00:00:00 2001 From: Luca Montaigut Date: Sat, 23 Mar 2024 19:49:57 +0100 Subject: [PATCH 06/11] feat: add global scoreboard --- src/core/cache.ts | 1 + .../cookieHunter/cookieHunter.helpers.ts | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/core/cache.ts b/src/core/cache.ts index 26c0c2af..201dfd5d 100644 --- a/src/core/cache.ts +++ b/src/core/cache.ts @@ -28,6 +28,7 @@ export interface CacheEntries { currentHuntMessageId: string; cookieHunterDailyCount: Record; cookieHunterDailyLogChannels: string[]; + cookieHunterScoreboard: Record; recurringMessages: { id: string; channelId: string; frequency: Frequency; message: string }[]; } diff --git a/src/modules/cookieHunter/cookieHunter.helpers.ts b/src/modules/cookieHunter/cookieHunter.helpers.ts index 8d6b1b1d..8f1c88d2 100644 --- a/src/modules/cookieHunter/cookieHunter.helpers.ts +++ b/src/modules/cookieHunter/cookieHunter.helpers.ts @@ -44,10 +44,12 @@ const sendMessageInRandomChannel = async (client: Client) => { cookieMessage.react('🥛'); cookieMessage.react('🍪'); // 1 point for grandma here, she beats everyone who doesn't find her - setTimeout(() => { + setTimeout(async () => { cookieMessage.delete(); - logDailyCount(client); - // TODO : add the daily count into the global scoreboard + await logDailyCount(client); + await updateGlobalScoreboard(); + await cache.delete('currentHuntMessageId'); + await cache.delete('cookieHunterDailyCount'); }, ONE_MINUTE); }; @@ -113,3 +115,12 @@ const getHuntersFoundGrandmaMessage = ( return `${baseMessage}${totalEaten}${ranking}${usersRanking}${lastWords}`; }; + +const updateGlobalScoreboard = async () => { + const cookieHunterDailyCount = await cache.get('cookieHunterDailyCount', {}); + const coockieHunterScoreboard = await cache.get('cookieHunterScoreboard', {}); + for (const [userId, count] of Object.entries(cookieHunterDailyCount)) { + coockieHunterScoreboard[userId] = (coockieHunterScoreboard[userId] || 0) + count; + } + await cache.set('cookieHunterScoreboard', coockieHunterScoreboard); +}; From 2e901672cc333eb280bafb02e2dc2330a0e57310 Mon Sep 17 00:00:00 2001 From: Luca Montaigut Date: Sat, 23 Mar 2024 20:15:30 +0100 Subject: [PATCH 07/11] fix: lint --- src/core/cache.ts | 3 +- src/helpers/channels.ts | 1 + src/main.ts | 2 +- .../cookieHunter/cookieHunter.helpers.ts | 90 ++++++++++--------- .../cookieHunter/cookieHunter.module.ts | 10 ++- src/modules/quoiFeur/quoiFeur.module.ts | 2 +- 6 files changed, 58 insertions(+), 50 deletions(-) diff --git a/src/core/cache.ts b/src/core/cache.ts index 201dfd5d..732f7a1d 100644 --- a/src/core/cache.ts +++ b/src/core/cache.ts @@ -20,7 +20,7 @@ interface Cache> { clear: () => Promise; } -export interface CacheEntries { +interface CacheEntries { lobbyIds: string[]; onDemandChannels: string[]; quoiFeurChannels: string[]; @@ -70,3 +70,4 @@ class CacheImpl implements Cache { } export const cache = new CacheImpl(); +export type { CacheEntries }; diff --git a/src/helpers/channels.ts b/src/helpers/channels.ts index 840f6c1c..c18262b8 100644 --- a/src/helpers/channels.ts +++ b/src/helpers/channels.ts @@ -4,6 +4,7 @@ import { type DMChannel, type NonThreadGuildBasedChannel, } from 'discord.js'; + import { cache, type CacheEntries } from '../core/cache'; type ChannelArrayCacheKey = Pick< diff --git a/src/main.ts b/src/main.ts index 0a491c5c..76b0105d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,13 +4,13 @@ import { createAllModules } from './core/createEnvForModule'; import { env } from './core/env'; import { getIntentsFromModules } from './core/getIntentsFromModules'; import { loadModules } from './core/loadModules'; +import { cookieHunter } from './modules/cookieHunter/cookieHunter.module'; import { coolLinksManagement } from './modules/coolLinksManagement/coolLinksManagement.module'; import { fart } from './modules/fart/fart.module'; import { fixEmbedTwitterVideo } from './modules/fixEmbedTwitterVideo/fixEmbedTwitterVideo.module'; import { quoiFeur } from './modules/quoiFeur/quoiFeur.module'; import { recurringMessage } from './modules/recurringMessage/recurringMessage.module'; import { voiceOnDemand } from './modules/voiceOnDemand/voiceOnDemand.module'; -import { cookieHunter } from './modules/cookieHunter/cookieHunter.module'; const modules = [ fart, diff --git a/src/modules/cookieHunter/cookieHunter.helpers.ts b/src/modules/cookieHunter/cookieHunter.helpers.ts index 8f1c88d2..f780dc60 100644 --- a/src/modules/cookieHunter/cookieHunter.helpers.ts +++ b/src/modules/cookieHunter/cookieHunter.helpers.ts @@ -1,33 +1,20 @@ import { CronJob } from 'cron'; -import { cache } from '../../core/cache'; import type { Client, + Message, MessageReaction, PartialMessageReaction, PartialUser, User, } from 'discord.js'; + +import { cache } from '../../core/cache'; import { ONE_MINUTE } from '../../helpers/timeConstants'; const IT_IS_SNACK_TIME = '0 30 16 * * *'; // 4:30pm every day let jobCurrentlyRunning: CronJob | null = null; -export const startHunting = async (client: Client) => { - console.log('Cookie hunter started'); - if (jobCurrentlyRunning !== null) { - // needed in case that the bot fire multiple ready event - jobCurrentlyRunning.stop(); - } - jobCurrentlyRunning = new CronJob( - IT_IS_SNACK_TIME, - () => sendMessageInRandomChannel(client), - null, - true, - 'Europe/Paris', - ); -}; - const sendMessageInRandomChannel = async (client: Client) => { const channel = await cache.get('cookieHunterChannels', []); if (!channel.length) return; @@ -41,34 +28,10 @@ const sendMessageInRandomChannel = async (client: Client) => { const cookieMessage = await channelToSend.send('**👵 Qui veut des cookies ?**'); await cache.set('currentHuntMessageId', cookieMessage.id); await cache.set('cookieHunterDailyCount', {}); - cookieMessage.react('🥛'); - cookieMessage.react('🍪'); // 1 point for grandma here, she beats everyone who doesn't find her - - setTimeout(async () => { - cookieMessage.delete(); - await logDailyCount(client); - await updateGlobalScoreboard(); - await cache.delete('currentHuntMessageId'); - await cache.delete('cookieHunterDailyCount'); - }, ONE_MINUTE); -}; + await cookieMessage.react('🥛'); + await cookieMessage.react('🍪'); // 1 point for grandma here, she beats everyone who doesn't find her -export const countCookies = async ( - reaction: MessageReaction | PartialMessageReaction, - user: User | PartialUser, -) => { - const currentHuntMessageId = await cache.get('currentHuntMessageId'); - if ( - !currentHuntMessageId || - reaction.message.id !== currentHuntMessageId || - reaction.emoji.name !== '🍪' - ) - return; - - const cookieHunterDailyCount = await cache.get('cookieHunterDailyCount', {}); - const userDailyCount = cookieHunterDailyCount[user.id] || 0; - const newDailyCount = { ...cookieHunterDailyCount, [user.id]: userDailyCount + 1 }; - await cache.set('cookieHunterDailyCount', newDailyCount); + setTimeout(() => void dailyHuntEnd(client, cookieMessage), ONE_MINUTE); }; const logDailyCount = async (client: Client) => { @@ -124,3 +87,44 @@ const updateGlobalScoreboard = async () => { } await cache.set('cookieHunterScoreboard', coockieHunterScoreboard); }; + +const dailyHuntEnd = async (client: Client, cookieMessage: Message) => { + await cookieMessage.delete(); + await logDailyCount(client); + await updateGlobalScoreboard(); + await cache.delete('currentHuntMessageId'); + await cache.delete('cookieHunterDailyCount'); +}; + +export const countCookies = async ( + reaction: MessageReaction | PartialMessageReaction, + user: User | PartialUser, +) => { + const currentHuntMessageId = await cache.get('currentHuntMessageId'); + if ( + !currentHuntMessageId || + reaction.message.id !== currentHuntMessageId || + reaction.emoji.name !== '🍪' + ) + return; + + const cookieHunterDailyCount = await cache.get('cookieHunterDailyCount', {}); + const userDailyCount = cookieHunterDailyCount[user.id] || 0; + const newDailyCount = { ...cookieHunterDailyCount, [user.id]: userDailyCount + 1 }; + await cache.set('cookieHunterDailyCount', newDailyCount); +}; + +export const startHunting = (client: Client) => { + console.log('Cookie hunter started'); + if (jobCurrentlyRunning !== null) { + // needed in case that the bot fire multiple ready event + jobCurrentlyRunning.stop(); + } + jobCurrentlyRunning = new CronJob( + IT_IS_SNACK_TIME, + () => sendMessageInRandomChannel(client), + null, + true, + 'Europe/Paris', + ); +}; diff --git a/src/modules/cookieHunter/cookieHunter.module.ts b/src/modules/cookieHunter/cookieHunter.module.ts index ccc9c418..6792d5dd 100644 --- a/src/modules/cookieHunter/cookieHunter.module.ts +++ b/src/modules/cookieHunter/cookieHunter.module.ts @@ -33,7 +33,8 @@ export const cookieHunter = createModule({ .setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages) .toJSON(), handler: { - 'start': (interaction) => startHunting(interaction.client), + // eslint-disable-next-line @typescript-eslint/require-await + 'start': async (interaction) => startHunting(interaction.client), 'enable': (interaction) => addChannelInCache(interaction, 'Cookie Hunter', 'cookieHunterChannels'), 'disable': (interaction) => @@ -54,11 +55,12 @@ export const cookieHunter = createModule({ }, ], eventHandlers: () => ({ - ready: startHunting, + // eslint-disable-next-line @typescript-eslint/require-await + ready: async (client) => startHunting(client), messageReactionAdd: countCookies, channelDelete: async (channel) => { - cleanCacheOnChannelDelete(channel, 'cookieHunterChannels'); - cleanCacheOnChannelDelete(channel, 'cookieHunterDailyLogChannels'); + await cleanCacheOnChannelDelete(channel, 'cookieHunterChannels'); + await cleanCacheOnChannelDelete(channel, 'cookieHunterDailyLogChannels'); }, }), intents: ['Guilds'], diff --git a/src/modules/quoiFeur/quoiFeur.module.ts b/src/modules/quoiFeur/quoiFeur.module.ts index dfeaa985..d9e28044 100644 --- a/src/modules/quoiFeur/quoiFeur.module.ts +++ b/src/modules/quoiFeur/quoiFeur.module.ts @@ -1,12 +1,12 @@ import { PermissionFlagsBits, SlashCommandBuilder } from 'discord.js'; import { createModule } from '../../core/createModule'; -import { reactOnEndWithQuoi } from './quoiFeur.helpers'; import { addChannelInCache, cleanCacheOnChannelDelete, removeChannelFromChache, } from '../../helpers/channels'; +import { reactOnEndWithQuoi } from './quoiFeur.helpers'; export const quoiFeur = createModule({ name: 'quoiFeur', From af40697579b0604ec76208eab848fd8b0c81a437 Mon Sep 17 00:00:00 2001 From: Luca Montaigut Date: Sat, 23 Mar 2024 23:21:26 +0100 Subject: [PATCH 08/11] feat: milk joker --- src/core/cache.ts | 1 + .../cookieHunter/cookieHunter.helpers.ts | 42 +++++++++++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/core/cache.ts b/src/core/cache.ts index 732f7a1d..33c28e61 100644 --- a/src/core/cache.ts +++ b/src/core/cache.ts @@ -29,6 +29,7 @@ interface CacheEntries { cookieHunterDailyCount: Record; cookieHunterDailyLogChannels: string[]; cookieHunterScoreboard: Record; + milkJokerUserId: string; recurringMessages: { id: string; channelId: string; frequency: Frequency; message: string }[]; } diff --git a/src/modules/cookieHunter/cookieHunter.helpers.ts b/src/modules/cookieHunter/cookieHunter.helpers.ts index f780dc60..8b9caad2 100644 --- a/src/modules/cookieHunter/cookieHunter.helpers.ts +++ b/src/modules/cookieHunter/cookieHunter.helpers.ts @@ -34,6 +34,16 @@ const sendMessageInRandomChannel = async (client: Client) => { setTimeout(() => void dailyHuntEnd(client, cookieMessage), ONE_MINUTE); }; +const applyMilkJoker = async () => { + const milkJokerUserId = await cache.get('milkJokerUserId'); + if (!milkJokerUserId) return; + + const cookieHunterDailyCount = await cache.get('cookieHunterDailyCount', {}); + const userDailyCount = cookieHunterDailyCount[milkJokerUserId] || 0; + const newDailyCount = { ...cookieHunterDailyCount, [milkJokerUserId]: userDailyCount * 2 }; + await cache.set('cookieHunterDailyCount', newDailyCount); +}; + const logDailyCount = async (client: Client) => { const dailyLogChannels = await cache.get('cookieHunterDailyLogChannels', []); if (!dailyLogChannels.length) return; @@ -45,13 +55,15 @@ const logDailyCount = async (client: Client) => { const cookieHunterDailyCount = await cache.get('cookieHunterDailyCount', {}); const hunterCount = Object.keys(cookieHunterDailyCount).length - 1; // grandma is not a hunter + const milkJokerUserId = await cache.get('milkJokerUserId'); + const resume = `**🍪 Résumé de la chasse aux cookies du jour**\n`; const where = `Mamie a servi des cookies dans <#${currentHuntMessageId}>\n`; const baseMessage = `${resume}${where}`; const message = hunterCount > 0 - ? getHuntersFoundGrandmaMessage(baseMessage, cookieHunterDailyCount) + ? getHuntersFoundGrandmaMessage(baseMessage, cookieHunterDailyCount, milkJokerUserId) : `${baseMessage}**🍪 Personne n'a trouvé Mamie !**\nFaut dire qu'elle se cache bien (et que vous êtes nazes) !`; for (const channelId of dailyLogChannels) { @@ -64,6 +76,7 @@ const logDailyCount = async (client: Client) => { const getHuntersFoundGrandmaMessage = ( baseMessage: string, cookieHunterDailyCount: Record, + milkJokerUserId: string | undefined, ) => { const cookieEatenCount = Object.values(cookieHunterDailyCount).reduce( (acc, count) => acc + count, @@ -74,9 +87,12 @@ const getHuntersFoundGrandmaMessage = ( const totalEaten = `Nombre de cookies total mangés : ${cookieEatenCount}\n`; const ranking = `**Classement des chasseurs de cookies du jour**\n`; const usersRanking = dailyRank.map(([userId, count]) => `<@${userId}>: ${count}\n`).join('\n'); + const milkJoker = milkJokerUserId + ? `<@${milkJokerUserId}> a accompagné ses cookies d'un grand verre de lait 🥛\n` + : ''; const lastWords = `Sacré bande de gourmands !`; - return `${baseMessage}${totalEaten}${ranking}${usersRanking}${lastWords}`; + return `${baseMessage}${totalEaten}${ranking}${usersRanking}${milkJoker}${lastWords}`; }; const updateGlobalScoreboard = async () => { @@ -90,8 +106,10 @@ const updateGlobalScoreboard = async () => { const dailyHuntEnd = async (client: Client, cookieMessage: Message) => { await cookieMessage.delete(); + await applyMilkJoker(); await logDailyCount(client); await updateGlobalScoreboard(); + await cache.delete('milkJokerUserId'); await cache.delete('currentHuntMessageId'); await cache.delete('cookieHunterDailyCount'); }; @@ -104,10 +122,28 @@ export const countCookies = async ( if ( !currentHuntMessageId || reaction.message.id !== currentHuntMessageId || - reaction.emoji.name !== '🍪' + reaction.emoji.name === null || + !['🍪', '🥛'].includes(reaction.emoji.name) ) return; + const isMilkJokerAlreadyFound = await cache.get('milkJokerUserId'); + + if (reaction.emoji.name === '🥛') { + if (isMilkJokerAlreadyFound) { + reaction.message.reply({ + content: `Il est lent ce lait... 🥛`, + options: { ephemeral: true }, + }); + } else { + await cache.set('milkJokerUserId', user.id); + reaction.message.reply({ + content: `Premier arrivé, premier servit. Cul sec 🥛 !`, + options: { ephemeral: true }, + }); + } + } + const cookieHunterDailyCount = await cache.get('cookieHunterDailyCount', {}); const userDailyCount = cookieHunterDailyCount[user.id] || 0; const newDailyCount = { ...cookieHunterDailyCount, [user.id]: userDailyCount + 1 }; From 3d3ad2ff47fed45177a3923e83f00c24fa1b3ea1 Mon Sep 17 00:00:00 2001 From: Luca Montaigut Date: Sat, 23 Mar 2024 23:34:46 +0100 Subject: [PATCH 09/11] feat: display scoreboard --- .../cookieHunter/cookieHunter.helpers.ts | 73 ++++++++++++------- .../cookieHunter/cookieHunter.module.ts | 6 +- 2 files changed, 52 insertions(+), 27 deletions(-) diff --git a/src/modules/cookieHunter/cookieHunter.helpers.ts b/src/modules/cookieHunter/cookieHunter.helpers.ts index 8b9caad2..26dd761d 100644 --- a/src/modules/cookieHunter/cookieHunter.helpers.ts +++ b/src/modules/cookieHunter/cookieHunter.helpers.ts @@ -1,5 +1,6 @@ import { CronJob } from 'cron'; import type { + ChatInputCommandInteraction, Client, Message, MessageReaction, @@ -34,6 +35,25 @@ const sendMessageInRandomChannel = async (client: Client) => { setTimeout(() => void dailyHuntEnd(client, cookieMessage), ONE_MINUTE); }; +const handleMilkReaction = async ( + reaction: MessageReaction | PartialMessageReaction, + user: User | PartialUser, + isMilkJokerAlreadyFound: boolean, +) => { + if (isMilkJokerAlreadyFound) { + await reaction.message.reply({ + content: `Il est lent ce lait... 🥛`, + options: { ephemeral: true }, + }); + } else { + await cache.set('milkJokerUserId', user.id); + await reaction.message.reply({ + content: `Premier arrivé, premier servit. Cul sec 🥛 !`, + options: { ephemeral: true }, + }); + } +}; + const applyMilkJoker = async () => { const milkJokerUserId = await cache.get('milkJokerUserId'); if (!milkJokerUserId) return; @@ -114,6 +134,21 @@ const dailyHuntEnd = async (client: Client, cookieMessage: Message) => { await cache.delete('cookieHunterDailyCount'); }; +export const startHunting = (client: Client) => { + console.log('Cookie hunter started'); + if (jobCurrentlyRunning !== null) { + // needed in case that the bot fire multiple ready event + jobCurrentlyRunning.stop(); + } + jobCurrentlyRunning = new CronJob( + IT_IS_SNACK_TIME, + () => sendMessageInRandomChannel(client), + null, + true, + 'Europe/Paris', + ); +}; + export const countCookies = async ( reaction: MessageReaction | PartialMessageReaction, user: User | PartialUser, @@ -127,21 +162,10 @@ export const countCookies = async ( ) return; - const isMilkJokerAlreadyFound = await cache.get('milkJokerUserId'); + const isMilkJokerAlreadyFound = Boolean(await cache.get('milkJokerUserId')); if (reaction.emoji.name === '🥛') { - if (isMilkJokerAlreadyFound) { - reaction.message.reply({ - content: `Il est lent ce lait... 🥛`, - options: { ephemeral: true }, - }); - } else { - await cache.set('milkJokerUserId', user.id); - reaction.message.reply({ - content: `Premier arrivé, premier servit. Cul sec 🥛 !`, - options: { ephemeral: true }, - }); - } + await handleMilkReaction(reaction, user, isMilkJokerAlreadyFound); } const cookieHunterDailyCount = await cache.get('cookieHunterDailyCount', {}); @@ -150,17 +174,14 @@ export const countCookies = async ( await cache.set('cookieHunterDailyCount', newDailyCount); }; -export const startHunting = (client: Client) => { - console.log('Cookie hunter started'); - if (jobCurrentlyRunning !== null) { - // needed in case that the bot fire multiple ready event - jobCurrentlyRunning.stop(); - } - jobCurrentlyRunning = new CronJob( - IT_IS_SNACK_TIME, - () => sendMessageInRandomChannel(client), - null, - true, - 'Europe/Paris', - ); +export const displayScoreboard = async (interaction: ChatInputCommandInteraction) => { + const cookieHunterScoreboard = await cache.get('cookieHunterScoreboard', {}); + const ranking = Object.entries(cookieHunterScoreboard) + .sort((a, b) => b[1] - a[1]) + .map(([userId, count], index) => `${index + 1}. <@${userId}>: ${count}`) + .join('\n'); + + const message = `**🍪 Classement général des chasseurs de cookies**\n${ranking}`; + + await interaction.reply(message); }; diff --git a/src/modules/cookieHunter/cookieHunter.module.ts b/src/modules/cookieHunter/cookieHunter.module.ts index 6792d5dd..ca434cac 100644 --- a/src/modules/cookieHunter/cookieHunter.module.ts +++ b/src/modules/cookieHunter/cookieHunter.module.ts @@ -6,7 +6,7 @@ import { cleanCacheOnChannelDelete, removeChannelFromChache, } from '../../helpers/channels'; -import { countCookies, startHunting } from './cookieHunter.helpers'; +import { countCookies, displayScoreboard, startHunting } from './cookieHunter.helpers'; export const cookieHunter = createModule({ name: 'cookieHunter', @@ -30,6 +30,9 @@ export const cookieHunter = createModule({ .addSubcommand((subcommand) => subcommand.setName('remove-daily-log').setDescription('Add daily log to the channel'), ) + .addSubcommand((subcommand) => + subcommand.setName('scoreboard').setDescription('Show the scoreboard'), + ) .setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages) .toJSON(), handler: { @@ -51,6 +54,7 @@ export const cookieHunter = createModule({ 'Cookie Hunter Daily logs', 'cookieHunterDailyLogChannels', ), + 'scoreboard': async (interaction) => displayScoreboard(interaction), }, }, ], From 3a24fbe7ff96824dc9f91e22ac1426b5893758f5 Mon Sep 17 00:00:00 2001 From: Luca Montaigut Date: Sun, 24 Mar 2024 00:24:35 +0100 Subject: [PATCH 10/11] fix: typo --- src/modules/cookieHunter/cookieHunter.helpers.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/modules/cookieHunter/cookieHunter.helpers.ts b/src/modules/cookieHunter/cookieHunter.helpers.ts index 26dd761d..cc7a686b 100644 --- a/src/modules/cookieHunter/cookieHunter.helpers.ts +++ b/src/modules/cookieHunter/cookieHunter.helpers.ts @@ -31,7 +31,6 @@ const sendMessageInRandomChannel = async (client: Client) => { await cache.set('cookieHunterDailyCount', {}); await cookieMessage.react('🥛'); await cookieMessage.react('🍪'); // 1 point for grandma here, she beats everyone who doesn't find her - setTimeout(() => void dailyHuntEnd(client, cookieMessage), ONE_MINUTE); }; @@ -106,11 +105,11 @@ const getHuntersFoundGrandmaMessage = ( const totalEaten = `Nombre de cookies total mangés : ${cookieEatenCount}\n`; const ranking = `**Classement des chasseurs de cookies du jour**\n`; - const usersRanking = dailyRank.map(([userId, count]) => `<@${userId}>: ${count}\n`).join('\n'); + const usersRanking = dailyRank.map(([userId, count]) => `<@${userId}>: ${count}`).join('\n'); const milkJoker = milkJokerUserId - ? `<@${milkJokerUserId}> a accompagné ses cookies d'un grand verre de lait 🥛\n` + ? `\n<@${milkJokerUserId}> a accompagné ses cookies d'un grand verre de lait 🥛` : ''; - const lastWords = `Sacré bande de gourmands !`; + const lastWords = `\nSacré bande de gourmands !`; return `${baseMessage}${totalEaten}${ranking}${usersRanking}${milkJoker}${lastWords}`; }; @@ -164,7 +163,7 @@ export const countCookies = async ( const isMilkJokerAlreadyFound = Boolean(await cache.get('milkJokerUserId')); - if (reaction.emoji.name === '🥛') { + if (reaction.emoji.name === '🥛' && !user.bot) { await handleMilkReaction(reaction, user, isMilkJokerAlreadyFound); } From c7d86a264c6a617ccd833da2f8297f1d2bbd2b18 Mon Sep 17 00:00:00 2001 From: Luca Montaigut Date: Sun, 24 Mar 2024 01:32:00 +0100 Subject: [PATCH 11/11] fix: PR returns --- src/helpers/channels.ts | 6 ++- .../cookieHunter/cookieHunter.helpers.ts | 41 +++++++++++-------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/helpers/channels.ts b/src/helpers/channels.ts index c18262b8..3045b3bb 100644 --- a/src/helpers/channels.ts +++ b/src/helpers/channels.ts @@ -12,9 +12,11 @@ type ChannelArrayCacheKey = Pick< 'quoiFeurChannels' | 'cookieHunterChannels' | 'cookieHunterDailyLogChannels' >; +type FeatureName = 'Cookie Hunter' | 'Cookie Hunter Daily logs' | 'Quoi-Feur'; + export const addChannelInCache = async ( interaction: ChatInputCommandInteraction, - featureName: string, + featureName: FeatureName, cacheKey: keyof ChannelArrayCacheKey, ) => { const { channel } = interaction; @@ -35,7 +37,7 @@ export const addChannelInCache = async ( export const removeChannelFromChache = async ( interaction: ChatInputCommandInteraction, - featureName: string, + featureName: FeatureName, cacheKey: keyof ChannelArrayCacheKey, ) => { const { channel } = interaction; diff --git a/src/modules/cookieHunter/cookieHunter.helpers.ts b/src/modules/cookieHunter/cookieHunter.helpers.ts index cc7a686b..8cf0615e 100644 --- a/src/modules/cookieHunter/cookieHunter.helpers.ts +++ b/src/modules/cookieHunter/cookieHunter.helpers.ts @@ -27,10 +27,12 @@ const sendMessageInRandomChannel = async (client: Client) => { if (!channelToSend || !channelToSend.isTextBased()) return; const cookieMessage = await channelToSend.send('**👵 Qui veut des cookies ?**'); - await cache.set('currentHuntMessageId', cookieMessage.id); - await cache.set('cookieHunterDailyCount', {}); - await cookieMessage.react('🥛'); - await cookieMessage.react('🍪'); // 1 point for grandma here, she beats everyone who doesn't find her + await Promise.all([ + cache.set('currentHuntMessageId', cookieMessage.id), + cache.set('cookieHunterDailyCount', {}), + cookieMessage.react('🥛'), + cookieMessage.react('🍪'), // 1 point for grandma here, she beats everyone who doesn't find her + ]); setTimeout(() => void dailyHuntEnd(client, cookieMessage), ONE_MINUTE); }; @@ -87,7 +89,7 @@ const logDailyCount = async (client: Client) => { for (const channelId of dailyLogChannels) { const channel = await client.channels.fetch(channelId); - if (!channel || !channel.isTextBased()) return; + if (!channel || !channel.isTextBased()) continue; await channel.send(message); } }; @@ -103,15 +105,18 @@ const getHuntersFoundGrandmaMessage = ( ); const dailyRank = Object.entries(cookieHunterDailyCount).sort((a, b) => b[1] - a[1]); - const totalEaten = `Nombre de cookies total mangés : ${cookieEatenCount}\n`; - const ranking = `**Classement des chasseurs de cookies du jour**\n`; - const usersRanking = dailyRank.map(([userId, count]) => `<@${userId}>: ${count}`).join('\n'); - const milkJoker = milkJokerUserId - ? `\n<@${milkJokerUserId}> a accompagné ses cookies d'un grand verre de lait 🥛` - : ''; - const lastWords = `\nSacré bande de gourmands !`; - - return `${baseMessage}${totalEaten}${ranking}${usersRanking}${milkJoker}${lastWords}`; + return [ + baseMessage, + `Nombre de cookies total mangés : ${cookieEatenCount}`, + `**Classement des chasseurs de cookies du jour**`, + dailyRank.map(([userId, count]) => `<@${userId}>: ${count}`).join('\n'), + milkJokerUserId + ? `<@${milkJokerUserId}> a accompagné ses cookies d'un grand verre de lait 🥛` + : null, + `Sacré bande de gourmands !`, + ] + .filter(Boolean) + .join('\n'); }; const updateGlobalScoreboard = async () => { @@ -128,9 +133,11 @@ const dailyHuntEnd = async (client: Client, cookieMessage: Message) => { await applyMilkJoker(); await logDailyCount(client); await updateGlobalScoreboard(); - await cache.delete('milkJokerUserId'); - await cache.delete('currentHuntMessageId'); - await cache.delete('cookieHunterDailyCount'); + await Promise.all([ + cache.delete('milkJokerUserId'), + cache.delete('currentHuntMessageId'), + cache.delete('cookieHunterDailyCount'), + ]); }; export const startHunting = (client: Client) => {