diff --git a/package-lock.json b/package-lock.json index 34701662..1d77c292 100755 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "discordbot", - "version": "4.1.1", + "version": "4.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "discordbot", - "version": "4.1.1", + "version": "4.2.0", "license": "AGPL-3.0-only", "dependencies": { "ajv": "^8.17.1", @@ -1441,9 +1441,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.29.tgz", - "integrity": "sha512-sXdt2elaVnhpDNRDz+1BDx1JQoJRuNk7oVlAlbGiFkLikHCAQiccexF/9e91zVi6RCgqspl04aP+6Cnl9zRLrA==", + "version": "2.8.30", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.30.tgz", + "integrity": "sha512-aTUKW4ptQhS64+v2d6IkPzymEzzhw+G0bA1g3uBRV3+ntkH+svttKseW5IOR4Ed6NUVKqnY7qT3dKvzQ7io4AA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -1553,9 +1553,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001755", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001755.tgz", - "integrity": "sha512-44V+Jm6ctPj7R52Na4TLi3Zri4dWUljJd+RDm+j8LtNCc/ihLCT+X1TzoOAkRETEWqjuLnh9581Tl80FvK7jVA==", + "version": "1.0.30001756", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001756.tgz", + "integrity": "sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==", "dev": true, "funding": [ { @@ -1998,9 +1998,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.256", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.256.tgz", - "integrity": "sha512-uqYq1IQhpXXLX+HgiXdyOZml7spy4xfy42yPxcCCRjswp0fYM2X+JwCON07lqnpLEGVCj739B7Yr+FngmHBMEQ==", + "version": "1.5.258", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.258.tgz", + "integrity": "sha512-rHUggNV5jKQ0sSdWwlaRDkFc3/rRJIVnOSe9yR4zrR07m3ZxhP4N27Hlg8VeJGGYgFTxK5NqDmWI4DSH72vIJg==", "dev": true, "license": "ISC" }, diff --git a/src/commands/fun/word-leaderboard.js b/src/commands/fun/word-leaderboard.js new file mode 100644 index 00000000..3f119bd6 --- /dev/null +++ b/src/commands/fun/word-leaderboard.js @@ -0,0 +1,74 @@ +import { SlashCommandBuilder } from "discord.js"; +import { embedReplyPrimaryColor } from "../../helpers/embeds/embed-reply.js"; +import { positionEmojis } from "../../helpers/format.js"; +import { checkIfNotInGuild } from "../../helpers/command-validation/general.js"; +import { logToFileAndDatabase } from "../../helpers/logger.js"; +import { query } from "../../helpers/db.js"; + +const commandName = "word-leaderboard"; + +export default { + data: new SlashCommandBuilder() + .setName(commandName) + // Counts a specified word in the current server and sends back a leaderboard with the users who used that word the most. + .setDescription( + "Gives back a word count leaderboard with the top users who used that word the most." + ) + .addStringOption((option) => + option + .setName("word") + .setDescription("The word that will get counted.") + .setRequired(true) + .setMinLength(2) + .setMaxLength(12) + ) + .setDMPermission(false), + async execute(interaction) { + await interaction.deferReply(); + + const guildCheck = checkIfNotInGuild(commandName, interaction); + if (guildCheck) { + return await replyAndLog(interaction, guildCheck); + } + + const targetWord = interaction.options.getString("word"); + const currentServerId = interaction.guild.id; + + const usersQuery = await query( + `SELECT senderUserId, senderUserName, COUNT(*) as wordCount + FROM messageLog + WHERE serverId = ? AND messageContent LIKE ? + GROUP BY senderUserId + ORDER BY wordCount DESC + LIMIT 10`, + [currentServerId, `%${targetWord}%`] + ); + + let leaderboardContent = ""; + for (let i = 0; i < usersQuery.length; i++) { + const userResult = usersQuery[i]; + const emoji = positionEmojis[i + 1]; + + let username; + try { + const member = await interaction.guild.members.fetch(userResult.senderUserId); + username = member.user.username; + } catch (error) { + username = userResult.senderUserName || "Unknown username"; + } + + leaderboardContent += `${emoji} <@${userResult.senderUserId}> (${username}): **#${userResult.wordCount}**\n`; + } + + const embedReply = embedReplyPrimaryColor( + `Word Leaderboard: "${targetWord}"`, + usersQuery.length !== 0 + ? `Leaderboard of users whose messages contained the word **${targetWord}** the most:\n\n${leaderboardContent}` + : `:x: No user has used the word **${targetWord}** in their messages so far.`, + interaction + ); + + await interaction.editReply({ embeds: [embedReply] }); + await logToFileAndDatabase(interaction, JSON.stringify(embedReply.toJSON())); + }, +}; diff --git a/src/data/commandData.csv b/src/data/commandData.csv index 5881c8d7..14e202d6 100644 --- a/src/data/commandData.csv +++ b/src/data/commandData.csv @@ -37,3 +37,4 @@ 193,cigany,fun,"Says ""fuj"" and sends a random pic of a ""person"" from a specific race." 194,cooldown,economy,Lets you check your or other users economy cooldowns for all or a specified economy command. 195,invite,utility,Gives you back a link for inviting the bot to any server where you have permissions to do so. +652,word-leaderboard,fun,Counts a specified word in the current server and sends back a leaderboard with the users who used that word the most through all their previous messages. diff --git a/src/events/scripts/messageCreate/messageLogging.js b/src/events/scripts/messageCreate/messageLogging.js index 01c9bca3..52b52804 100644 --- a/src/events/scripts/messageCreate/messageLogging.js +++ b/src/events/scripts/messageCreate/messageLogging.js @@ -29,9 +29,9 @@ export const logToDB = async (message) => { ] ); - console.log( - `Logged message "${message.content}" to database from ${senderUserName} in ${serverName}.` - ); + // console.log( + // `Logged message "${message.content}" to database from ${senderUserName} in ${serverName}.` + // ); } } } catch (error) { diff --git a/src/helpers/darwin/darwinCache.js b/src/helpers/darwin/darwinCache.js index d3b8ca10..b462fe7e 100644 --- a/src/helpers/darwin/darwinCache.js +++ b/src/helpers/darwin/darwinCache.js @@ -32,7 +32,7 @@ export const addToCache = async (directVideoUrl, forumPostUrl, videoTitle) => { try { const exists = await isInCache(directVideoUrl); if (exists) { - console.log(`URL already in cache: ${directVideoUrl}`); + // console.log(`URL already in cache: ${directVideoUrl}`); return true; } @@ -76,7 +76,7 @@ export const addToCache = async (directVideoUrl, forumPostUrl, videoTitle) => { export const isInCache = async (url) => { try { const videoId = extractVideoId(url); - console.log(`Checking "${url}" (ID: ${videoId}) in Darwin's cache`); + // console.log(`Checking "${url}" (ID: ${videoId}) in Darwin's cache`); const result = await query( "SELECT directVideoUrl FROM darwinCache WHERE directVideoUrl = ? OR videoId = ?", diff --git a/src/helpers/format.js b/src/helpers/format.js index a6744d52..a9abded9 100644 --- a/src/helpers/format.js +++ b/src/helpers/format.js @@ -15,3 +15,16 @@ export const formatRouletteColor = (color) => { console.error("The color you've provided for the formatRouletteColor() function is invalid."); } }; + +export const positionEmojis = { + 1: ":first_place:", + 2: ":second_place:", + 3: ":third_place:", + 4: ":number_4:", + 5: ":number_5:", + 6: ":number_6:", + 7: ":number_7:", + 8: ":number_8:", + 9: ":number_9:", + 10: ":number_10:", +}; diff --git a/src/index.js b/src/index.js index b50936d9..b44fb321 100644 --- a/src/index.js +++ b/src/index.js @@ -79,7 +79,7 @@ client.once(Events.ClientReady, (readyClient) => { setInterval(() => { if (darwinProcessRunning) { - console.log("Darwin process already running, skipping this execution"); + // console.log("Darwin process already running, skipping this execution"); return; } diff --git a/src/sql/darwinCache/migrate-to-new-schema.sql b/src/sql/darwinCache/migrate-to-new-schema.sql deleted file mode 100644 index 22c9bb2a..00000000 --- a/src/sql/darwinCache/migrate-to-new-schema.sql +++ /dev/null @@ -1,14 +0,0 @@ -ALTER TABLE `darwinCache` -CHANGE COLUMN `videoUrl` `directVideoUrl` VARCHAR(255) NOT NULL; - -ALTER TABLE `darwinCache` -ADD COLUMN `forumPostUrl` VARCHAR(255) NOT NULL DEFAULT '' AFTER `directVideoUrl`, -ADD COLUMN `videoTitle` VARCHAR(180) NOT NULL DEFAULT '' AFTER `videoId`; - -ALTER TABLE `darwinCache` -DROP INDEX `videoUrl`; - -ALTER TABLE `darwinCache` -ADD UNIQUE KEY `directVideoUrl` (`directVideoUrl`); - -DESCRIBE `darwinCache`;