diff --git a/package-lock.json b/package-lock.json index aeee32d..9448aa0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "@devraelfreeze/discordjs-pagination": "^2.7.6", + "axios": "^1.7.2", "discord.js": "^14.14.1", "mysql2": "^3.10.0", "ts-node": "^10.9.2" @@ -265,11 +266,45 @@ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", @@ -320,6 +355,38 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/generate-function": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", @@ -369,6 +436,25 @@ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mysql2": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.10.0.tgz", @@ -414,6 +500,11 @@ "node": ">=12" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", diff --git a/package.json b/package.json index f45f340..b2d6e5c 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "license": "ISC", "dependencies": { "@devraelfreeze/discordjs-pagination": "^2.7.6", + "axios": "^1.7.2", "discord.js": "^14.14.1", "mysql2": "^3.10.0", "ts-node": "^10.9.2" diff --git a/src/bot.ts b/src/bot.ts index 190ee1b..8f7f4e9 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -6,13 +6,10 @@ import { Events, GatewayIntentBits, Message, - PermissionsBitField, REST, Routes, User, - APIUser } from 'discord.js'; -import * as sql from 'mysql2'; import config from "./config"; import Fact from "./commands/fact"; import AddFact from "./commands/addFact"; @@ -20,15 +17,19 @@ import RemoveFact from "./commands/removeFact"; import EnableFact from "./commands/enableFact"; import EditFact from "./commands/editFact"; import Facts from "./commands/facts"; +import axios from 'axios'; import {pagination} from "@devraelfreeze/discordjs-pagination"; export class Bot { - private connection: sql.Connection; + public axiosClient; private static instance: Bot; public init() { const client = new Client({intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent]}); - this.connectDatabase(true); + this.axiosClient = axios.create({ + baseURL: config.apiUrl, + }); + this.axiosClient.defaults.headers.common['x-api-key'] = config.apiKey; this.registerCommands(); const commands: Collection = new Collection(); @@ -108,31 +109,6 @@ export class Bot { })(); } - private connectDatabase(closeConnection: boolean) { - this.connection = sql.createConnection({ - host: config.dbHost, - user: config.dbUser, - password: config.dbPassword, - database: config.dbName, - port: config.dbPort, - }) - - this.connection.execute('CREATE TABLE if NOT EXISTS facts (id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, content TEXT NOT NULL, active TINYINT(1) NOT NULL DEFAULT 1, fake TINYINT(1) NOT NULL DEFAULT 1, counter INT UNSIGNED NOT NULL DEFAULT 0);'); - - if (closeConnection) { - this.endConection(); - } - } - - public getConnection() { - this.connectDatabase(false); - return this.connection; - } - - public endConection() { - this.connection.end(); - } - public static getInstance(): Bot { if (!Bot.instance) { Bot.instance = new Bot(); @@ -142,13 +118,23 @@ export class Bot { } public async sendEmbed(msg: Message | ChatInputCommandInteraction) { - const [results] = await this.getConnection().promise().execute('SELECT * FROM facts;'); - this.endConection(); + let facts = []; + try { + const responseActive = await this.axiosClient.get('/facts-active'); + const responseInactive = await this.axiosClient.get('/facts-active'); + facts = responseActive.data.facts.concat(responseInactive.data.facts); + } catch (error) { + console.log('There was an error while querying the facts! ' + error.response.data.error + ': ' + error.response.data.message); + return await msg.reply({ + content: 'There was an error while executing this command!', + ephemeral: true, + }); + } let fields = []; let embeds = []; - for (let index in results) { - let result = results[index]; + for (let index in facts) { + let result = facts[index]; fields.push({ name: 'content', @@ -169,8 +155,14 @@ export class Bot { }); fields.push({ - name: 'fake', - value: result.fake ? 'True' : 'False', + name: 'real', + value: result.real ? 'True' : 'False', + inline: true, + }); + + fields.push({ + name: 'source', + value: result.source, inline: true, }); diff --git a/src/commands/addFact.ts b/src/commands/addFact.ts index 84cae2a..65dc237 100644 --- a/src/commands/addFact.ts +++ b/src/commands/addFact.ts @@ -12,14 +12,19 @@ const factOption = (new SlashCommandStringOption()) .setDescription('The fact.') .setRequired(true); +const sourceOption = (new SlashCommandStringOption()) + .setName('source') + .setDescription('The source of the fact.') + .setRequired(true); + const activeOption = (new SlashCommandBooleanOption()) .setName('active') .setDescription('Should the fact be used?') .setRequired(false); const fakeOption = (new SlashCommandBooleanOption()) - .setName('fake') - .setDescription('Is this a fake fact?') + .setName('real') + .setDescription('Is this a real fact?') .setRequired(false); export default { @@ -27,25 +32,30 @@ export default { .setName('addfact') .setDescription('Add a logtastic fact.') .addStringOption(factOption) + .addStringOption(sourceOption) .addBooleanOption(activeOption) .addBooleanOption(fakeOption) .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator), execute: async function (interaction: ChatInputCommandInteraction) { const bot = Bot.getInstance(); - await bot.getConnection().promise().execute( - 'INSERT INTO facts SET content = ?, active = ?, fake = ?;', - [ - interaction.options.data.at(0).value, - interaction.options.data.at(1)?.value ?? true, - interaction.options.data.at(2)?.value ?? false, - ] - ); - bot.endConection(); - console.log('Fact has been saved!'); + await bot.axiosClient.post('/createfact', { + content: interaction.options.data.at(0)?.value, + source: interaction.options.data.at(1)?.value, + active: interaction.options.data.at(2)?.value ?? true, + real: interaction.options.data.at(3)?.value ?? true, + }).catch(async function (error) { + console.log('There was an error while adding the fact! ' + error.response.data.error + ': ' + error.response.data.message); + return await interaction.reply({ + content: 'There was an error while executing this command!', + ephemeral: true, + }); + }); + console.log('Fact has been saved!'); return await interaction.reply({ content: 'Done!', ephemeral: true, }); + }, }; \ No newline at end of file diff --git a/src/commands/editFact.ts b/src/commands/editFact.ts index 30389c1..d3604bf 100644 --- a/src/commands/editFact.ts +++ b/src/commands/editFact.ts @@ -15,16 +15,21 @@ const idOption = (new SlashCommandIntegerOption()) const factOption = (new SlashCommandStringOption()) .setName('fact') .setDescription('The fact.') - .setRequired(true); + .setRequired(false); + +const sourceOption = (new SlashCommandStringOption()) + .setName('source') + .setDescription('The source of the fact.') + .setRequired(false); const activeOption = (new SlashCommandBooleanOption()) .setName('active') .setDescription('Should the fact be enabled?') .setRequired(false); -const fakeOption = (new SlashCommandBooleanOption()) - .setName('fake') - .setDescription('Is this a fake fact?') +const realOption = (new SlashCommandBooleanOption()) + .setName('real') + .setDescription('Is this a real fact?') .setRequired(false); export default { @@ -33,25 +38,46 @@ export default { .setDescription('Edit a logtastic fact.') .addIntegerOption(idOption) .addStringOption(factOption) + .addStringOption(sourceOption) .addBooleanOption(activeOption) - .addBooleanOption(fakeOption) + .addBooleanOption(realOption) .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator), execute: async function (interaction: ChatInputCommandInteraction) { const bot = Bot.getInstance(); - await bot.getConnection().promise().execute( - 'UPDATE facts SET content = ?, active = IF(? != NULL, ?, active), fake = IF(? != NULL, ?, fake) WHERE id = ?;', - [ - interaction.options.data.at(1).value, - interaction.options.data.at(2)?.value ?? null, - interaction.options.data.at(2)?.value ?? null, - interaction.options.data.at(3)?.value ?? null, - interaction.options.data.at(3)?.value ?? null, - interaction.options.data.at(0).value, - ] - ); - bot.endConection(); - console.log('Fact has been saved!'); + const factId = interaction.options.data.at(0).value; + let response = await bot.axiosClient.get('/fact/' + factId) + .catch(async function (error) { + console.log('There was an error while querying the fact! Tried to query fact with id ' + factId + '. ' + error.response.data.error + ': ' + error.response.data.message); + return await interaction.reply({ + content: 'There was an error while executing this command!', + ephemeral: true, + }); + }); + + if (response.data.error) { + console.log('There was an error while querying the fact! Tried to query fact with id ' + factId + '.'); + return await interaction.reply({ + content: 'There was an error while executing this command!', + ephemeral: true, + }); + } + + const fact = response.data.fact; + response = await bot.axiosClient.patch('/updatefact/' + factId, { + content: interaction.options.data.at(1)?.value ?? fact.content, + source: interaction.options.data.at(2)?.value ?? fact.source, + active: interaction.options.data.at(3)?.value ?? fact.active, + real: interaction.options.data.at(4)?.value ?? fact.real, + }).catch(async function (error) { + console.log('There was an error while updating the fact! Tried to edit fact with id ' + factId + '. ' + error.response.data.error + ': ' + error.response.data.message); + return await interaction.reply({ + content: 'There was an error while executing this command!', + ephemeral: true, + }); + }); + + console.log('Fact has been saved!'); return await interaction.reply({ content: 'Done!', ephemeral: true, diff --git a/src/commands/enableFact.ts b/src/commands/enableFact.ts index 5e65cd9..a52e7b1 100644 --- a/src/commands/enableFact.ts +++ b/src/commands/enableFact.ts @@ -20,13 +20,38 @@ export default { .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator), execute: async function (interaction: ChatInputCommandInteraction) { const bot = Bot.getInstance(); - await bot.getConnection().promise().execute( - 'UPDATE facts SET active = 1 WHERE id = ?;', - [interaction.options.data.at(0).value] - ); - bot.endConection(); - console.log('Fact has been enabled!'); + const factId = interaction.options.data.at(0).value; + let response = await bot.axiosClient.get('/fact/' + factId) + .catch(async function (error) { + console.log('There was an error while querying the fact! Tried to query fact with id ' + factId + '. ' + error.response.data.error + ': ' + error.response.data.message); + return await interaction.reply({ + content: 'There was an error while executing this command!', + ephemeral: true, + }); + }); + if (response.data.error) { + console.log('There was an error while querying the fact! Tried to query fact with id ' + factId + '.'); + return await interaction.reply({ + content: 'There was an error while executing this command!', + ephemeral: true, + }); + } + + const fact = response.data.fact; + response = await bot.axiosClient.patch('/updatefact/' + factId, { + content: fact.content, + source: fact.source, + active: true + }).catch(async function (error) { + console.log('There was an error while updating the fact! Tried to enable fact with id ' + factId + '. ' + error.response.data.error + ': ' + error.response.data.message); + return await interaction.reply({ + content: 'There was an error while executing this command!', + ephemeral: true, + }); + }); + + console.log('Fact has been enabled!'); return await interaction.reply({ content: 'Done!', ephemeral: true, diff --git a/src/commands/fact.ts b/src/commands/fact.ts index 033c3f1..53804e8 100644 --- a/src/commands/fact.ts +++ b/src/commands/fact.ts @@ -1,5 +1,5 @@ import { - ChatInputCommandInteraction, + ChatInputCommandInteraction, EmbedBuilder, SlashCommandBuilder, } from 'discord.js'; import {Bot} from "../bot"; @@ -10,16 +10,26 @@ export default { .setDescription('Get a logtastic fact.'), execute: async function (interaction: ChatInputCommandInteraction) { const bot = Bot.getInstance(); - const [results] = await bot.getConnection().promise().execute('SELECT * FROM facts WHERE active = 1 ORDER BY RAND() LIMIT 1;'); - bot.endConection(); - let fact = results[0]; - await bot.getConnection().promise().execute('UPDATE facts SET counter = counter + 1 WHERE id = ?', [fact.id]); - bot.endConection(); + const response = await bot.axiosClient.get('/randomfact') + .catch(async function (error) { + console.log('There was an error while querying the fact! ' + error.response.data.error + ': ' + error.response.data.message); + return await interaction.reply({ + content: 'There was an error while executing this command!', + ephemeral: true, + }); + }); + + const fact = response.data.fact; + const embed = new EmbedBuilder() + .setTitle('Random Fact') + .setDescription(fact.content) + .setURL(fact.source) + .setColor(0xffe600); + console.log('Sent beaver fact with id: ' + fact.id); - return await interaction.reply({ - content: fact.content, + embeds: [embed], }); }, }; \ No newline at end of file diff --git a/src/commands/removeFact.ts b/src/commands/removeFact.ts index fa6e63c..65f9f7e 100644 --- a/src/commands/removeFact.ts +++ b/src/commands/removeFact.ts @@ -26,22 +26,52 @@ export default { .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator), execute: async function (interaction: ChatInputCommandInteraction) { const bot = Bot.getInstance(); + + const factId = interaction.options.data.at(0).value; + let response = await bot.axiosClient.get('/fact/' + factId) + .catch(async function (error) { + console.log('There was an error while querying the fact! Tried to query fact with id ' + factId + '. ' + response.data.error + ': ' + response.data.message); + return await interaction.reply({ + content: 'There was an error while executing this command!', + ephemeral: true, + }); + }); + + if (response.data.error) { + console.log('There was an error while querying the fact! Tried to query fact with id ' + factId + '.'); + return await interaction.reply({ + content: 'There was an error while executing this command!', + ephemeral: true, + }); + } + + const fact = response.data.fact; if (interaction.options.data.at(1)?.value ?? false) { - await bot.getConnection().promise().execute( - 'DELETE FROM facts WHERE id = ?;', - [interaction.options.data.at(0).value] - ); + const response = await bot.axiosClient.delete('/deletefact/' + factId) + .catch(async function (error) { + console.log('There was an error while deleting the fact! Tried to delete fact with id ' + factId + '. ' + error.response.data.error + ': ' + error.response.data.message); + return await interaction.reply({ + content: 'There was an error while executing this command!', + ephemeral: true, + }); + }); console.log('Fact has been deleted!'); } else { - await bot.getConnection().promise().execute( - 'UPDATE facts SET active = 0 WHERE id = ?;', - [interaction.options.data.at(0).value] - ); + const response = await bot.axiosClient.patch('/updatefact/' + factId, { + content: fact.content, + source: fact.source, + active: false + }).catch(async function (error) { + console.log('There was an error while updating the fact! Tried to disable fact with id ' + factId + '. ' + error.response.data.error + ': ' + error.response.data.message); + return await interaction.reply({ + content: 'There was an error while executing this command!', + ephemeral: true, + }); + }); console.log('Fact has been disabled!'); } - bot.endConection(); return await interaction.reply({ content: 'Done!', diff --git a/src/config.ts b/src/config.ts index dc385a5..e2ae95b 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,10 +1,7 @@ export default { - dbName: '', - dbHost: '', - dbPort: 3306, - dbUser: '', - dbPassword: '', token: '', guildId: '', clientId: '', + apiKey: '', + apiUrl: '' }; \ No newline at end of file