diff --git a/src/award_message.ts b/src/award_message.ts index 3385063..56cce51 100644 --- a/src/award_message.ts +++ b/src/award_message.ts @@ -1,6 +1,6 @@ import { EmbedBuilder, MessageReaction, PartialMessageReaction } from 'discord.js'; -export default function awardEmbed(reaction: MessageReaction | PartialMessageReaction) { +export default function awardEmbed(reaction: MessageReaction | PartialMessageReaction, score: number) { function formatTime(time: Date) { const yyyy = time.getFullYear(); const mm = time.getMonth().toString().padStart(2, '0'); @@ -18,7 +18,7 @@ export default function awardEmbed(reaction: MessageReaction | PartialMessageRea .setDescription(reaction.message.content || null) .setThumbnail(reaction.message.attachments.filter((attachment) => !(!attachment.height && !attachment.width)).first()?.url || null) .addFields({ - name: `${reaction.emoji.name} x${reaction.count} `, - value: `in <#${reaction.message.channelId}> • ${formatTime(reaction.message.createdAt)}\n-# ${reaction.message.id}`, + name: `${reaction.message.reactions.cache.map((reaction) => `${reaction.emoji} x${reaction.count},`).join(' ')} / ${score.toFixed(1)}`, + value: `${reaction.message.url} • ${formatTime(reaction.message.createdAt)}\n-# ${reaction.message.id}`, }) } \ No newline at end of file diff --git a/src/commands.ts b/src/commands.ts index 30a9d36..8c20d8a 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -59,12 +59,12 @@ const emojis_type = new SlashCommandSubcommandBuilder() { name: 'Include', value: 'include' }, { name: 'Exclude', value: 'exclude' } )); -const count = new SlashCommandSubcommandBuilder() - .setName('count') - .setDescription('Set the number of reactions to trigger') +const score = new SlashCommandSubcommandBuilder() + .setName('score') + .setDescription('Set the number of scores to trigger') .addIntegerOption(option => - option.setName('count') - .setDescription('The number of reactions to trigger') + option.setName('score') + .setDescription('The number of scores to trigger') .setRequired(true)); @@ -76,7 +76,7 @@ const config = new SlashCommandBuilder() .addSubcommand(channels_type) .addSubcommand(emojis) .addSubcommand(emojis_type) - .addSubcommand(count); + .addSubcommand(score); const commands = [config]; export default commands; diff --git a/src/entry.ts b/src/entry.ts index 0430014..7edf8b2 100644 --- a/src/entry.ts +++ b/src/entry.ts @@ -11,7 +11,7 @@ export class Entry { channels: string[] = []; emoji_type: Type = Type.EXCLUDE; emojis: string[] = []; - count: number = 3; + score: number = 5; constructor(server: string) { this.server = server; } diff --git a/src/index.ts b/src/index.ts index 0693ead..ebb69cd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import 'dotenv/config' +import "dotenv/config"; const TOKEN = process.env.TOKEN; const CLIENT_ID = process.env.CLIENT_ID; if (!TOKEN || !CLIENT_ID) { @@ -15,6 +15,8 @@ import { GatewayIntentBits, Events, TextChannel, + Message, + PartialMessage, } from "discord.js"; const rest = new REST({ version: "10" }).setToken(TOKEN); @@ -65,8 +67,10 @@ client.on("interactionCreate", async (interaction) => { if (subcommand === "output") { const channel = interaction.options.getChannel("channel", true); try { - await interaction.deferReply({ ephemeral: true, }); - let entry = entries.find((entry) => entry.server === interaction.guildId); + await interaction.deferReply({ ephemeral: true }); + let entry = entries.find( + (entry) => entry.server === interaction.guildId + ); if (!entry) { entry = new Entry(interaction.guildId!); entries.push(entry); @@ -85,7 +89,9 @@ client.on("interactionCreate", async (interaction) => { const mode = interaction.options.getString("add_remove", true); try { await interaction.deferReply({ ephemeral: true }); - let entry = entries.find((entry) => entry.server === interaction.guildId); + let entry = entries.find( + (entry) => entry.server === interaction.guildId + ); if (!entry) { entry = new Entry(interaction.guildId!); entries.push(entry); @@ -101,11 +107,16 @@ client.on("interactionCreate", async (interaction) => { }); } } else if (subcommand === "channels_type") { - const channel_type = interaction.options.getString("channels_type", true); + const channel_type = interaction.options.getString( + "channels_type", + true + ); try { await interaction.deferReply({ ephemeral: true }); - let entry = entries.find((entry) => entry.server === interaction.guildId); + let entry = entries.find( + (entry) => entry.server === interaction.guildId + ); if (!entry) { entry = new Entry(interaction.guildId!); entries.push(entry); @@ -164,8 +175,8 @@ client.on("interactionCreate", async (interaction) => { ephemeral: true, }); } - } else if (subcommand === "count") { - const count = interaction.options.getInteger("count", true); + } else if (subcommand === "score") { + const score = interaction.options.getInteger("score", true); try { await interaction.deferReply({ ephemeral: true }); let entry = entries.find( @@ -175,13 +186,13 @@ client.on("interactionCreate", async (interaction) => { entry = new Entry(interaction.guildId!); entries.push(entry); } - entry.count = count; + entry.score = score; await writeConfig(entries); } catch (error) { console.error(error); } finally { await interaction.followUp({ - content: `The number needed to trigger is now ${count}`, + content: `The score needed to trigger is now ${score}`, ephemeral: true, }); } @@ -202,26 +213,136 @@ client.on(Events.MessageReactionAdd, async (reaction, user) => { } if (!reaction.message.guild) return; - const entry = entries.find((entry) => entry.server === reaction.message.guildId); + const entry = entries.find( + (entry) => entry.server === reaction.message.guildId + ); if (entry === undefined) return; - if ((entry.channel_type === Type.EXCLUDE) === entry.channels.includes(reaction.message.channelId)) return; - if ((entry.emoji_type === Type.EXCLUDE) === entry.emojis.includes(`${reaction.emoji.name}`)) return; + if ( + (entry.channel_type === Type.EXCLUDE) === + entry.channels.includes(reaction.message.channelId) + ) + return; + if ( + (entry.emoji_type === Type.EXCLUDE) === + entry.emojis.includes(`${reaction.emoji.id || reaction.emoji.name}`) + ) + return; - if (reaction.count === null || reaction.count < entry.count) return; + console.log(`reaction added: ${reaction.emoji.id || reaction.emoji.name}`); + const totalScore = await getMessageScore(entry, reaction.message); - const channel = reaction.message.guild.channels.cache.get(entry.output_channel) as TextChannel; - channel.messages.fetch({ limit: 5 }).then((messages) => { + if (totalScore >= entry.score) { + const channel = reaction.message.guild.channels.cache.get( + entry.output_channel + ) as TextChannel; + channel.messages.fetch({ limit: 10 }).then((messages) => { + messages.find((message) => { + if (message.embeds[0]?.fields[0].value.includes(reaction.message.id)) { + message.delete(); + console.log("old message deleted."); + return true; + } + return false; + }); + channel.send({ + embeds: [awardEmbed(reaction, totalScore)], + }); + console.log("message sent."); + }); + } +}); + +client.on(Events.MessageReactionRemove, async (reaction, user) => { + if (reaction.partial) { + try { + await reaction.fetch(); + } catch (error) { + console.error("Something went wrong when fetching the message:", error); + return; + } + } + if (!reaction.message.guild) return; + + const entry = entries.find( + (entry) => entry.server === reaction.message.guildId + ); + if (entry === undefined) return; + if ( + (entry.channel_type === Type.EXCLUDE) === + entry.channels.includes(reaction.message.channelId) + ) + return; + if ( + (entry.emoji_type === Type.EXCLUDE) === + entry.emojis.includes(`${reaction.emoji.id || reaction.emoji.name}`) + ) + return; + + console.log(`reaction removed: ${reaction.emoji.id || reaction.emoji.name}`); + const totalScore = await getMessageScore(entry, reaction.message); + + const channel = reaction.message.guild.channels.cache.get( + entry.output_channel + ) as TextChannel; + channel.messages.fetch({ limit: 10 }).then((messages) => { messages.find((message) => { if (message.embeds[0]?.fields[0].value.includes(reaction.message.id)) { message.delete(); + console.log("old message deleted."); + if (totalScore >= entry.score) { + channel.send({ + embeds: [awardEmbed(reaction, totalScore)], + }); + console.log("message sent."); + } return true; } return false; }); - channel.send({ - embeds: [awardEmbed(reaction)] - }); }); }); -client.login(TOKEN); \ No newline at end of file +client.login(TOKEN); + +async function getMessageScore( + entry: Entry, + message: Message | PartialMessage +): Promise { + /* Message Score Calculation * + * Per user: 1 / (-1 + 2x) */ + + const allReactions: Map = new Map(); + const reactions = message.reactions.cache; + for (const r of reactions.values()) { + if ( + (entry.emoji_type === Type.EXCLUDE) === + entry.emojis.includes(`${r.emoji.id || r.emoji.name}`) + ) + continue; + const users = await r.users.fetch(); + for (const user of users.values()) { + if (user.bot) continue; + const score = allReactions.get(user.id) || 0; + allReactions.set(user.id, score + 1 / (-1 + 2 * (score + 1))); + } + } + + let totalScore = 0; + allReactions.forEach((value) => { + totalScore += value; + }); + + // "message" by "user" + // id : score + // ---------------- + // Total: 1.5 + + console.log(`"${message.id}" by "${message.author?.username}"`); + allReactions.forEach((value, key) => { + console.log(`${key} : ${value}`); + }); + console.log(`----------------`); + console.log(`Total: ${totalScore}`); + + return totalScore; +}