diff --git a/.eslintrc.json b/.eslintrc.json index 3c89880..5ba7ee8 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,6 @@ { "env": { - "es6": true, + "es2020": true, "node": true }, "extends": ["eslint:recommended"], diff --git a/commands/general/ping.js b/commands/general/ping.js index 8740288..5e40723 100644 --- a/commands/general/ping.js +++ b/commands/general/ping.js @@ -6,7 +6,7 @@ class PingCommand extends Command { const pingMessage = await this.client.createMessage(this.channel.id, Object.assign({ content: "🏓 Ping?" }, this.reference)); - pingMessage.edit(`🏓 Pong!\n\`\`\`\nLatency: ${pingMessage.timestamp - this.message.timestamp}ms${this.channel.guild ? `\nShard Latency: ${Math.round(this.client.shards.get(this.client.guildShardMap[this.channel.guild.id]).latency)}ms` : ""}\n\`\`\``); + await pingMessage.edit(`🏓 Pong!\n\`\`\`\nLatency: ${pingMessage.timestamp - this.message.timestamp}ms${this.channel.guild ? `\nShard Latency: ${Math.round(this.client.shards.get(this.client.guildShardMap[this.channel.guild.id]).latency)}ms` : ""}\n\`\`\``); } else { await this.interaction.createMessage("🏓 Ping?"); const pingMessage = await this.interaction.getOriginalMessage(); diff --git a/commands/image-editing/swirl.js b/commands/image-editing/swirl.js index 83fb287..476b506 100644 --- a/commands/image-editing/swirl.js +++ b/commands/image-editing/swirl.js @@ -2,7 +2,7 @@ import ImageCommand from "../../classes/imageCommand.js"; class SwirlCommand extends ImageCommand { static description = "Swirls an image"; - static aliases = ["whirlpool"]; + static aliases = ["whirlpool", "distort"]; static noImage = "You need to provide an image/GIF to swirl!"; static command = "swirl"; diff --git a/package.json b/package.json index f6c0203..13361e5 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,15 @@ }, "scripts": { "build": "cmake-js compile", + "build:debug": "cmake-js compile -DCMAKE_BUILD_TYPE=Debug", "docker:build": "DOCKER_BUILDKIT=1 docker build -t esmbot .", "docker:run-bot": "docker run --rm --network=host esmbot", "docker:run-api": "docker run --rm --network=host esmbot-api", "docker:run-lava": "docker run --rm --network host -v \"$(pwd)\"/application.yml:/opt/Lavalink/application.yml -v \"$(pwd)\"/assets:/opt/Lavalink/assets fredboat/lavalink:dev", - "start": "node app.js" + "start": "node app.js", + "start:debug": "DEBUG=true node app.js", + "start-api": "node api/index.js", + "start-api:debug": "DEBUG=true node app.js" }, "author": "Essem ", "license": "MIT", @@ -53,7 +57,7 @@ "better-sqlite3": "^7.6.2", "bufferutil": "^4.0.6", "erlpack": "github:abalabahaha/erlpack", - "pg": "^8.8.0", + "postgres": "^3.2.4", "uuid": "^8.3.2", "ws": "^8.8.1", "zlib-sync": "^0.1.7" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 096593d..18eee5e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,7 +21,7 @@ specifiers: jsqr: ^1.4.0 node-addon-api: ^5.0.0 node-emoji: ^1.11.0 - pg: ^8.8.0 + postgres: ^3.2.4 qrcode: ^1.5.1 sharp: ^0.30.7 shoukaku: github:Deivu/shoukaku @@ -55,7 +55,7 @@ optionalDependencies: better-sqlite3: 7.6.2 bufferutil: 4.0.6 erlpack: github.com/abalabahaha/erlpack/f7d730debe32c416d1b55b4217f8aef2ade05874 - pg: 8.8.0 + postgres: 3.2.4 uuid: 8.3.2 ws: 8.8.1_bufferutil@4.0.6 zlib-sync: 0.1.7 @@ -658,12 +658,6 @@ packages: resolution: {integrity: sha512-Zy8ZXMyxIT6RMTeY7OP/bDndfj6bwCan7SS98CEndS6deHwWPpseeHlwarNcBim+etXnF9HBc1non5JgDaJU1g==} dev: false - /buffer-writer/2.0.0: - resolution: {integrity: sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==} - engines: {node: '>=4'} - dev: false - optional: true - /buffer/5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} dependencies: @@ -1864,11 +1858,6 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - /packet-reader/1.0.0: - resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==} - dev: false - optional: true - /parent-module/1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -1913,70 +1902,6 @@ packages: engines: {node: '>=14.16'} dev: false - /pg-connection-string/2.5.0: - resolution: {integrity: sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==} - dev: false - optional: true - - /pg-int8/1.0.1: - resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} - engines: {node: '>=4.0.0'} - dev: false - optional: true - - /pg-pool/3.5.2_pg@8.8.0: - resolution: {integrity: sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==} - peerDependencies: - pg: '>=8.0' - dependencies: - pg: 8.8.0 - dev: false - optional: true - - /pg-protocol/1.5.0: - resolution: {integrity: sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==} - dev: false - optional: true - - /pg-types/2.2.0: - resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} - engines: {node: '>=4'} - dependencies: - pg-int8: 1.0.1 - postgres-array: 2.0.0 - postgres-bytea: 1.0.0 - postgres-date: 1.0.7 - postgres-interval: 1.2.0 - dev: false - optional: true - - /pg/8.8.0: - resolution: {integrity: sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==} - engines: {node: '>= 8.0.0'} - requiresBuild: true - peerDependencies: - pg-native: '>=3.0.1' - peerDependenciesMeta: - pg-native: - optional: true - dependencies: - buffer-writer: 2.0.0 - packet-reader: 1.0.0 - pg-connection-string: 2.5.0 - pg-pool: 3.5.2_pg@8.8.0 - pg-protocol: 1.5.0 - pg-types: 2.2.0 - pgpass: 1.0.5 - dev: false - optional: true - - /pgpass/1.0.5: - resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} - dependencies: - split2: 4.1.0 - dev: false - optional: true - /picocolors/1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} dev: true @@ -1996,29 +1921,9 @@ packages: engines: {node: '>=10.13.0'} dev: false - /postgres-array/2.0.0: - resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} - engines: {node: '>=4'} - dev: false - optional: true - - /postgres-bytea/1.0.0: - resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} - engines: {node: '>=0.10.0'} - dev: false - optional: true - - /postgres-date/1.0.7: - resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} - engines: {node: '>=0.10.0'} - dev: false - optional: true - - /postgres-interval/1.2.0: - resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} - engines: {node: '>=0.10.0'} - dependencies: - xtend: 4.0.2 + /postgres/3.2.4: + resolution: {integrity: sha512-iscysD+ZlM4A9zj0RS2zo3f4Us4yuov94Yx+p3dE1rEARaBHC8R3/gRq40KEnWp1lxjuFq9EjuAenIUsPaTaDA==} + requiresBuild: true dev: false optional: true @@ -2349,12 +2254,6 @@ packages: resolution: {integrity: sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==} dev: true - /split2/4.1.0: - resolution: {integrity: sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==} - engines: {node: '>= 10.x'} - dev: false - optional: true - /splitargs/0.0.7: resolution: {integrity: sha512-UUFYD2oWbNwULH6WoVtLUOw8ch586B+HUqcsAjjjeoBQAM1bD4wZRXu01koaxyd8UeYpybWqW4h+lO1Okv40Tg==} dev: false @@ -2747,12 +2646,6 @@ packages: bufferutil: 4.0.6 dev: false - /xtend/4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} - dev: false - optional: true - /y18n/3.2.2: resolution: {integrity: sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==} dev: false diff --git a/utils/database/postgresql.js b/utils/database/postgresql.js index 1d108ed..4c8b696 100644 --- a/utils/database/postgresql.js +++ b/utils/database/postgresql.js @@ -1,11 +1,8 @@ import { prefixCache, disabledCmdCache, disabledCache, commands, messageCommands } from "../collections.js"; import * as logger from "../logger.js"; -import Postgres from "pg"; -const connection = new Postgres.Pool({ - connectionString: process.env.DB, - statement_timeout: 10000 -}); +import Postgres from "postgres"; +const sql = Postgres(process.env.DB); const psqlUpdates = [ "", // reserved @@ -16,30 +13,30 @@ const psqlUpdates = [ export async function setup() { let counts; try { - counts = await connection.query("SELECT * FROM counts"); + counts = await sql`SELECT * FROM counts`; } catch { - counts = { rows: [] }; + counts = []; } const merged = new Map([...commands, ...messageCommands]); - if (!counts.rows[0]) { + if (!counts.length) { for (const command of merged.keys()) { - await connection.query("INSERT INTO counts (command, count) VALUES ($1, $2)", [command, 0]); + await sql`INSERT INTO counts ${sql({ command, count: 0 }, "command", "count")}`; } } else { const exists = []; for (const command of merged.keys()) { - const count = await connection.query("SELECT * FROM counts WHERE command = $1", [command]); - if (!count.rows[0]) { - await connection.query("INSERT INTO counts (command, count) VALUES ($1, $2)", [command, 0]); + const count = await sql`SELECT * FROM counts WHERE command = ${command}`; + if (!count.length) { + await sql`INSERT INTO counts ${sql({ command, count: 0 }, "command", "count")}`; } exists.push(command); } - for (const { command } of counts.rows) { + for (const { command } of counts) { if (!exists.includes(command)) { - await connection.query("DELETE FROM counts WHERE command = $1", [command]); + await sql`DELETE FROM counts WHERE command = ${command}`; } } } @@ -48,24 +45,24 @@ export async function setup() { export async function upgrade(logger) { let version; try { - version = (await connection.query("SELECT version FROM settings WHERE id = 1")).rows[0].version; + version = (await sql`SELECT version FROM settings WHERE id = 1`)[0].version; } catch { version = 0; } if (version < (psqlUpdates.length - 1)) { logger.warn(`Migrating PostgreSQL database, which is currently at version ${version}...`); - await connection.query("BEGIN"); try { - while (version < (psqlUpdates.length - 1)) { - version++; - logger.warn(`Running version ${version} update script (${psqlUpdates[version]})...`); - await connection.query(psqlUpdates[version]); - } - await connection.query("COMMIT"); - await connection.query("INSERT INTO settings (id, version) VALUES (1, $1) ON CONFLICT (id) DO UPDATE SET version = $1", [psqlUpdates.length - 1]); + await sql.begin(async (db) => { + while (version < (psqlUpdates.length - 1)) { + version++; + logger.warn(`Running version ${version} update script (${psqlUpdates[version]})...`); + await db`${psqlUpdates[version]}`; + } + }); + const ver = psqlUpdates.length - 1; + await sql`INSERT INTO settings ${sql({ id: 1, version: ver })} ON CONFLICT (id) DO UPDATE SET version = ${ver}`; } catch (e) { logger.error(`PostgreSQL migration failed: ${e}`); - await connection.query("ROLLBACK"); logger.error("Unable to start the bot, quitting now."); return 1; } @@ -73,21 +70,21 @@ export async function upgrade(logger) { } export async function getGuild(query) { - return (await connection.query("SELECT * FROM guilds WHERE guild_id = $1", [query])).rows[0]; + return (await sql`SELECT * FROM guilds WHERE guild_id = ${query}`)[0]; } export async function setPrefix(prefix, guild) { - await connection.query("UPDATE guilds SET prefix = $1 WHERE guild_id = $2", [prefix, guild.id]); + await sql`UPDATE guilds SET prefix = ${prefix} WHERE guild_id = ${guild.id}`; prefixCache.set(guild.id, prefix); } export async function getTag(guild, tag) { - const tagResult = (await connection.query("SELECT * FROM tags WHERE guild_id = $1 AND name = $2", [guild, tag])).rows; + const tagResult = await sql`SELECT * FROM tags WHERE guild_id = ${guild} AND name = ${tag}`; return tagResult[0] ? { content: tagResult[0].content, author: tagResult[0].author } : undefined; } export async function getTags(guild) { - const tagArray = (await connection.query("SELECT * FROM tags WHERE guild_id = $1", [guild])).rows; + const tagArray = await sql`SELECT * FROM tags WHERE guild_id = ${guild}`; const tags = {}; for (const tag of tagArray) { tags[tag.name] = { content: tag.content, author: tag.author }; @@ -96,67 +93,61 @@ export async function getTags(guild) { } export async function setTag(name, content, guild) { - await connection.query("INSERT INTO tags (guild_id, name, content, author) VALUES ($1, $2, $3, $4)", [guild.id, name, content.content, content.author]); + await sql`INSERT INTO tags ${sql({ guild_id: guild.id, name, content: content.content, author: content.author }, "guild_id", "name", "content", "author")}`; } export async function editTag(name, content, guild) { - await connection.query("UPDATE tags SET content = $1, author = $2 WHERE guild_id = $3 AND name = $4", [content.content, content.author, guild.id, name]); + await sql`UPDATE tags SET content = ${content.content}, author = ${content.author} WHERE guild_id = ${guild.id} AND name = ${name}`; } export async function removeTag(name, guild) { - await connection.query("DELETE FROM tags WHERE guild_id = $1 AND name = $2", [guild.id, name]); + await sql`DELETE FROM tags WHERE guild_id = ${guild.id} AND name = ${name}`; } export async function disableCommand(guild, command) { const guildDB = await this.getGuild(guild); - await connection.query("UPDATE guilds SET disabled_commands = $1 WHERE guild_id = $2", [(guildDB.disabled_commands ? [...guildDB.disabled_commands, command] : [command]).filter((v) => !!v), guild]); + await sql`UPDATE guilds SET disabled_commands = ${(guildDB.disabled_commands ? [...guildDB.disabled_commands, command] : [command]).filter((v) => !!v)} WHERE guild_id = ${guild}`; disabledCmdCache.set(guild, guildDB.disabled_commands ? [...guildDB.disabled_commands, command] : [command].filter((v) => !!v)); } export async function enableCommand(guild, command) { const guildDB = await this.getGuild(guild); const newDisabled = guildDB.disabled_commands ? guildDB.disabled_commands.filter(item => item !== command) : []; - await connection.query("UPDATE guilds SET disabled_commands = $1 WHERE guild_id = $2", [newDisabled, guild]); + await sql`UPDATE guilds SET disabled_commands = ${newDisabled} WHERE guild_id = ${guild}`; disabledCmdCache.set(guild, newDisabled); } export async function disableChannel(channel) { const guildDB = await this.getGuild(channel.guild.id); - await connection.query("UPDATE guilds SET disabled = $1 WHERE guild_id = $2", [[...guildDB.disabled, channel.id], channel.guild.id]); + await sql`UPDATE guilds SET disabled_commands = ${[...guildDB.disabled, channel.id]} WHERE guild_id = ${channel.guild.id}`; disabledCache.set(channel.guild.id, [...guildDB.disabled, channel.id]); } export async function enableChannel(channel) { const guildDB = await this.getGuild(channel.guild.id); const newDisabled = guildDB.disabled.filter(item => item !== channel.id); - await connection.query("UPDATE guilds SET disabled = $1 WHERE guild_id = $2", [newDisabled, channel.guild.id]); + await sql`UPDATE guilds SET disabled_commands = ${newDisabled} WHERE guild_id = ${channel.guild.id}`; disabledCache.set(channel.guild.id, newDisabled); } export async function getCounts() { - const counts = await connection.query("SELECT * FROM counts"); - //const countArray = []; + const counts = await sql`SELECT * FROM counts`; const countObject = {}; - for (const { command, count } of counts.rows) { + for (const { command, count } of counts) { countObject[command] = count; } return countObject; } export async function addCount(command) { - let count = await connection.query("SELECT * FROM counts WHERE command = $1", [command]); - if (!count.rows[0]) { - await connection.query("INSERT INTO counts (command, count) VALUES ($1, $2)", [command, 0]); - count = await connection.query("SELECT * FROM counts WHERE command = $1", [command]); - } - await connection.query("UPDATE counts SET count = $1 WHERE command = $2", [count.rows[0].count ? count.rows[0].count + 1 : 1, command]); + await sql`INSERT INTO counts ${sql({ command, count: 1 }, "command", "count")} ON CONFLICT (command) DO UPDATE SET count = counts.count + 1 WHERE counts.command = ${command}`; } export async function addGuild(guild) { const query = await this.getGuild(guild); if (query) return query; try { - await connection.query("INSERT INTO guilds (guild_id, prefix, disabled, disabled_commands) VALUES ($1, $2, $3, $4)", [guild.id, process.env.PREFIX, [], []]); + await sql`INSERT INTO guilds ${sql({ guild_id: guild.id, prefix: process.env.PREFIX, disabled: [], disabled_commands: [] })}`; } catch (e) { logger.error(`Failed to register guild ${guild.id}: ${e}`); } @@ -164,13 +155,13 @@ export async function addGuild(guild) { } export async function fixGuild(guild) { - const guildDB = await connection.query("SELECT exists(SELECT 1 FROM guilds WHERE guild_id = $1)", [guild.id]); - if (!guildDB.rows[0].exists) { + const guildDB = await sql`SELECT exists(SELECT 1 FROM guilds WHERE guild_id = ${guild.id})`; + if (!guildDB[0].exists) { logger.log(`Registering guild database entry for guild ${guild.id}...`); return await this.addGuild(guild); } } export async function stop() { - await connection.end(); + await sql.end(); }