From 1bc2e737e359d6efd09c6b5950c4e6e297425b81 Mon Sep 17 00:00:00 2001 From: Isaac Date: Wed, 7 Apr 2021 00:02:07 +0100 Subject: [PATCH] Database encryption Security :tm: --- example.env | 1 + package.json | 2 +- pnpm-lock.yaml | 39 +-- src/commands/new.js | 20 +- src/database/index.js | 283 +------------------- src/database/models/category.model.js | 52 ++++ src/database/models/channel_entity.model.js | 23 ++ src/database/models/guild.model.js | 41 +++ src/database/models/message.model.js | 37 +++ src/database/models/role_entity.model.js | 27 ++ src/database/models/ticket.model.js | 51 ++++ src/database/models/user_entity.model.js | 28 ++ src/index.js | 80 +++--- src/modules/tickets/archives.js | 24 +- src/modules/tickets/manager.js | 6 +- src/utils/index.js | 1 + 16 files changed, 361 insertions(+), 354 deletions(-) create mode 100644 src/database/models/category.model.js create mode 100644 src/database/models/channel_entity.model.js create mode 100644 src/database/models/guild.model.js create mode 100644 src/database/models/message.model.js create mode 100644 src/database/models/role_entity.model.js create mode 100644 src/database/models/ticket.model.js create mode 100644 src/database/models/user_entity.model.js diff --git a/example.env b/example.env index 3b707bd..db544bb 100644 --- a/example.env +++ b/example.env @@ -1,4 +1,5 @@ DISCORD_TOKEN= +DB_ENCRYPTION_KEY= DB_TYPE=sqlite DB_HOST= DB_PORT= diff --git a/package.json b/package.json index 76f697a..21879c8 100644 --- a/package.json +++ b/package.json @@ -30,9 +30,9 @@ "dependencies": { "@eartharoid/i18n": "^0.0.1", "boxen": "^5.0.0", + "cryptr": "^6.0.2", "discord.js": "^12.5.1", "dotenv": "^8.2.0", - "fs-extra": "^9.1.0", "leeks.js": "^0.0.9", "leekslazylogger-fastify": "^0.1.0", "node-fetch": "^2.6.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3d26e66..f289fd2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,9 +1,9 @@ dependencies: '@eartharoid/i18n': 0.0.1 boxen: 5.0.0 + cryptr: 6.0.2 discord.js: 12.5.1 dotenv: 8.2.0 - fs-extra: 9.1.0 leeks.js: 0.0.9 leekslazylogger-fastify: 0.1.0 node-fetch: 2.6.1 @@ -355,12 +355,6 @@ packages: /asynckit/0.4.0: resolution: integrity: sha1-x57Zf380y48robyXkLzDZkdLS3k= - /at-least-node/1.0.0: - dev: false - engines: - node: '>= 4.0.0' - resolution: - integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== /aws-sign2/0.7.0: resolution: integrity: sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= @@ -641,6 +635,10 @@ packages: node: '>=8' resolution: integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== + /cryptr/6.0.2: + dev: false + resolution: + integrity: sha512-1TRHI4bmuLIB8WgkH9eeYXzhEg1T4tonO4vVaMBKKde8Dre51J68nAgTVXTwMYXAf7+mV2gBCkm/9wksjSb2sA== /dashdash/1.14.1: dependencies: assert-plus: 1.0.0 @@ -1045,17 +1043,6 @@ packages: node: '>= 6' resolution: integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== - /fs-extra/9.1.0: - dependencies: - at-least-node: 1.0.0 - graceful-fs: 4.2.6 - jsonfile: 6.1.0 - universalify: 2.0.0 - dev: false - engines: - node: '>=10' - resolution: - integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== /fs-minipass/1.2.7: dependencies: minipass: 2.9.0 @@ -1467,14 +1454,6 @@ packages: /json-stringify-safe/5.0.1: resolution: integrity: sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - /jsonfile/6.1.0: - dependencies: - universalify: 2.0.0 - dev: false - optionalDependencies: - graceful-fs: 4.2.6 - resolution: - integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== /jsprim/1.4.1: dependencies: assert-plus: 1.0.0 @@ -2766,12 +2745,6 @@ packages: node: '>=8' resolution: integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== - /universalify/2.0.0: - dev: false - engines: - node: '>= 10.0.0' - resolution: - integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== /update-notifier/4.1.3: dependencies: boxen: 4.2.0 @@ -2969,10 +2942,10 @@ packages: specifiers: '@eartharoid/i18n': ^0.0.1 boxen: ^5.0.0 + cryptr: ^6.0.2 discord.js: ^12.5.1 dotenv: ^8.2.0 eslint: ^7.21.0 - fs-extra: ^9.1.0 leeks.js: ^0.0.9 leekslazylogger-fastify: ^0.1.0 mariadb: ^2.5.2 diff --git a/src/commands/new.js b/src/commands/new.js index 133f107..72f3e46 100644 --- a/src/commands/new.js +++ b/src/commands/new.js @@ -2,6 +2,7 @@ const { MessageEmbed } = require('discord.js'); const Command = require('../modules/commands/command'); const { footer } = require('../utils/discord'); const { letters } = require('../utils/emoji'); +const { wait } = require('../utils'); module.exports = class NewCommand extends Command { constructor(client) { @@ -87,8 +88,12 @@ module.exports = class NewCommand extends Command { } setTimeout(async () => { - await response.delete(); - await message.delete(); + await response + .delete() + .catch(() => this.client.log.warn('Failed to delete response message')); + await message + .delete() + .catch(() => this.client.log.warn('Failed to delete original message')); }, 15000); }; @@ -122,7 +127,8 @@ module.exports = class NewCommand extends Command { ); for (let i in categories.rows) { - await collector_message.react(letters_array[i]); // add the correct number of letter reactions + collector_message.react(letters_array[i]); // add the correct number of letter reactions + await wait(1000); // 1 reaction per second rate-limit } const collector_filter = (reaction, user) => { @@ -153,8 +159,12 @@ module.exports = class NewCommand extends Command { .setFooter(footer(settings.footer, i18n('message_will_be_deleted_in', 15)), message.guild.iconURL()) ); setTimeout(async () => { - await collector_message.delete(); - await message.delete(); + await collector_message + .delete() + .catch(() => this.client.log.warn('Failed to delete response (collector) message')); + await message + .delete() + .catch(() => this.client.log.warn('Failed to delete original message')); }, 15000); } }); diff --git a/src/database/index.js b/src/database/index.js index 9a637bf..30110f1 100644 --- a/src/database/index.js +++ b/src/database/index.js @@ -1,9 +1,6 @@ -const { - Sequelize, - DataTypes -} = require('sequelize'); +const { Sequelize } = require('sequelize'); +const fs = require('fs'); const { path } = require('../utils/fs'); -const config = require('../../user/config'); const types = require('./dialects'); module.exports = async (client) => { @@ -14,8 +11,7 @@ module.exports = async (client) => { DB_PORT, DB_USER, DB_PASS, - DB_NAME, - DB_TABLE_PREFIX + DB_NAME } = process.env; let type = (DB_TYPE || 'sqlite').toLowerCase(); @@ -62,273 +58,18 @@ module.exports = async (client) => { return process.exit(); } - const Guild = sequelize.define('Guild', { - id: { - type: DataTypes.CHAR(18), - primaryKey: true, - allowNull: false, - }, - colour: { - type: DataTypes.STRING, - defaultValue: config.defaults.colour - }, - command_prefix: { - type: DataTypes.STRING, - defaultValue: config.defaults.command_prefix - }, - error_colour: { - type: DataTypes.STRING, - defaultValue: 'RED' - }, - footer: { - type: DataTypes.STRING, - defaultValue: 'Discord Tickets by eartharoid' - }, - locale: { - type: DataTypes.STRING, - defaultValue: config.locale - }, - log_messages: { - type: DataTypes.BOOLEAN, - defaultValue: config.defaults.log_messages - }, - success_colour: { - type: DataTypes.STRING, - defaultValue: 'GREEN' - }, - }, { - tableName: DB_TABLE_PREFIX + 'guilds' - }); + const models = fs.readdirSync(path('./src/database/models')) + .filter(filename => filename.endsWith('.model.js')); - const Category = sequelize.define('Category', { - id: { - type: DataTypes.CHAR(18), - primaryKey: true, - allowNull: false, - }, - guild: { - type: DataTypes.CHAR(18), - allowNull: false, - references: { - model: Guild, - key: 'id' - }, - unique: 'name-guild' - }, - max_per_member: { - type: DataTypes.INTEGER, - defaultValue: 1 - }, - name: { - type: DataTypes.STRING, - allowNull: false, - unique: 'name-guild' - }, - name_format: { - type: DataTypes.STRING, - allowNull: false, - defaultValue: config.defaults.name_format - }, - opening_message: { - type: DataTypes.STRING, - defaultValue: config.defaults.opening_message, - }, - require_topic: { - type: DataTypes.BOOLEAN, - defaultValue: true, - }, - roles: { - type: DataTypes.JSON, - allowNull: false, - }, - questions: { - type: DataTypes.JSON, - allowNull: true, - }, - }, { - tableName: DB_TABLE_PREFIX + 'categories' - }); + for (const model of models) { + require(`./models/${model}`)(client, sequelize); + } - const Ticket = sequelize.define('Ticket', { - id: { - type: DataTypes.CHAR(18), - primaryKey: true, - allowNull: false, - }, - category: { - type: DataTypes.CHAR(18), - allowNull: false, - references: { - model: Category, - key: 'id' - }, - }, - closed_by: { - type: DataTypes.CHAR(18), - allowNull: true, - }, - creator: { - type: DataTypes.CHAR(18), - allowNull: false, - }, - guild: { - type: DataTypes.CHAR(18), - allowNull: false, - references: { - model: Guild, - key: 'id' - }, - unique: 'number-guild' - }, - number: { - type: DataTypes.INTEGER, - allowNull: false, - unique: 'number-guild' - }, - open: { - type: DataTypes.BOOLEAN, - defaultValue: true - }, - topic: { - type: DataTypes.STRING, - allowNull: true, - }, - }, { - tableName: DB_TABLE_PREFIX + 'tickets' + sequelize.sync({ + alter: { + drop: false + } }); - // eslint-disable-next-line no-unused-vars - const Message = sequelize.define('Message', { - id: { - type: DataTypes.CHAR(18), - primaryKey: true, - allowNull: false, - }, - author: { - type: DataTypes.CHAR(18), - allowNull: false, - }, - data: { - type: DataTypes.JSON, - allowNull: false, - }, - deleted: { - type: DataTypes.BOOLEAN, - defaultValue: false, - }, - edited: { - type: DataTypes.BOOLEAN, - defaultValue: false, - }, - ticket: { - type: DataTypes.CHAR(18), - allowNull: false, - references: { - model: Ticket, - key: 'id' - }, - }, - }, { - tableName: DB_TABLE_PREFIX + 'messages' - }); - - // eslint-disable-next-line no-unused-vars - const UserEntity = sequelize.define('UserEntity', { - avatar: { - type: DataTypes.STRING, - allowNull: false, - }, - bot: { - type: DataTypes.BOOLEAN, - defaultValue: false, - }, - colour: { - type: DataTypes.CHAR(6), - allowNull: true, - }, - discriminator: { - type: DataTypes.STRING, - allowNull: false, - }, - display_name: { - type: DataTypes.STRING, - allowNull: false, - }, - ticket: { - type: DataTypes.CHAR(18), - allowNull: false, - unique: 'id-ticket', - references: { - model: Ticket, - key: 'id' - }, - }, - user: { - type: DataTypes.CHAR(18), - allowNull: false, - unique: 'id-ticket' - }, - username: { - type: DataTypes.STRING, - allowNull: false, - }, - }, { - tableName: DB_TABLE_PREFIX + 'user_entities' - }); - - // eslint-disable-next-line no-unused-vars - const ChannelEntity = sequelize.define('ChannelEntity', { - channel: { - type: DataTypes.CHAR(18), - allowNull: false, - unique: 'id-ticket' - }, - name: { - type: DataTypes.STRING, - allowNull: false, - }, - ticket: { - type: DataTypes.CHAR(18), - allowNull: false, - unique: 'id-ticket', - references: { - model: Ticket, - key: 'id' - }, - }, - }, { - tableName: DB_TABLE_PREFIX + 'channel_entities' - }); - - // eslint-disable-next-line no-unused-vars - const RoleEntity = sequelize.define('RoleEntity', { - colour: { - type: DataTypes.CHAR(6), - defaultValue: '7289DA', - }, - name: { - type: DataTypes.STRING, - allowNull: false, - }, - role: { - type: DataTypes.CHAR(18), - allowNull: false, - unique: 'id-ticket' - }, - ticket: { - type: DataTypes.CHAR(18), - allowNull: false, - unique: 'id-ticket', - references: { - model: Ticket, - key: 'id' - }, - }, - }, { - tableName: DB_TABLE_PREFIX + 'role_entities' - }); - - sequelize.sync(); - return sequelize; }; \ No newline at end of file diff --git a/src/database/models/category.model.js b/src/database/models/category.model.js new file mode 100644 index 0000000..324b560 --- /dev/null +++ b/src/database/models/category.model.js @@ -0,0 +1,52 @@ +const { DataTypes } = require('sequelize'); +module.exports = ({ config }, sequelize) => { + const { DB_TABLE_PREFIX } = process.env; + sequelize.define('Category', { + id: { + type: DataTypes.CHAR(18), + primaryKey: true, + allowNull: false, + }, + guild: { + type: DataTypes.CHAR(18), + allowNull: false, + references: { + model: DB_TABLE_PREFIX + 'guilds', + key: 'id' + }, + unique: 'name-guild' + }, + max_per_member: { + type: DataTypes.INTEGER, + defaultValue: 1 + }, + name: { + type: DataTypes.STRING, + allowNull: false, + unique: 'name-guild' + }, + name_format: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: config.defaults.name_format + }, + opening_message: { + type: DataTypes.STRING, + defaultValue: config.defaults.opening_message, + }, + require_topic: { + type: DataTypes.BOOLEAN, + defaultValue: true, + }, + roles: { + type: DataTypes.JSON, + allowNull: false, + }, + questions: { + type: DataTypes.JSON, + allowNull: true, + }, + }, { + tableName: DB_TABLE_PREFIX + 'categories' + }); +}; \ No newline at end of file diff --git a/src/database/models/channel_entity.model.js b/src/database/models/channel_entity.model.js new file mode 100644 index 0000000..f14707d --- /dev/null +++ b/src/database/models/channel_entity.model.js @@ -0,0 +1,23 @@ +const { DataTypes } = require('sequelize'); +module.exports = (client, sequelize) => { + const { DB_TABLE_PREFIX } = process.env; + sequelize.define('ChannelEntity', { + channel: { + type: DataTypes.CHAR(18), + allowNull: false, + unique: 'id-ticket' + }, + name: DataTypes.TEXT, + ticket: { + type: DataTypes.CHAR(18), + allowNull: false, + unique: 'id-ticket', + references: { + model: DB_TABLE_PREFIX + 'tickets', + key: 'id' + }, + }, + }, { + tableName: DB_TABLE_PREFIX + 'channel_entities' + }); +}; \ No newline at end of file diff --git a/src/database/models/guild.model.js b/src/database/models/guild.model.js new file mode 100644 index 0000000..b8473bc --- /dev/null +++ b/src/database/models/guild.model.js @@ -0,0 +1,41 @@ +const { DataTypes } = require('sequelize'); +module.exports = ({ config }, sequelize) => { + const { DB_TABLE_PREFIX } = process.env; + sequelize.define('Guild', { + id: { + type: DataTypes.CHAR(18), + primaryKey: true, + allowNull: false, + }, + colour: { + type: DataTypes.STRING, + defaultValue: config.defaults.colour + }, + command_prefix: { + type: DataTypes.STRING, + defaultValue: config.defaults.command_prefix + }, + error_colour: { + type: DataTypes.STRING, + defaultValue: 'RED' + }, + footer: { + type: DataTypes.STRING, + defaultValue: 'Discord Tickets by eartharoid' + }, + locale: { + type: DataTypes.STRING, + defaultValue: config.locale + }, + log_messages: { + type: DataTypes.BOOLEAN, + defaultValue: config.defaults.log_messages + }, + success_colour: { + type: DataTypes.STRING, + defaultValue: 'GREEN' + }, + }, { + tableName: DB_TABLE_PREFIX + 'guilds' + }); +}; \ No newline at end of file diff --git a/src/database/models/message.model.js b/src/database/models/message.model.js new file mode 100644 index 0000000..63c73be --- /dev/null +++ b/src/database/models/message.model.js @@ -0,0 +1,37 @@ +const { DataTypes } = require('sequelize'); +module.exports = (client, sequelize) => { + const { DB_TABLE_PREFIX } = process.env; + sequelize.define('Message', { + id: { + type: DataTypes.CHAR(18), + primaryKey: true, + allowNull: false, + }, + author: { + type: DataTypes.CHAR(18), + allowNull: false, + }, + data: { + type: DataTypes.TEXT, + allowNull: false, + }, + deleted: { + type: DataTypes.BOOLEAN, + defaultValue: false, + }, + edited: { + type: DataTypes.BOOLEAN, + defaultValue: false, + }, + ticket: { + type: DataTypes.CHAR(18), + allowNull: false, + references: { + model: DB_TABLE_PREFIX + 'tickets', + key: 'id' + }, + }, + }, { + tableName: DB_TABLE_PREFIX + 'messages' + }); +}; \ No newline at end of file diff --git a/src/database/models/role_entity.model.js b/src/database/models/role_entity.model.js new file mode 100644 index 0000000..621301e --- /dev/null +++ b/src/database/models/role_entity.model.js @@ -0,0 +1,27 @@ +const { DataTypes } = require('sequelize'); +module.exports = (client, sequelize) => { + const { DB_TABLE_PREFIX } = process.env; + sequelize.define('RoleEntity', { + colour: { + type: DataTypes.CHAR(6), + defaultValue: '7289DA', + }, + name: DataTypes.TEXT, + role: { + type: DataTypes.CHAR(18), + allowNull: false, + unique: 'id-ticket' + }, + ticket: { + type: DataTypes.CHAR(18), + allowNull: false, + unique: 'id-ticket', + references: { + model: DB_TABLE_PREFIX + 'tickets', + key: 'id' + }, + }, + }, { + tableName: DB_TABLE_PREFIX + 'role_entities' + }); +}; \ No newline at end of file diff --git a/src/database/models/ticket.model.js b/src/database/models/ticket.model.js new file mode 100644 index 0000000..1ffebcb --- /dev/null +++ b/src/database/models/ticket.model.js @@ -0,0 +1,51 @@ +const { DataTypes } = require('sequelize'); +module.exports = (client, sequelize) => { + const { DB_TABLE_PREFIX } = process.env; + sequelize.define('Ticket', { + id: { + type: DataTypes.CHAR(18), + primaryKey: true, + allowNull: false, + }, + category: { + type: DataTypes.CHAR(18), + allowNull: false, + references: { + model: DB_TABLE_PREFIX + 'categories', + key: 'id' + }, + }, + closed_by: { + type: DataTypes.CHAR(18), + allowNull: true, + }, + creator: { + type: DataTypes.CHAR(18), + allowNull: false, + }, + guild: { + type: DataTypes.CHAR(18), + allowNull: false, + references: { + model: DB_TABLE_PREFIX + 'guilds', + key: 'id' + }, + unique: 'number-guild' + }, + number: { + type: DataTypes.INTEGER, + allowNull: false, + unique: 'number-guild' + }, + open: { + type: DataTypes.BOOLEAN, + defaultValue: true + }, + topic: { + type: DataTypes.TEXT, + allowNull: true, + }, + }, { + tableName: DB_TABLE_PREFIX + 'tickets' + }); +}; \ No newline at end of file diff --git a/src/database/models/user_entity.model.js b/src/database/models/user_entity.model.js new file mode 100644 index 0000000..bb22134 --- /dev/null +++ b/src/database/models/user_entity.model.js @@ -0,0 +1,28 @@ +const { DataTypes } = require('sequelize'); +module.exports = (client, sequelize) => { + const { DB_TABLE_PREFIX } = process.env; + sequelize.define('UserEntity', { + avatar: DataTypes.STRING, + bot: DataTypes.BOOLEAN, + colour: DataTypes.CHAR(6), + discriminator: DataTypes.STRING, + display_name: DataTypes.TEXT, + ticket: { + type: DataTypes.CHAR(18), + allowNull: false, + unique: 'id-ticket', + references: { + model: DB_TABLE_PREFIX + 'tickets', + key: 'id' + }, + }, + user: { + type: DataTypes.CHAR(18), + allowNull: false, + unique: 'id-ticket' + }, + username: DataTypes.TEXT, + }, { + tableName: DB_TABLE_PREFIX + 'user_entities' + }); +}; \ No newline at end of file diff --git a/src/index.js b/src/index.js index 24146ec..af0ba12 100644 --- a/src/index.js +++ b/src/index.js @@ -29,45 +29,68 @@ const fs = require('fs'); const { path } = require('./utils/fs'); const checkFile = (file, example) => { - - file = path(file); - example = path(example); - - if (fs.existsSync(file)) return true; - if (!fs.existsSync(example)) { - console.log(`Error: '${file}' not found, and unable to create it due to '${example}' being missing`); + if (fs.existsSync(path(file))) return true; + if (!fs.existsSync(path(example))) { + console.log(`Error: "${file}" not found, and unable to create it due to "${example}" being missing.`); return process.exit(); } - - console.log(`Copying '${example}' to '${file}'`); - fs.copyFileSync(example, file); + console.log(`Copying "${example}" to "${file}"...`); + fs.copyFileSync(path(example), path(file)); return false; - }; +checkFile('./user/config.js', './user/example.config.js'); + if (!checkFile('./.env', './example.env')) { - console.log('Please set your bot\'s token in \'.env\''); + console.log('Generating database encryption key...'); + + const file = path('./.env'); + const crypto = require('crypto'); + + let key = 'DB_ENCRYPTION_KEY='; + let value = crypto + .randomBytes(24) + .toString('hex'); + + let data = fs.readFileSync(file, { + encoding: 'utf-8' + }); + data = data.replace(key, key + value); + + fs.writeFileSync(file, data); + + console.log('Saved.'); + console.log('Please set your bot\'s "DISCORD_TOKEN" in "./.env".'); + process.exit(); } -checkFile('./user/config.js', './user/example.config.js'); - require('dotenv').config({ path: path('./.env') }); -const config = require('../user/config'); - require('./banner')(); const log = require('./logger'); +const { version } = require('../package.json'); +process.on('unhandledRejection', error => { + log.notice('PLEASE INCLUDE THIS INFORMATION IF YOU ASK FOR HELP ABOUT THE FOLLOWING ERROR:'); + log.notice(`Discord Tickets v${version}, Node v${process.versions.node} on ${process.platform}`); + log.warn('An error was not caught'); + if (error instanceof Error) log.warn(`Uncaught ${error.name}`); + log.error(error); +}); + const { selectPresence } = require('./utils/discord'); +const Cryptr = require('cryptr'); const I18n = require('@eartharoid/i18n'); const CommandManager = require('./modules/commands/manager'); const PluginManager = require('./modules/plugins/manager'); const TicketManager = require('./modules/tickets/manager'); +const fetch = require('node-fetch'); + require('./modules/structures')(); // load extended structures before creating the client const { @@ -95,12 +118,15 @@ class Bot extends Client { (async () => { /** The global bot configuration */ - this.config = config; + this.config = require('../user/config'); - /** A leekslazylogger instance */ + /** A [leekslazylogger](https://logger.eartharoid.me) instance */ this.log = log; - /** An @eartharoid/i18n instance */ + /** A [Cryptr](https://www.npmjs.com/package/cryptr) instance */ + this.cryptr = new Cryptr(process.env.DB_ENCRYPTION_KEY); + + /** An [@eartharoid/i18n](https://github.com/eartharoid/i18n) instance */ this.i18n = new I18n(path('./src/locales'), 'en-GB'); /** A sequelize instance */ @@ -134,12 +160,11 @@ class Bot extends Client { * You can see the source here: https://github.com/discord-tickets/stats */ if (this.config.super_secret_setting) { // you can disable it if you really want - const fetch = require('node-fetch'); let tickets = await this.db.models.Ticket.count(); await fetch(`https://stats.discordtickets.app/client?id=${this.user.id}&tickets=${tickets}`, { method: 'post', }).catch(e => { - // fail quietly, it doesn't really matter if it didn't work + this.log.warn('Failed to post tickets count to stats server (you can disable sending stats by setting "super_secret_setting" to false)'); this.log.debug(e); }); this.guilds.cache.forEach(async g => { @@ -147,6 +172,7 @@ class Bot extends Client { await fetch(`https://stats.discordtickets.app/guild?id=${g.id}&members=${members}`, { method: 'post', }).catch(e => { + // don't spam a warning for each server this.log.debug(e); }); }); @@ -155,14 +181,4 @@ class Bot extends Client { } -new Bot(); - -const { version } = require('../package.json'); -process.on('unhandledRejection', error => { - log.notice('PLEASE INCLUDE THIS INFORMATION IF YOU ASK FOR HELP ABOUT THE FOLLOWING ERROR:'); - log.notice(`Discord Tickets v${version}, Node v${process.versions.node} on ${process.platform}`); - log.warn('An error was not caught'); - if (error instanceof Error) log.warn(`Uncaught ${error.name}`); - log.error(error); -}); - +new Bot(); \ No newline at end of file diff --git a/src/modules/tickets/archives.js b/src/modules/tickets/archives.js index 851cc36..ef0f70d 100644 --- a/src/modules/tickets/archives.js +++ b/src/modules/tickets/archives.js @@ -11,6 +11,9 @@ module.exports = class TicketArchives { /** The Discord Client */ this.client = client; + this.encrypt = this.client.cryptr.encrypt; + this.decrypt = this.client.cryptr.decrypt; + } async addMessage(message) { @@ -25,14 +28,14 @@ module.exports = class TicketArchives { id: message.id, ticket: t_row.id, author: message.author.id, - data: { + data: this.encrypt(JSON.stringify({ content: message.content, // time: message.createdTimestamp, embeds: message.embeds.map(embed => { return { embed }; }), attachments: [...message.attachments.values()] - } + })) }); this.updateEntities(message); @@ -47,14 +50,13 @@ module.exports = class TicketArchives { }); if (m_row) { - m_row.data = { + m_row.data = this.encrypt(JSON.stringify({ content: message.content, - // time: message.editedTimestamp, embeds: message.embeds.map(embed => { return { embed }; }), attachments: [...message.attachments.values()] - }; + })); if (message.editedTimestamp) { m_row.edited = true; @@ -99,9 +101,9 @@ module.exports = class TicketArchives { }); await u_row.update({ avatar: message.author.displayAvatarURL(), - username: message.author.username, + username: this.encrypt(message.author.username), discriminator: message.author.discriminator, - display_name: message.member.displayName, + display_name: this.encrypt(message.member.displayName), colour: message.member.displayColor === 0 ? null : int2hex(message.member.displayColor), bot: message.author.bot }); @@ -119,9 +121,9 @@ module.exports = class TicketArchives { await m_row.update({ avatar: member.user.displayAvatarURL(), - username: member.user.username, + username: this.encrypt(member.user.username), discriminator: member.user.discriminator, - display_name: member.displayName, + display_name: this.encrypt(member.displayName), colour: member.displayColor === 0 ? null : int2hex(member.displayColor), bot: member.user.bot }); @@ -138,7 +140,7 @@ module.exports = class TicketArchives { defaults: c_model_data }); await c_row.update({ - name: channel.name + name: this.encrypt(channel.name) }); }); @@ -153,7 +155,7 @@ module.exports = class TicketArchives { defaults: r_model_data }); await r_row.update({ - name: role.name, + name: this.encrypt(role.name), colour: role.color === 0 ? '7289DA' : int2hex(role.color) // 7289DA = 7506394 }); }); diff --git a/src/modules/tickets/manager.js b/src/modules/tickets/manager.js index fdba983..674ec95 100644 --- a/src/modules/tickets/manager.js +++ b/src/modules/tickets/manager.js @@ -71,9 +71,11 @@ module.exports = class TicketManager extends EventEmitter { guild: guild_id, category: category_id, creator: creator_id, - topic + topic: this.client.cryptr.encrypt(topic) }); + this.client.log.info(`${member.user.tag} created a new ticket in "${guild.name}"`); + this.emit('create', t_row.id, creator_id); return t_row; } @@ -144,6 +146,8 @@ module.exports = class TicketManager extends EventEmitter { }, 5000); } + this.client.log.info(`${member.user.tag} closed a ticket (${ticket_id})`); + this.emit('close', ticket_id); return t_row; } diff --git a/src/utils/index.js b/src/utils/index.js index d75562b..d544ab4 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1,3 +1,4 @@ module.exports = { int2hex: (int) => int.toString(16).toUpperCase(), + wait: (time) => new Promise(res => setTimeout(res, time)), }; \ No newline at end of file