diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..9fbed32 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,338 @@ +{ + "name": "speaker-bot", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "speaker-bot", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@discordjs/builders": "^1.7.0", + "@discordjs/rest": "^2.2.0", + "discord-api-types": "^0.37.71", + "discord.js": "^14.14.1", + "dotenv": "^16.4.5", + "fast-deep-equal": "^3.1.3", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "lodash.snakecase": "^4.1.1", + "magic-bytes.js": "^1.8.0", + "ts-mixer": "^6.0.4", + "tslib": "^2.6.2", + "undici": "^5.27.2", + "undici-types": "^5.26.5", + "ws": "^8.14.2" + } + }, + "node_modules/@discordjs/builders": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.7.0.tgz", + "integrity": "sha512-GDtbKMkg433cOZur8Dv6c25EHxduNIBsxeHrsRoIM8+AwmEZ8r0tEpckx/sHwTLwQPOF3e2JWloZh9ofCaMfAw==", + "dependencies": { + "@discordjs/formatters": "^0.3.3", + "@discordjs/util": "^1.0.2", + "@sapphire/shapeshift": "^3.9.3", + "discord-api-types": "0.37.61", + "fast-deep-equal": "^3.1.3", + "ts-mixer": "^6.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/builders/node_modules/discord-api-types": { + "version": "0.37.61", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.61.tgz", + "integrity": "sha512-o/dXNFfhBpYHpQFdT6FWzeO7pKc838QeeZ9d91CfVAtpr5XLK4B/zYxQbYgPdoMiTDvJfzcsLW5naXgmHGDNXw==" + }, + "node_modules/@discordjs/collection": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz", + "integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==", + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/formatters": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.3.3.tgz", + "integrity": "sha512-wTcI1Q5cps1eSGhl6+6AzzZkBBlVrBdc9IUhJbijRgVjCNIIIZPgqnUj3ntFODsHrdbGU8BEG9XmDQmgEEYn3w==", + "dependencies": { + "discord-api-types": "0.37.61" + }, + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/formatters/node_modules/discord-api-types": { + "version": "0.37.61", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.61.tgz", + "integrity": "sha512-o/dXNFfhBpYHpQFdT6FWzeO7pKc838QeeZ9d91CfVAtpr5XLK4B/zYxQbYgPdoMiTDvJfzcsLW5naXgmHGDNXw==" + }, + "node_modules/@discordjs/rest": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.2.0.tgz", + "integrity": "sha512-nXm9wT8oqrYFRMEqTXQx9DUTeEtXUDMmnUKIhZn6O2EeDY9VCdwj23XCPq7fkqMPKdF7ldAfeVKyxxFdbZl59A==", + "dependencies": { + "@discordjs/collection": "^2.0.0", + "@discordjs/util": "^1.0.2", + "@sapphire/async-queue": "^1.5.0", + "@sapphire/snowflake": "^3.5.1", + "@vladfrangu/async_event_emitter": "^2.2.2", + "discord-api-types": "0.37.61", + "magic-bytes.js": "^1.5.0", + "tslib": "^2.6.2", + "undici": "5.27.2" + }, + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/rest/node_modules/@discordjs/collection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.0.0.tgz", + "integrity": "sha512-YTWIXLrf5FsrLMycpMM9Q6vnZoR/lN2AWX23/Cuo8uOOtS8eHB2dyQaaGnaF8aZPYnttf2bkLMcXn/j6JUOi3w==", + "engines": { + "node": ">=18" + } + }, + "node_modules/@discordjs/rest/node_modules/discord-api-types": { + "version": "0.37.61", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.61.tgz", + "integrity": "sha512-o/dXNFfhBpYHpQFdT6FWzeO7pKc838QeeZ9d91CfVAtpr5XLK4B/zYxQbYgPdoMiTDvJfzcsLW5naXgmHGDNXw==" + }, + "node_modules/@discordjs/util": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.0.2.tgz", + "integrity": "sha512-IRNbimrmfb75GMNEjyznqM1tkI7HrZOf14njX7tCAAUetyZM1Pr8hX/EK2lxBCOgWDRmigbp24fD1hdMfQK5lw==", + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/ws": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.0.2.tgz", + "integrity": "sha512-+XI82Rm2hKnFwAySXEep4A7Kfoowt6weO6381jgW+wVdTpMS/56qCvoXyFRY0slcv7c/U8My2PwIB2/wEaAh7Q==", + "dependencies": { + "@discordjs/collection": "^2.0.0", + "@discordjs/rest": "^2.1.0", + "@discordjs/util": "^1.0.2", + "@sapphire/async-queue": "^1.5.0", + "@types/ws": "^8.5.9", + "@vladfrangu/async_event_emitter": "^2.2.2", + "discord-api-types": "0.37.61", + "tslib": "^2.6.2", + "ws": "^8.14.2" + }, + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/ws/node_modules/@discordjs/collection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.0.0.tgz", + "integrity": "sha512-YTWIXLrf5FsrLMycpMM9Q6vnZoR/lN2AWX23/Cuo8uOOtS8eHB2dyQaaGnaF8aZPYnttf2bkLMcXn/j6JUOi3w==", + "engines": { + "node": ">=18" + } + }, + "node_modules/@discordjs/ws/node_modules/discord-api-types": { + "version": "0.37.61", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.61.tgz", + "integrity": "sha512-o/dXNFfhBpYHpQFdT6FWzeO7pKc838QeeZ9d91CfVAtpr5XLK4B/zYxQbYgPdoMiTDvJfzcsLW5naXgmHGDNXw==" + }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@sapphire/async-queue": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.2.tgz", + "integrity": "sha512-7X7FFAA4DngXUl95+hYbUF19bp1LGiffjJtu7ygrZrbdCSsdDDBaSjB7Akw0ZbOu6k0xpXyljnJ6/RZUvLfRdg==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@sapphire/shapeshift": { + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.9.6.tgz", + "integrity": "sha512-4+Na/fxu2SEepZRb9z0dbsVh59QtwPuBg/UVaDib3av7ZY14b14+z09z6QVn0P6Dv6eOU2NDTsjIi0mbtgP56g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@sapphire/snowflake": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.1.tgz", + "integrity": "sha512-BxcYGzgEsdlG0dKAyOm0ehLGm2CafIrfQTZGWgkfKYbj+pNNsorZ7EotuZukc2MT70E0UbppVbtpBrqpzVzjNA==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@types/node": { + "version": "20.11.22", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.22.tgz", + "integrity": "sha512-/G+IxWxma6V3E+pqK1tSl2Fo1kl41pK1yeCyDsgkF9WlVAme4j5ISYM2zR11bgLFJGLN5sVK40T4RJNuiZbEjA==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/ws": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz", + "integrity": "sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vladfrangu/async_event_emitter": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.4.tgz", + "integrity": "sha512-ButUPz9E9cXMLgvAW8aLAKKJJsPu1dY1/l/E8xzLFuysowXygs6GBcyunK9rnGC4zTsnIc2mQo71rGw9U+Ykug==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/discord-api-types": { + "version": "0.37.71", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.71.tgz", + "integrity": "sha512-oYDVWoiQdblr9DpwOgpi5d78dVhPcoN9YZCCqYZf2T0v9+iICs7k2bYGumoHuYMtaIitpp5aQNs+2guVkgjbOA==" + }, + "node_modules/discord.js": { + "version": "14.14.1", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.14.1.tgz", + "integrity": "sha512-/hUVzkIerxKHyRKopJy5xejp4MYKDPTszAnpYxzVVv4qJYf+Tkt+jnT2N29PIPschicaEEpXwF2ARrTYHYwQ5w==", + "dependencies": { + "@discordjs/builders": "^1.7.0", + "@discordjs/collection": "1.5.3", + "@discordjs/formatters": "^0.3.3", + "@discordjs/rest": "^2.1.0", + "@discordjs/util": "^1.0.2", + "@discordjs/ws": "^1.0.2", + "@sapphire/snowflake": "3.5.1", + "@types/ws": "8.5.9", + "discord-api-types": "0.37.61", + "fast-deep-equal": "3.1.3", + "lodash.snakecase": "4.1.1", + "tslib": "2.6.2", + "undici": "5.27.2", + "ws": "8.14.2" + }, + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/discord.js/node_modules/discord-api-types": { + "version": "0.37.61", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.61.tgz", + "integrity": "sha512-o/dXNFfhBpYHpQFdT6FWzeO7pKc838QeeZ9d91CfVAtpr5XLK4B/zYxQbYgPdoMiTDvJfzcsLW5naXgmHGDNXw==" + }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" + }, + "node_modules/magic-bytes.js": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.8.0.tgz", + "integrity": "sha512-lyWpfvNGVb5lu8YUAbER0+UMBTdR63w2mcSUlhhBTyVbxJvjgqwyAf3AZD6MprgK0uHuBoWXSDAMWLupX83o3Q==" + }, + "node_modules/ts-mixer": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", + "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==" + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/undici": { + "version": "5.27.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.27.2.tgz", + "integrity": "sha512-iS857PdOEy/y3wlM3yRp+6SNQQ6xU0mmZcwRSriqk+et/cwWAtwmIGf6WkoDN2EK/AMdCO/dfXzIwi+rFMrjjQ==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/ws": { + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..6c90a76 --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "speaker-bot", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "dependencies": { + "@discordjs/builders": "^1.7.0", + "@discordjs/rest": "^2.2.0", + "discord-api-types": "^0.37.71", + "discord.js": "^14.14.1", + "dotenv": "^16.4.5", + "fast-deep-equal": "^3.1.3", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "lodash.snakecase": "^4.1.1", + "magic-bytes.js": "^1.8.0", + "ts-mixer": "^6.0.4", + "tslib": "^2.6.2", + "undici": "^5.27.2", + "undici-types": "^5.26.5", + "ws": "^8.14.2" + }, + "scripts": { + "start": "node src/index.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/phrases.yml b/phrases.yml new file mode 100644 index 0000000..25bcbc7 --- /dev/null +++ b/phrases.yml @@ -0,0 +1,33 @@ +phrases: + - trigger: ".*(I).*(motion|proposal).*(speaker).*" + response: "A motion has been recognized. Please state your motion clearly for the house's consideration." + + - trigger: ".*(point).*(order).*(speaker).*" + response: "A Point of Order has been recognized. I ask the current speaker to pause. Please state your point, member." + + - trigger: ".*(request).*(speak|address).*(speaker).*" + response: "A request to speak has been acknowledged. Please wait for recognition before proceeding." + + - trigger: ".*(information).*(request).*(speaker).*" + response: "A Request for Information has been noted. What information do you seek, member?" + + - trigger: ".*(call).*(question).*(speaker).*" + response: "A Call to Question has been noted. We will proceed to vote on the current motion shortly." + + - trigger: ".*(second).*(motion).*(speaker).*" + response: "The motion has been seconded. Discussion will now commence." + + - trigger: ".*(adjourn).*(request|motion).*(speaker).*" + response: "A motion to adjourn has been recognized. Please stand by for the vote." + + - trigger: ".*(roll).*(call).*(vote).*(speaker).*" + response: "A Roll Call Vote has been requested. Members will now vote on the motion in order." + + - trigger: ".*(unanimous).*(consent).*(speaker).*" + response: "Unanimous Consent has been requested. Is there any objection?" + + - trigger: ".*(quorum).*(call).*(speaker).*" + response: "A Quorum Call has been initiated. Members, please indicate your presence." + + - trigger: ".*(point).*(privilege).*(speaker).*" + response: "A Point of Privilege is raised. Please state the nature of your privilege, member." diff --git a/src/commands/deploy-commands.js b/src/commands/deploy-commands.js new file mode 100644 index 0000000..e5a94af --- /dev/null +++ b/src/commands/deploy-commands.js @@ -0,0 +1,42 @@ +import { config } from 'dotenv'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import { SlashCommandBuilder } from '@discordjs/builders'; +import { REST } from '@discordjs/rest'; +import { Routes } from 'discord-api-types/v9'; + +// Load environment variables from .env file +config(); + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const commands = []; +const commandsPath = path.join(__dirname, 'commands'); +const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js') && file !== 'deploy-commands.js'); + +for (const file of commandFiles) { + const filePath = path.join(commandsPath, file); + import(filePath).then((command) => { + if (command.data instanceof SlashCommandBuilder) { + commands.push(command.data.toJSON()); + } + }); +} + +const rest = new REST({ version: '9' }).setToken(process.env.DISCORD_TOKEN); + +// Due to the asynchronous nature of dynamic imports, we need to ensure all commands are loaded before proceeding +const waitForCommandsToLoad = async () => { + while (commands.length < commandFiles.length) { + await new Promise(resolve => setTimeout(resolve, 100)); + } + + // Once all commands are loaded, proceed to register them + rest.put(Routes.applicationGuildCommands(process.env.CLIENT_ID, process.env.GUILD_ID), { body: commands }) + .then(() => console.log('Successfully registered application commands.')) + .catch(console.error); +}; + +waitForCommandsToLoad(); diff --git a/src/commands/help.js b/src/commands/help.js new file mode 100644 index 0000000..22fba3e --- /dev/null +++ b/src/commands/help.js @@ -0,0 +1,48 @@ +import { SlashCommandBuilder } from '@discordjs/builders'; +import { MessageEmbed } from 'discord.js'; + +// Define the command data using SlashCommandBuilder +export const data = new SlashCommandBuilder() + .setName('help') + .setDescription('List all my commands or info about a specific command.') + .addStringOption(option => + option.setName('command') + .setDescription('The command to get help for') + .setRequired(false)); + +// The execute function now needs to be exported separately and aligned with how commands are dynamically loaded. +export async function execute(interaction) { + const commands = interaction.client.commands.cache; + + if (!interaction.options.getString('command')) { + // Generate a list of all commands if no specific command is asked + const commandList = Array.from(commands.values()).map(cmd => cmd.data.name).join(', '); + const embed = new MessageEmbed() + .setTitle('Available Commands') + .setDescription(commandList) + .setColor('#0099ff') + .setFooter('Use /help [command name] to get detailed information about a specific command.'); + + await interaction.reply({ embeds: [embed], ephemeral: true }); + } else { + // Provide detailed help for a specific command + const name = interaction.options.getString('command'); + const command = commands.get(name); + + if (!command) { + await interaction.reply({ content: `No information found for command: ${name}`, ephemeral: true }); + return; + } + + const embed = new MessageEmbed() + .setTitle(`Command: ${command.data.name}`) + .setDescription(command.data.description || 'No description available.') + .setColor('#0099ff'); + + // Assuming 'usage' and 'examples' are properties you might add to your command objects for detailed help + if (command.usage) embed.addField('Usage', command.usage); + if (command.examples) embed.addField('Examples', command.examples.join('\n')); + + await interaction.reply({ embeds: [embed], ephemeral: true }); + } +} diff --git a/src/commands/listPhrases.js b/src/commands/listPhrases.js new file mode 100644 index 0000000..fa12a29 --- /dev/null +++ b/src/commands/listPhrases.js @@ -0,0 +1,27 @@ +import { MessageEmbed } from 'discord.js'; +import { loadPhrases } from '../utils/phrasesLoader.js'; + +export const name = 'listphrases'; // Ensure this matches the command name you registered with Discord. +export const description = 'Lists all phrases the bot is listening for.'; + +export async function execute(interaction) { + if (!interaction.isCommand() || interaction.commandName !== 'listphrases') return; + + const phrases = loadPhrases(); + + const embed = new MessageEmbed() + .setTitle('Phrases I Listen For') + .setColor('#0099ff'); + + // Limiting embed size due to Discord's embed limits + const maxFields = 25; + phrases.slice(0, maxFields).forEach(({ trigger, response }) => { + embed.addField(`Trigger: ${trigger}`, `Response: ${response}`, false); + }); + + if (phrases.length > maxFields) { + embed.setFooter('Only showing the first 25 phrases due to embed limits.'); + } + + await interaction.reply({ embeds: [embed] }); +} diff --git a/src/events/messageCreate.js b/src/events/messageCreate.js new file mode 100644 index 0000000..6aa8678 --- /dev/null +++ b/src/events/messageCreate.js @@ -0,0 +1,20 @@ +import { loadPhrases } from '../utils/phrasesLoader.js'; + +export const name = 'messageCreate'; + +export function execute(message) { + if (message.author.bot) return; + + checkForPhrases(message); +} + +function checkForPhrases(message) { + const phrases = loadPhrases(); + + phrases.forEach(({ trigger, response }) => { + const regex = new RegExp(trigger, 'i'); + if (regex.test(message.content)) { + message.reply(response); + } + }); +} diff --git a/src/events/ready.js b/src/events/ready.js new file mode 100644 index 0000000..e777872 --- /dev/null +++ b/src/events/ready.js @@ -0,0 +1,9 @@ +// events/ready.js +import { logger } from '../utils/logger.js'; // Adjust the path as necessary + +export const name = 'ready'; +export const once = true; +export function execute(client) { + logger(client, 'Speaker Bot is online!'); + // The 'client' parameter is now passed to the execute function automatically by discord.js event handling +} diff --git a/src/interactions/PointOfPrivilegeInteraction.js b/src/interactions/PointOfPrivilegeInteraction.js new file mode 100644 index 0000000..e69de29 diff --git a/src/utils/logger.js b/src/utils/logger.js new file mode 100644 index 0000000..944ecfe --- /dev/null +++ b/src/utils/logger.js @@ -0,0 +1,17 @@ +function sendLogToDiscord(client, message, isError = false) { + const channelId = process.env.LOG_CHANNEL_ID; // Ensure this is set in your .env + const channel = client.channels.cache.get(channelId); + if (channel) { + channel.send(`${isError ? 'ERROR: ' : ''}${message}`); + } else { + console.error('Log channel not found.'); + } +} + +export function logger(client, message, level = 'info') { + console.log(message); // Log to console for all messages + if (level === 'error') { + // Send only error messages to a dedicated Discord channel + sendLogToDiscord(client, message, true); + } +} diff --git a/src/utils/phrasesLoader.js b/src/utils/phrasesLoader.js new file mode 100644 index 0000000..43dfb4d --- /dev/null +++ b/src/utils/phrasesLoader.js @@ -0,0 +1,13 @@ +import fs from 'fs'; +import yaml from 'js-yaml'; + +export function loadPhrases() { + try { + const fileContents = fs.readFileSync('./phrases.yml', 'utf8'); + const config = yaml.load(fileContents); + return config.phrases; + } catch (error) { + console.error('Error loading phrases from YAML file:', error); + return []; + } +}