From db9f6397664e60f4f99e27d21e85ce885ed590de Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Mon, 28 Feb 2022 18:38:13 +0000 Subject: [PATCH 01/12] Add database and default values --- .gitignore | 3 +- docker-compose.yml | 22 ++- ormconfig.json.template | 24 ++++ package.json | 5 +- src/constants/DefaultValues.ts | 56 ++++++++ src/contracts/BaseEntity.ts | 68 ++++++++++ src/entity/Setting.ts | 48 +++++++ tsconfig.json | 2 +- yarn.lock | 236 ++++++++++++++++++++++++++++++++- 9 files changed, 453 insertions(+), 11 deletions(-) create mode 100644 ormconfig.json.template create mode 100644 src/constants/DefaultValues.ts create mode 100644 src/contracts/BaseEntity.ts create mode 100644 src/entity/Setting.ts diff --git a/.gitignore b/.gitignore index 707ff92..1707d85 100644 --- a/.gitignore +++ b/.gitignore @@ -104,4 +104,5 @@ dist .tern-port config.json -.DS_Store \ No newline at end of file +.DS_Store +ormconfig.json \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index da68dfd..62c761e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,24 @@ version: "3.9" services: discord: - build: . \ No newline at end of file + build: . + + database: + image: mysql/mysql-server + command: --default-authentication-plugin=mysql_native_password + restart: always + environment: + - MYSQL_DATABASE=vylbot + - MYSQL_USER=dev + - MYSQL_PASSWORD=dev + - MYSQL_ROOT_PASSWORD=root + ports: + - 3306:3306 + + phpmyadmin: + image: phpmyadmin + restart: always + ports: + - 8080:80 + environment: + - PMA_ARBITRARY=1 \ No newline at end of file diff --git a/ormconfig.json.template b/ormconfig.json.template new file mode 100644 index 0000000..5e24f5c --- /dev/null +++ b/ormconfig.json.template @@ -0,0 +1,24 @@ +{ + "type": "mysql", + "host": "localhost", + "port": 3306, + "username": "dev", + "password": "dev", + "database": "droplet", + "synchronize": true, + "logging": false, + "entities": [ + "dist/entity/**/*.js" + ], + "migrations": [ + "dist/migration/**/*.js" + ], + "subscribers": [ + "dist/subscriber/**/*.js" + ], + "cli": { + "entitiesDir": "dist/entity", + "migrationsDir": "dist/migration", + "subscribersDir": "dist/subscriber" + } +} \ No newline at end of file diff --git a/package.json b/package.json index 27c731c..c9f1ecd 100644 --- a/package.json +++ b/package.json @@ -19,13 +19,16 @@ "homepage": "https://github.com/Vylpes/vylbot-app", "dependencies": { "@types/jest": "^27.0.3", + "@types/uuid": "^8.3.4", "discord.js": "12.5.3", "dotenv": "^10.0.0", "emoji-regex": "^9.2.0", "jest": "^27.4.5", "jest-mock-extended": "^2.0.4", "random-bunny": "^2.0.0", - "ts-jest": "^27.1.2" + "ts-jest": "^27.1.2", + "typeorm": "^0.2.44", + "uuid": "^8.3.2" }, "devDependencies": { "@types/node": "^16.11.10", diff --git a/src/constants/DefaultValues.ts b/src/constants/DefaultValues.ts new file mode 100644 index 0000000..0298e44 --- /dev/null +++ b/src/constants/DefaultValues.ts @@ -0,0 +1,56 @@ +export default class DefaultValues { + public static readonly values: ISettingValue[]; + + public static GetValue(key: string): string | undefined { + this.SetValues(); + + const res = this.values.find(x => x.Key == key); + + if (!res) { + return undefined; + } + + return res.Value; + } + + private static SetValues() { + if (this.values.length == 0) { + // Bot + this.values.push({ Key: "bot.prefix", Value: "v!" }); + this.values.push({ Key: "bot.version", Value: "3.0" }); + this.values.push({ Key: "bot.author", Value: "Vylpes" }); + this.values.push({ Key: "bot.date", Value: "28 Feb 2022" }); + this.values.push({ Key: "bot.owner", Value: "147392775707426816" }); + + // Folders + this.values.push({ Key: "folders.commands", Value: "src/commands" }); + this.values.push({ Key: "folders.events", Value: "src/events" }); + + // Commands + this.values.push({ Key: "commands.disabled", Value: "" }); + this.values.push({ Key: "commands.disabled.message", Value: "This command is disabled." }); + + // Role (Command) + this.values.push({ Key: "role.assignable", Value: "" }); + this.values.push({ Key: "role.moderator", Value: "Moderator" }); + this.values.push({ Key: "role.muted", Value: "Muted" }); + + // Rules (Command) + this.values.push({ Key: "rules.file", Value: "data/rules/rules" }); + + // Embed + this.values.push({ Key: "embed.colour.info", Value: "0x3050ba" }); + this.values.push({ Key: "embed.colour.error", Value: "0xd52803" }); + + // Channels + this.values.push({ Key: "channels.logs.message", Value: "message-logs" }); + this.values.push({ Key: "channels.logs.member", Value: "member-logs" }); + this.values.push({ Key: "channels.logs.mod", Value: "mod-logs" }); + } + } +} + +export interface ISettingValue { + Key: string, + Value: string, +}; \ No newline at end of file diff --git a/src/contracts/BaseEntity.ts b/src/contracts/BaseEntity.ts new file mode 100644 index 0000000..18c2b76 --- /dev/null +++ b/src/contracts/BaseEntity.ts @@ -0,0 +1,68 @@ +import { Column, DeepPartial, EntityTarget, getConnection, PrimaryColumn } from "typeorm"; +import { v4 } from "uuid"; + +export default class BaseEntity { + constructor() { + this.Id = v4(); + + this.WhenCreated = new Date(); + this.WhenUpdated = new Date(); + } + + @PrimaryColumn() + Id: string; + + @Column() + WhenCreated: Date; + + @Column() + WhenUpdated: Date; + + public async Save(target: EntityTarget, entity: DeepPartial): Promise { + this.WhenUpdated = new Date(); + + const connection = getConnection(); + + const repository = connection.getRepository(target); + + await repository.save(entity); + } + + public static async Remove(target: EntityTarget, entity: T): Promise { + const connection = getConnection(); + + const repository = connection.getRepository(target); + + await repository.remove(entity); + } + + public static async FetchAll(target: EntityTarget, relations?: string[]): Promise { + const connection = getConnection(); + + const repository = connection.getRepository(target); + + const all = await repository.find({ relations: relations || [] }); + + return all; + } + + public static async FetchOneById(target: EntityTarget, id: string, relations?: string[]): Promise { + const connection = getConnection(); + + const repository = connection.getRepository(target); + + const single = await repository.findOne(id, { relations: relations || [] }); + + return single; + } + + public static async Any(target: EntityTarget): Promise { + const connection = getConnection(); + + const repository = connection.getRepository(target); + + const any = await repository.find(); + + return any.length > 0; + } +} \ No newline at end of file diff --git a/src/entity/Setting.ts b/src/entity/Setting.ts new file mode 100644 index 0000000..7ddb436 --- /dev/null +++ b/src/entity/Setting.ts @@ -0,0 +1,48 @@ +import { Column, Entity, getConnection } from "typeorm"; +import DefaultValues from "../constants/DefaultValues"; +import BaseEntity from "../contracts/BaseEntity"; + +@Entity() +export default class Setting extends BaseEntity { + constructor(key: string, value: string) { + super(); + + this.Key = key; + this.Value = value; + } + + @Column() + Key: string; + + @Column() + Value: string; + + public UpdateBasicDetails(key: string, value: string) { + this.Key = key; + this.Value = value; + } + + public static async FetchOneByKey(key: string, relations?: string[]): Promise { + const connection = getConnection(); + + const repository = connection.getRepository(Setting); + + const single = await repository.findOne({ Key: key }, { relations: relations || [] }); + + return single; + } + + public static async FetchValueByKeyOrDefault(key: string): Promise { + const connection = getConnection(); + + const repository = connection.getRepository(Setting); + + const single = await repository.findOne({ Key: key }); + + if (!single) { + return DefaultValues.GetValue(key); + } + + return single.Value; + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 3f030e6..4e61c2e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -62,7 +62,7 @@ // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ /* Advanced Options */ diff --git a/yarn.lock b/yarn.lock index b9359b7..92ce5f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -504,6 +504,11 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@sqltools/formatter@^1.2.2": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@sqltools/formatter/-/formatter-1.2.3.tgz#1185726610acc37317ddab11c3c7f9066966bd20" + integrity sha512-O3uyB/JbkAEMZaP3YqyHH7TMnex7tWyCbCI4EfJdOCoN6HIhqdJBWTM6aCCiWQ/5f5wxjgU735QAIpJbjDvmzg== + "@szmarczak/http-timer@^4.0.5": version "4.0.6" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" @@ -632,6 +637,11 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== +"@types/uuid@^8.3.4": + version "8.3.4" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" + integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== + "@types/yargs-parser@*": version "20.2.1" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" @@ -644,6 +654,11 @@ dependencies: "@types/yargs-parser" "*" +"@types/zen-observable@0.8.3": + version "0.8.3" + resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.3.tgz#781d360c282436494b32fe7d9f7f8e64b3118aa3" + integrity sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw== + abab@^2.0.3, abab@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" @@ -717,6 +732,11 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + anymatch@^3.0.3: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" @@ -725,6 +745,11 @@ anymatch@^3.0.3: normalize-path "^3.0.0" picomatch "^2.0.4" +app-root-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.0.0.tgz#210b6f43873227e18a4b810a032283311555d5ad" + integrity sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -732,6 +757,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -803,6 +833,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -853,6 +888,14 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + cacheable-lookup@^5.0.3: version "5.0.4" resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" @@ -900,7 +943,7 @@ chalk@^2.0.0: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0: +chalk@^4.0.0, chalk@^4.1.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -923,6 +966,18 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== +cli-highlight@^2.1.11: + version "2.1.11" + resolved "https://registry.yarnpkg.com/cli-highlight/-/cli-highlight-2.1.11.tgz#49736fa452f0aaf4fae580e30acb26828d2dc1bf" + integrity sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg== + dependencies: + chalk "^4.0.0" + highlight.js "^10.7.1" + mz "^2.4.0" + parse5 "^5.1.1" + parse5-htmlparser2-tree-adapter "^6.0.0" + yargs "^16.0.0" + cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -1027,7 +1082,7 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" -debug@4, debug@^4.1.0: +debug@4, debug@^4.1.0, debug@^4.3.1: version "4.3.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== @@ -1114,6 +1169,11 @@ dotenv@^10.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== +dotenv@^8.2.0: + version "8.6.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" + integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== + electron-to-chromium@^1.4.17: version "1.4.25" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.25.tgz#ce95e6678f8c6893ae892c7e95a5000e83f1957f" @@ -1310,7 +1370,7 @@ glob-parent@^6.0.0: dependencies: is-glob "^4.0.3" -glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: +glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== @@ -1366,6 +1426,11 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +highlight.js@^10.7.1: + version "10.7.3" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" + integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== + html-encoding-sniffer@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" @@ -1420,6 +1485,11 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + import-local@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.3.tgz#4d51c2c495ca9393da259ec66b62e022920211e0" @@ -1441,7 +1511,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2: +inherits@2, inherits@^2.0.1: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -1983,6 +2053,13 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsdom@^16.6.0: version "16.7.0" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" @@ -2158,11 +2235,25 @@ minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +mz@^2.4.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -2207,6 +2298,11 @@ nwsapi@^2.2.0: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== +object-assign@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -2257,11 +2353,23 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -parse5@6.0.1: +parse5-htmlparser2-tree-adapter@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" + integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== + dependencies: + parse5 "^6.0.1" + +parse5@6.0.1, parse5@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== +parse5@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" + integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -2368,6 +2476,11 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +reflect-metadata@^0.1.13: + version "0.1.13" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" + integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -2417,6 +2530,11 @@ rimraf@^3.0.0: dependencies: glob "^7.1.3" +safe-buffer@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -2427,6 +2545,11 @@ safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sax@>=0.6.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + saxes@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" @@ -2451,6 +2574,14 @@ setimmediate@^1.0.5: resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= +sha.js@^2.4.11: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -2521,7 +2652,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -string-width@^4.1.0, string-width@^4.2.0: +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -2598,6 +2729,20 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY= + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + throat@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" @@ -2660,6 +2805,11 @@ ts-jest@^27.1.2: semver "7.x" yargs-parser "20.x" +tslib@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + tweetnacl@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" @@ -2689,6 +2839,29 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" +typeorm@^0.2.44: + version "0.2.44" + resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.44.tgz#4cc07eb1eb7a0e7f3ec9e65ded9eb3c3aedbb3e1" + integrity sha512-yFyb9Ts73vGaS/O06TvLpzvT5U/ngO31GeciNc0eoH7P1QcG8kVZdOy9FHJqkTeDmIljMRgWjbYUoMw53ZY7Xw== + dependencies: + "@sqltools/formatter" "^1.2.2" + app-root-path "^3.0.0" + buffer "^6.0.3" + chalk "^4.1.0" + cli-highlight "^2.1.11" + debug "^4.3.1" + dotenv "^8.2.0" + glob "^7.1.6" + js-yaml "^4.0.0" + mkdirp "^1.0.4" + reflect-metadata "^0.1.13" + sha.js "^2.4.11" + tslib "^2.1.0" + uuid "^8.3.2" + xml2js "^0.4.23" + yargs "^17.0.1" + zen-observable-ts "^1.0.0" + typescript@^4.5.2: version "4.5.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.2.tgz#8ac1fba9f52256fdb06fb89e4122fa6a346c2998" @@ -2699,6 +2872,11 @@ universalify@^0.1.2: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + v8-to-istanbul@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.0.tgz#0aeb763894f1a0a1676adf8a8b7612a38902446c" @@ -2819,6 +2997,19 @@ xml-name-validator@^3.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== +xml2js@^0.4.23: + version "0.4.23" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" + integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + +xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" @@ -2839,7 +3030,12 @@ yargs-parser@20.x, yargs-parser@^20.2.2: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs@^16.2.0: +yargs-parser@^21.0.0: + version "21.0.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35" + integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg== + +yargs@^16.0.0, yargs@^16.2.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== @@ -2851,3 +3047,29 @@ yargs@^16.2.0: string-width "^4.2.0" y18n "^5.0.5" yargs-parser "^20.2.2" + +yargs@^17.0.1: + version "17.3.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.3.1.tgz#da56b28f32e2fd45aefb402ed9c26f42be4c07b9" + integrity sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.0.0" + +zen-observable-ts@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.1.0.tgz#2d1aa9d79b87058e9b75698b92791c1838551f83" + integrity sha512-1h4zlLSqI2cRLPJUHJFL8bCWHhkpuXkF+dbGkRaWjgDIG26DmzyshUMrdV/rL3UnR+mhaX4fRq8LPouq0MYYIA== + dependencies: + "@types/zen-observable" "0.8.3" + zen-observable "0.8.15" + +zen-observable@0.8.15: + version "0.8.15" + resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" + integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== -- 2.43.4 From 8b2da347b156ed4513c1f6fbd8bfda5829329524 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Thu, 3 Mar 2022 18:59:45 +0000 Subject: [PATCH 02/12] Add ability to save a setting to the database --- src/entity/Setting.ts | 14 ------------- src/helpers/SettingsHelper.ts | 37 +++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 14 deletions(-) create mode 100644 src/helpers/SettingsHelper.ts diff --git a/src/entity/Setting.ts b/src/entity/Setting.ts index 7ddb436..2743567 100644 --- a/src/entity/Setting.ts +++ b/src/entity/Setting.ts @@ -31,18 +31,4 @@ export default class Setting extends BaseEntity { return single; } - - public static async FetchValueByKeyOrDefault(key: string): Promise { - const connection = getConnection(); - - const repository = connection.getRepository(Setting); - - const single = await repository.findOne({ Key: key }); - - if (!single) { - return DefaultValues.GetValue(key); - } - - return single.Value; - } } \ No newline at end of file diff --git a/src/helpers/SettingsHelper.ts b/src/helpers/SettingsHelper.ts new file mode 100644 index 0000000..e9a33bb --- /dev/null +++ b/src/helpers/SettingsHelper.ts @@ -0,0 +1,37 @@ +import { getConnection } from "typeorm"; +import DefaultValues from "../constants/DefaultValues"; +import Setting from "../entity/Setting"; + +export default class SettingsHelper { + public static async GetSetting(key: string): Promise { + const connection = getConnection(); + + const repository = connection.getRepository(Setting); + + const single = await repository.findOne({ Key: key }); + + if (!single) { + return DefaultValues.GetValue(key); + } + + return single.Value; + } + + public static async SetSetting(key: string, value: string): Promise { + const connection = getConnection(); + + const repository = connection.getRepository(Setting); + + const setting = await repository.findOne({ Key: key }); + + if (setting) { + setting.UpdateBasicDetails(key, value); + + await setting.Save(Setting, setting); + } else { + const newSetting = new Setting(key, value); + + await newSetting.Save(Setting, newSetting); + } + } +} \ No newline at end of file -- 2.43.4 From 6ca08bee789700f6890b8122f712208faef1c7d3 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Thu, 3 Mar 2022 19:45:33 +0000 Subject: [PATCH 03/12] Get commands and events to use database --- src/commands/ban.ts | 2 +- src/commands/kick.ts | 2 +- src/commands/mute.ts | 2 +- src/commands/unmute.ts | 2 +- src/commands/warn.ts | 4 +-- src/constants/DefaultValues.ts | 11 ------ src/entity/Server.ts | 19 ++++++++++ src/entity/Setting.ts | 7 ++-- src/events/MemberEvents.ts | 12 +++---- src/events/MemberEvents/GuildMemberUpdate.ts | 4 +-- src/events/MessageEvents.ts | 8 ++--- src/helpers/SettingsHelper.ts | 37 +++++++++++++------- src/helpers/embeds/EventEmbed.ts | 32 +++++++++++++---- src/helpers/embeds/LogEmbed.ts | 31 ++++++++++++---- tsconfig.json | 2 +- 15 files changed, 119 insertions(+), 56 deletions(-) create mode 100644 src/entity/Server.ts diff --git a/src/commands/ban.ts b/src/commands/ban.ts index f565834..c80deb8 100644 --- a/src/commands/ban.ts +++ b/src/commands/ban.ts @@ -69,7 +69,7 @@ export default class Ban extends Command { await targetMember.ban({ reason: `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}` }); - logEmbed.SendToModLogsChannel(); + await logEmbed.SendToModLogsChannel(); publicEmbed.SendToCurrentChannel(); return { diff --git a/src/commands/kick.ts b/src/commands/kick.ts index 852b57a..ae6a55d 100644 --- a/src/commands/kick.ts +++ b/src/commands/kick.ts @@ -72,7 +72,7 @@ export default class Kick extends Command { await targetMember.kick(`Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`); - logEmbed.SendToModLogsChannel(); + await logEmbed.SendToModLogsChannel(); publicEmbed.SendToCurrentChannel(); return { diff --git a/src/commands/mute.ts b/src/commands/mute.ts index 4b63527..e12b756 100644 --- a/src/commands/mute.ts +++ b/src/commands/mute.ts @@ -85,7 +85,7 @@ export default class Mute extends Command { await targetMember.roles.add(mutedRole, `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`); - logEmbed.SendToModLogsChannel(); + await logEmbed.SendToModLogsChannel(); publicEmbed.SendToCurrentChannel(); return { diff --git a/src/commands/unmute.ts b/src/commands/unmute.ts index 6b5901c..70a1363 100644 --- a/src/commands/unmute.ts +++ b/src/commands/unmute.ts @@ -85,7 +85,7 @@ export default class Unmute extends Command { await targetMember.roles.remove(mutedRole, `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`); - logEmbed.SendToModLogsChannel(); + await logEmbed.SendToModLogsChannel(); publicEmbed.SendToCurrentChannel(); return { diff --git a/src/commands/warn.ts b/src/commands/warn.ts index b4fc236..be15344 100644 --- a/src/commands/warn.ts +++ b/src/commands/warn.ts @@ -15,7 +15,7 @@ export default class Warn extends Command { ]; } - public override execute(context: ICommandContext): ICommandReturnContext { + public override async execute(context: ICommandContext): Promise { const user = context.message.mentions.users.first(); if (!user) { @@ -60,7 +60,7 @@ export default class Warn extends Command { const publicEmbed = new PublicEmbed(context, "", `${user} has been warned`); publicEmbed.AddReason(reason); - logEmbed.SendToModLogsChannel(); + await logEmbed.SendToModLogsChannel(); publicEmbed.SendToCurrentChannel(); return { diff --git a/src/constants/DefaultValues.ts b/src/constants/DefaultValues.ts index 0298e44..e099a5a 100644 --- a/src/constants/DefaultValues.ts +++ b/src/constants/DefaultValues.ts @@ -15,17 +15,6 @@ export default class DefaultValues { private static SetValues() { if (this.values.length == 0) { - // Bot - this.values.push({ Key: "bot.prefix", Value: "v!" }); - this.values.push({ Key: "bot.version", Value: "3.0" }); - this.values.push({ Key: "bot.author", Value: "Vylpes" }); - this.values.push({ Key: "bot.date", Value: "28 Feb 2022" }); - this.values.push({ Key: "bot.owner", Value: "147392775707426816" }); - - // Folders - this.values.push({ Key: "folders.commands", Value: "src/commands" }); - this.values.push({ Key: "folders.events", Value: "src/events" }); - // Commands this.values.push({ Key: "commands.disabled", Value: "" }); this.values.push({ Key: "commands.disabled.message", Value: "This command is disabled." }); diff --git a/src/entity/Server.ts b/src/entity/Server.ts new file mode 100644 index 0000000..180aef0 --- /dev/null +++ b/src/entity/Server.ts @@ -0,0 +1,19 @@ +import { Column, Entity, getConnection, OneToMany } from "typeorm"; +import BaseEntity from "../contracts/BaseEntity"; +import Setting from "./Setting"; + +@Entity() +export default class Server extends BaseEntity { + constructor(serverId: string) { + super(); + + this.Id = serverId; + } + + @OneToMany(() => Setting, x => x.Server) + Settings: Setting[]; + + public AddSettingToServer(setting: Setting) { + this.Settings.push(setting); + } +} \ No newline at end of file diff --git a/src/entity/Setting.ts b/src/entity/Setting.ts index 2743567..bab0ad9 100644 --- a/src/entity/Setting.ts +++ b/src/entity/Setting.ts @@ -1,6 +1,6 @@ -import { Column, Entity, getConnection } from "typeorm"; -import DefaultValues from "../constants/DefaultValues"; +import { Column, Entity, getConnection, ManyToOne } from "typeorm"; import BaseEntity from "../contracts/BaseEntity"; +import Server from "./Server"; @Entity() export default class Setting extends BaseEntity { @@ -17,6 +17,9 @@ export default class Setting extends BaseEntity { @Column() Value: string; + @ManyToOne(() => Server, x => x.Settings) + Server: Server; + public UpdateBasicDetails(key: string, value: string) { this.Key = key; this.Value = value; diff --git a/src/events/MemberEvents.ts b/src/events/MemberEvents.ts index 5bcda27..7ab41fd 100644 --- a/src/events/MemberEvents.ts +++ b/src/events/MemberEvents.ts @@ -9,37 +9,37 @@ export default class MemberEvents extends Event { super(); } - public override guildMemberAdd(member: GuildMember): IEventReturnContext { + public override async guildMemberAdd(member: GuildMember): Promise { const embed = new EventEmbed(member.guild, "Member Joined"); embed.AddUser("User", member.user, true); embed.addField("Created", member.user.createdAt); embed.setFooter(`Id: ${member.user.id}`); - embed.SendToMemberLogsChannel(); + await embed.SendToMemberLogsChannel(); return { embeds: [embed] }; } - public override guildMemberRemove(member: GuildMember): IEventReturnContext { + public override async guildMemberRemove(member: GuildMember): Promise { const embed = new EventEmbed(member.guild, "Member Left"); embed.AddUser("User", member.user, true); embed.addField("Joined", member.joinedAt); embed.setFooter(`Id: ${member.user.id}`); - embed.SendToMemberLogsChannel(); + await embed.SendToMemberLogsChannel(); return { embeds: [embed] }; } - public override guildMemberUpdate(oldMember: GuildMember, newMember: GuildMember): IEventReturnContext { + public override async guildMemberUpdate(oldMember: GuildMember, newMember: GuildMember): Promise { const handler = new GuildMemberUpdate(oldMember, newMember); if (oldMember.nickname != newMember.nickname) { // Nickname change - handler.NicknameChanged(); + await handler.NicknameChanged(); } return { diff --git a/src/events/MemberEvents/GuildMemberUpdate.ts b/src/events/MemberEvents/GuildMemberUpdate.ts index 984721a..ecc4de5 100644 --- a/src/events/MemberEvents/GuildMemberUpdate.ts +++ b/src/events/MemberEvents/GuildMemberUpdate.ts @@ -11,7 +11,7 @@ export default class GuildMemberUpdate { this.newMember = newMember; } - public NicknameChanged(): IEventReturnContext { + public async NicknameChanged(): Promise { const oldNickname = this.oldMember.nickname || "*none*"; const newNickname = this.newMember.nickname || "*none*"; @@ -21,7 +21,7 @@ export default class GuildMemberUpdate { embed.addField("After", newNickname, true); embed.setFooter(`Id: ${this.newMember.user.id}`); - embed.SendToMemberLogsChannel(); + await embed.SendToMemberLogsChannel(); return { embeds: [embed] diff --git a/src/events/MessageEvents.ts b/src/events/MessageEvents.ts index 18f3704..a664d5e 100644 --- a/src/events/MessageEvents.ts +++ b/src/events/MessageEvents.ts @@ -8,7 +8,7 @@ export default class MessageEvents extends Event { super(); } - public override messageDelete(message: Message): IEventReturnContext { + public override async messageDelete(message: Message): Promise { if (!message.guild) { return { embeds: [] @@ -30,14 +30,14 @@ export default class MessageEvents extends Event { embed.addField("Attachments", `\`\`\`${message.attachments.map(x => x.url).join("\n")}\`\`\``); } - embed.SendToMessageLogsChannel(); + await embed.SendToMessageLogsChannel(); return { embeds: [embed] }; } - public override messageUpdate(oldMessage: Message, newMessage: Message): IEventReturnContext { + public override async messageUpdate(oldMessage: Message, newMessage: Message): Promise { if (!newMessage.guild){ return { embeds: [] @@ -62,7 +62,7 @@ export default class MessageEvents extends Event { embed.addField("Before", `\`\`\`${oldMessage.content || "*none*"}\`\`\``); embed.addField("After", `\`\`\`${newMessage.content || "*none*"}\`\`\``); - embed.SendToMessageLogsChannel(); + await embed.SendToMessageLogsChannel(); return { embeds: [embed] diff --git a/src/helpers/SettingsHelper.ts b/src/helpers/SettingsHelper.ts index e9a33bb..35bc342 100644 --- a/src/helpers/SettingsHelper.ts +++ b/src/helpers/SettingsHelper.ts @@ -1,28 +1,37 @@ import { getConnection } from "typeorm"; import DefaultValues from "../constants/DefaultValues"; +import Server from "../entity/Server"; import Setting from "../entity/Setting"; export default class SettingsHelper { - public static async GetSetting(key: string): Promise { - const connection = getConnection(); + public static async GetSetting(key: string, serverId: string): Promise { + const server = await Server.FetchOneById(Server, serverId, [ + "Settings" + ]); - const repository = connection.getRepository(Setting); - - const single = await repository.findOne({ Key: key }); - - if (!single) { + if (!server) { return DefaultValues.GetValue(key); } - return single.Value; + const setting = server.Settings.filter(x => x.Key == key)[0]; + + if (!setting) { + return DefaultValues.GetValue(key); + } + + return setting.Value; } - public static async SetSetting(key: string, value: string): Promise { - const connection = getConnection(); + public static async SetSetting(key: string, serverId: string, value: string): Promise { + const server = await Server.FetchOneById(Server, serverId, [ + "Settings" + ]); - const repository = connection.getRepository(Setting); + if (!server) { + return; + } - const setting = await repository.findOne({ Key: key }); + const setting = server.Settings.filter(x => x.Key == key)[0]; if (setting) { setting.UpdateBasicDetails(key, value); @@ -32,6 +41,10 @@ export default class SettingsHelper { const newSetting = new Setting(key, value); await newSetting.Save(Setting, newSetting); + + server.AddSettingToServer(newSetting); + + await server.Save(Server, server); } } } \ No newline at end of file diff --git a/src/helpers/embeds/EventEmbed.ts b/src/helpers/embeds/EventEmbed.ts index 885961b..a745856 100644 --- a/src/helpers/embeds/EventEmbed.ts +++ b/src/helpers/embeds/EventEmbed.ts @@ -1,4 +1,6 @@ import { MessageEmbed, TextChannel, User, Guild } from "discord.js"; +import { ICommandContext } from "../../contracts/ICommandContext"; +import SettingsHelper from "../SettingsHelper"; export default class EventEmbed extends MessageEmbed { public guild: Guild; @@ -38,15 +40,33 @@ export default class EventEmbed extends MessageEmbed { channel.send(this); } - public SendToMessageLogsChannel() { - this.SendToChannel(process.env.CHANNELS_LOGS_MESSAGE!) + public async SendToMessageLogsChannel() { + const channelName = await SettingsHelper.GetSetting("channels.logs.message", this.guild.id); + + if (!channelName) { + return; + } + + this.SendToChannel(channelName); } - public SendToMemberLogsChannel() { - this.SendToChannel(process.env.CHANNELS_LOGS_MEMBER!) + public async SendToMemberLogsChannel() { + const channelName = await SettingsHelper.GetSetting("channels.logs.member", this.guild.id); + + if (!channelName) { + return; + } + + this.SendToChannel(channelName); } - public SendToModLogsChannel() { - this.SendToChannel(process.env.CHANNELS_LOGS_MOD!) + public async SendToModLogsChannel() { + const channelName = await SettingsHelper.GetSetting("channels.logs.mod", this.guild.id); + + if (!channelName) { + return; + } + + this.SendToChannel(channelName); } } \ No newline at end of file diff --git a/src/helpers/embeds/LogEmbed.ts b/src/helpers/embeds/LogEmbed.ts index f5e2e36..3aa1620 100644 --- a/src/helpers/embeds/LogEmbed.ts +++ b/src/helpers/embeds/LogEmbed.ts @@ -1,6 +1,7 @@ import { MessageEmbed, TextChannel, User } from "discord.js"; import ErrorMessages from "../../constants/ErrorMessages"; import { ICommandContext } from "../../contracts/ICommandContext"; +import SettingsHelper from "../SettingsHelper"; import ErrorEmbed from "./ErrorEmbed"; export default class LogEmbed extends MessageEmbed { @@ -46,15 +47,33 @@ export default class LogEmbed extends MessageEmbed { channel.send(this); } - public SendToMessageLogsChannel() { - this.SendToChannel(process.env.CHANNELS_LOGS_MESSAGE!) + public async SendToMessageLogsChannel() { + const channelName = await SettingsHelper.GetSetting("channels.logs.message", this.context.message.guild?.id!); + + if (!channelName) { + return; + } + + this.SendToChannel(channelName); } - public SendToMemberLogsChannel() { - this.SendToChannel(process.env.CHANNELS_LOGS_MEMBER!) + public async SendToMemberLogsChannel() { + const channelName = await SettingsHelper.GetSetting("channels.logs.member", this.context.message.guild?.id!); + + if (!channelName) { + return; + } + + this.SendToChannel(channelName); } - public SendToModLogsChannel() { - this.SendToChannel(process.env.CHANNELS_LOGS_MOD!) + public async SendToModLogsChannel() { + const channelName = await SettingsHelper.GetSetting("channels.logs.mod", this.context.message.guild?.id!); + + if (!channelName) { + return; + } + + this.SendToChannel(channelName); } } \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 4e61c2e..4cd216c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -30,7 +30,7 @@ // "strictNullChecks": true, /* Enable strict null checks. */ // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + "strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */ // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ -- 2.43.4 From d111363c87af473512d2110ae74c567ee6af8222 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Thu, 10 Mar 2022 19:43:26 +0000 Subject: [PATCH 04/12] Setup and config command --- .env.template | 19 +----- data/config.txt | 18 +++++ docker-compose.yml | 4 +- package.json | 1 + src/Register.ts | 4 ++ src/client/client.ts | 7 +- src/commands/config.ts | 118 +++++++++++++++++++++++++++++++++ src/commands/setup.ts | 34 ++++++++++ src/constants/DefaultValues.ts | 2 +- src/vylbot.ts | 11 +-- tsconfig.json | 2 +- yarn.lock | 72 ++++++++++++++++++-- 12 files changed, 253 insertions(+), 39 deletions(-) create mode 100644 data/config.txt create mode 100644 src/commands/config.ts create mode 100644 src/commands/setup.ts diff --git a/.env.template b/.env.template index 22c5724..4c776c2 100644 --- a/.env.template +++ b/.env.template @@ -14,21 +14,4 @@ BOT_DATE=28 Nov 2021 BOT_OWNERID=147392775707426816 FOLDERS_COMMANDS=src/commands -FOLDERS_EVENTS=src/events - -COMMANDS_DISABLED= -COMMANDS_DISABLED_MESSAGE=This command is disabled. - -COMMANDS_ROLE_ROLES=Notify,VotePings,ProjectUpdates - -COMMANDS_RULES_FILE=data/rules/rules.json - -EMBED_COLOUR=0x3050ba -EMBED_COLOUR_ERROR=0xD52803 - -ROLES_MODERATOR=Moderator -ROLES_MUTED=Muted - -CHANNELS_LOGS_MESSAGE=message-logs -CHANNELS_LOGS_MEMBER=member-logs -CHANNELS_LOGS_MOD=mod-logs \ No newline at end of file +FOLDERS_EVENTS=src/events \ No newline at end of file diff --git a/data/config.txt b/data/config.txt new file mode 100644 index 0000000..efa13ed --- /dev/null +++ b/data/config.txt @@ -0,0 +1,18 @@ +USAGE: [value] + +===[ KEYS ]=== +commands.disabled: Disabled commands (Default: "") +commands.disabled.message: The message to show when a disabled command is ran (Default: "This command is disabled.") + +role.assignable: List of roles assignable to user (Default: []) +role.moderator: The moderator role name (Default: "Moderator") +role.muted: The muted role name (Default: "Muted") + +rules.file: The location of the rules file (Default: "data/rules/rules") + +embed.colour.info: The HEX value of the info embeds (Default: "0x3050ba") +embed.colour.error: The HEX value of the error embeds (Default: "0xd52803") + +channels.logs.message: The channel message events will be logged to (Default: "message-logs") +channels.logs.member: The channel member events will be logged to (Default: "member-logs") +channels.logs.mod: The channel mod events will be logged to (Default: "mod-logs") \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 62c761e..a10aebd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: "3.9" services: - discord: - build: . + # discord: + # build: . database: image: mysql/mysql-server diff --git a/package.json b/package.json index c9f1ecd..e455a26 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "emoji-regex": "^9.2.0", "jest": "^27.4.5", "jest-mock-extended": "^2.0.4", + "mysql": "^2.18.1", "random-bunny": "^2.0.0", "ts-jest": "^27.1.2", "typeorm": "^0.2.44", diff --git a/src/Register.ts b/src/Register.ts index d8390bf..452c6c4 100644 --- a/src/Register.ts +++ b/src/Register.ts @@ -2,6 +2,7 @@ import { CoreClient } from "./client/client"; import About from "./commands/about"; import Ban from "./commands/ban"; import Clear from "./commands/clear"; +import Config from "./commands/config"; import Evaluate from "./commands/eval"; import Help from "./commands/help"; import Kick from "./commands/kick"; @@ -9,6 +10,7 @@ import Mute from "./commands/mute"; import Poll from "./commands/poll"; import Role from "./commands/role"; import Rules from "./commands/rules"; +import Setup from "./commands/setup"; import Unmute from "./commands/unmute"; import Warn from "./commands/warn"; import MemberEvents from "./events/MemberEvents"; @@ -28,6 +30,8 @@ export default class Register { client.RegisterCommand("rules", new Rules()); client.RegisterCommand("unmute", new Unmute()); client.RegisterCommand("warn", new Warn()); + client.RegisterCommand("setup", new Setup()); + client.RegisterCommand("config", new Config()); } public static RegisterEvents(client: CoreClient) { diff --git a/src/client/client.ts b/src/client/client.ts index c52734c..f5a8a6e 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -1,5 +1,6 @@ import { Client } from "discord.js"; import * as dotenv from "dotenv"; +import { createConnection } from "typeorm"; import ICommandItem from "../contracts/ICommandItem"; import IEventItem from "../contracts/IEventItem"; import { Command } from "../type/command"; @@ -34,12 +35,16 @@ export class CoreClient extends Client { this._util = new Util(); } - public start() { + public async start() { if (!process.env.BOT_TOKEN) throw "BOT_TOKEN is not defined in .env"; if (!process.env.BOT_PREFIX) throw "BOT_PREFIX is not defined in .env"; if (!process.env.FOLDERS_COMMANDS) throw "FOLDERS_COMMANDS is not defined in .env"; if (!process.env.FOLDERS_EVENTS) throw "FOLDERS_EVENTS is not defined in .env"; + await createConnection().catch(e => { + throw e; + }); + super.on("message", (message) => this._events.onMessage(message, this._commandItems)); super.on("ready", this._events.onReady); diff --git a/src/commands/config.ts b/src/commands/config.ts new file mode 100644 index 0000000..7e719da --- /dev/null +++ b/src/commands/config.ts @@ -0,0 +1,118 @@ +import { Guild } from "discord.js"; +import { readFileSync } from "fs"; +import DefaultValues from "../constants/DefaultValues"; +import { ICommandContext } from "../contracts/ICommandContext"; +import ICommandReturnContext from "../contracts/ICommandReturnContext"; +import Server from "../entity/Server"; +import Setting from "../entity/Setting"; +import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; +import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import { Command } from "../type/command"; + +export default class Config extends Command { + constructor() { + super(); + super._category = "Administration"; + } + + public override async execute(context: ICommandContext) { + if (!context.message.guild) { + return; + } + + const server = await Server.FetchOneById(Server, context.message.guild?.id, [ + "Settings", + ]); + + if (!server) { + const embed = new ErrorEmbed(context, "This server hasn't been setup yet, please run the setup command"); + embed.SendToCurrentChannel(); + return; + } + + const key = context.args[0]; + const action = context.args[1]; + const value = context.args.splice(2).join(" "); + + if (!key) { + this.SendHelpText(context); + } else if (!action) { + this.GetValue(context, server, key); + } else { + switch(action) { + case 'reset': + this.ResetValue(context, server, key); + break; + case 'set': + if (!value) { + const errorEmbed = new ErrorEmbed(context, "Value is required when setting"); + errorEmbed.SendToCurrentChannel(); + return; + } + + this.SetValue(context, server, key, value); + break; + default: + const errorEmbed = new ErrorEmbed(context, "Action must be either set or reset"); + errorEmbed.SendToCurrentChannel(); + return; + } + } + } + + private async SendHelpText(context: ICommandContext) { + const description = readFileSync(`${process.cwd()}/data/config.txt`).toString(); + + const embed = new PublicEmbed(context, "Config", description); + + embed.SendToCurrentChannel(); + } + + private async GetValue(context: ICommandContext, server: Server, key: string) { + const setting = server.Settings.filter(x => x.Key == key)[0]; + + if (setting) { + const embed = new PublicEmbed(context, "", `${key}: ${setting.Value}`); + embed.SendToCurrentChannel(); + } else { + const embed = new PublicEmbed(context, "", `${key}: ${DefaultValues.GetValue(key)} `); + embed.SendToCurrentChannel(); + } + } + + private async ResetValue(context: ICommandContext, server: Server, key: string) { + const setting = server.Settings.filter(x => x.Key == key)[0]; + + if (!setting) { + const embed = new PublicEmbed(context, "", "Setting has been reset"); + embed.SendToCurrentChannel(); + return; + } + + await Setting.Remove(Setting, setting); + + const embed = new PublicEmbed(context, "", "Setting has been reset"); + embed.SendToCurrentChannel(); + } + + private async SetValue(context: ICommandContext, server: Server, key: string, value: string) { + const setting = server.Settings.filter(x => x.Key == key)[0]; + + if (setting) { + setting.UpdateBasicDetails(key, value); + + await setting.Save(Setting, setting); + } else { + const newSetting = new Setting(key, value); + + await newSetting.Save(Setting, newSetting); + + server.AddSettingToServer(newSetting); + + await server.Save(Server, server); + } + + const embed = new PublicEmbed(context, "", "Setting has been set"); + embed.SendToCurrentChannel(); + } +} \ No newline at end of file diff --git a/src/commands/setup.ts b/src/commands/setup.ts new file mode 100644 index 0000000..73ae1ab --- /dev/null +++ b/src/commands/setup.ts @@ -0,0 +1,34 @@ +import { ICommandContext } from "../contracts/ICommandContext"; +import ICommandReturnContext from "../contracts/ICommandReturnContext"; +import Server from "../entity/Server"; +import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; +import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import { Command } from "../type/command"; + +export default class Setup extends Command { + constructor() { + super(); + super._category = "Administration"; + } + + public override async execute(context: ICommandContext) { + if (!context.message.guild) { + return; + } + + const server = await Server.FetchOneById(Server, context.message.guild?.id); + + if (server) { + const embed = new ErrorEmbed(context, "This server has already been setup, please configure using the config command"); + embed.SendToCurrentChannel(); + return; + } + + const newServer = new Server(context.message.guild?.id); + + await newServer.Save(Server, newServer); + + const embed = new PublicEmbed(context, "Success", "Please configure using the config command"); + embed.SendToCurrentChannel(); + } +} \ No newline at end of file diff --git a/src/constants/DefaultValues.ts b/src/constants/DefaultValues.ts index e099a5a..12f8142 100644 --- a/src/constants/DefaultValues.ts +++ b/src/constants/DefaultValues.ts @@ -1,5 +1,5 @@ export default class DefaultValues { - public static readonly values: ISettingValue[]; + public static readonly values: ISettingValue[] = []; public static GetValue(key: string): string | undefined { this.SetValues(); diff --git a/src/vylbot.ts b/src/vylbot.ts index 56cbdc4..3437d89 100644 --- a/src/vylbot.ts +++ b/src/vylbot.ts @@ -4,16 +4,7 @@ import Register from "./Register"; dotenv.config(); -const requiredConfigs = [ - "EMBED_COLOUR", - "EMBED_COLOUR_ERROR", - "ROLES_MODERATOR", - "ROLES_MUTED", - "CHANNELS_LOGS_MESSAGE", - "CHANNELS_LOGS_MEMBER", - "CHANNELS_LOGS_MOD", - "COMMANDS_ROLE_ROLES", - "COMMANDS_RULES_FILE" +const requiredConfigs: string[] = [ ]; requiredConfigs.forEach(config => { diff --git a/tsconfig.json b/tsconfig.json index 4cd216c..e11fe0b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -63,7 +63,7 @@ /* Experimental Options */ "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ /* Advanced Options */ "skipLibCheck": true, /* Skip type checking of declaration files. */ diff --git a/yarn.lock b/yarn.lock index 92ce5f0..71fe276 100644 --- a/yarn.lock +++ b/yarn.lock @@ -838,6 +838,11 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +bignumber.js@9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.0.tgz#805880f84a329b5eac6e7cb6f8274b6d82bdf075" + integrity sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1047,6 +1052,11 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: dependencies: safe-buffer "~5.1.1" +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -1511,7 +1521,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1: +inherits@2, inherits@^2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -1565,6 +1575,11 @@ is-typedarray@^1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -2245,6 +2260,16 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +mysql@^2.18.1: + version "2.18.1" + resolved "https://registry.yarnpkg.com/mysql/-/mysql-2.18.1.tgz#2254143855c5a8c73825e4522baf2ea021766717" + integrity sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig== + dependencies: + bignumber.js "9.0.0" + readable-stream "2.3.7" + safe-buffer "5.1.2" + sqlstring "2.3.1" + mz@^2.4.0: version "2.7.0" resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" @@ -2432,6 +2457,11 @@ prism-media@^1.2.9: resolved "https://registry.yarnpkg.com/prism-media/-/prism-media-1.3.2.tgz#a1f04423ec15d22f3d62b1987b6a25dc49aad13b" integrity sha512-L6UsGHcT6i4wrQhFF1aPK+MNYgjRqR2tUoIqEY+CG1NqVkMjPRKzS37j9f8GiYPlD6wG9ruBj+q5Ax+bH8Ik1g== +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + prompts@^2.0.1: version "2.4.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" @@ -2476,6 +2506,19 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +readable-stream@2.3.7: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + reflect-metadata@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" @@ -2530,16 +2573,16 @@ rimraf@^3.0.0: dependencies: glob "^7.1.3" +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + safe-buffer@^5.0.1: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -2637,6 +2680,11 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= +sqlstring@2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.1.tgz#475393ff9e91479aea62dcaf0ca3d14983a7fb40" + integrity sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A= + stack-utils@^2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" @@ -2661,6 +2709,13 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -2872,6 +2927,11 @@ universalify@^0.1.2: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" -- 2.43.4 From 1387200c00cd74dd0b9d3bc390cda2bfa2550353 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Wed, 16 Mar 2022 20:12:50 +0000 Subject: [PATCH 05/12] Update commands to check roles per server --- data/config.txt | 1 + ormconfig.json.template | 2 +- src/client/events.ts | 4 +-- src/client/util.ts | 52 +++++++++++++++++++++++++++----- src/commands/ban.ts | 2 +- src/commands/clear.ts | 2 +- src/commands/config.ts | 22 ++++++++++++-- src/commands/kick.ts | 2 +- src/commands/mute.ts | 2 +- src/commands/rules.ts | 2 +- src/commands/setup.ts | 3 ++ src/commands/unmute.ts | 2 +- src/commands/warn.ts | 2 +- src/constants/CommandResponse.ts | 5 +++ src/constants/DefaultValues.ts | 3 +- src/constants/ErrorMessages.ts | 16 ++++++++++ src/type/command.ts | 9 ++++++ 17 files changed, 111 insertions(+), 20 deletions(-) create mode 100644 src/constants/CommandResponse.ts diff --git a/data/config.txt b/data/config.txt index efa13ed..37365fa 100644 --- a/data/config.txt +++ b/data/config.txt @@ -6,6 +6,7 @@ commands.disabled.message: The message to show when a disabled command is ran (D role.assignable: List of roles assignable to user (Default: []) role.moderator: The moderator role name (Default: "Moderator") +role.administrator: The administrator role name (Default: "Administrator") role.muted: The muted role name (Default: "Muted") rules.file: The location of the rules file (Default: "data/rules/rules") diff --git a/ormconfig.json.template b/ormconfig.json.template index 5e24f5c..6c48598 100644 --- a/ormconfig.json.template +++ b/ormconfig.json.template @@ -4,7 +4,7 @@ "port": 3306, "username": "dev", "password": "dev", - "database": "droplet", + "database": "vylbot", "synchronize": true, "logging": false, "entities": [ diff --git a/src/client/events.ts b/src/client/events.ts index ca0d058..6752b82 100644 --- a/src/client/events.ts +++ b/src/client/events.ts @@ -21,7 +21,7 @@ export class Events { // Emit when a message is sent // Used to check for commands - public onMessage(message: Message, commands: ICommandItem[]): IEventResponse { + public async onMessage(message: Message, commands: ICommandItem[]): Promise { if (!message.guild) return { valid: false, message: "Message was not sent in a guild, ignoring.", @@ -43,7 +43,7 @@ export class Events { message: "Command name was not found", }; - const res = this._util.loadCommand(name, args, message, commands); + const res = await this._util.loadCommand(name, args, message, commands); if (!res.valid) { return { diff --git a/src/client/util.ts b/src/client/util.ts index 61ec0f9..07d1fef 100644 --- a/src/client/util.ts +++ b/src/client/util.ts @@ -7,6 +7,10 @@ import { Event } from "../type/event"; import { ICommandContext } from "../contracts/ICommandContext"; import ICommandItem from "../contracts/ICommandItem"; import IEventItem from "../contracts/IEventItem"; +import SettingsHelper from "../helpers/SettingsHelper"; +import StringTools from "../helpers/StringTools"; +import { CommandResponse } from "../constants/CommandResponse"; +import ErrorMessages from "../constants/ErrorMessages"; export interface IUtilResponse extends IBaseResponse { context?: { @@ -18,7 +22,7 @@ export interface IUtilResponse extends IBaseResponse { // Util Class export class Util { - public loadCommand(name: string, args: string[], message: Message, commands: ICommandItem[]): IUtilResponse { + public async loadCommand(name: string, args: string[], message: Message, commands: ICommandItem[]): Promise { if (!message.member) return { valid: false, message: "Member is not part of message", @@ -51,13 +55,26 @@ export class Util { const requiredRoles = item.Command._roles; for (const i in requiredRoles) { - if (!message.member.roles.cache.find(role => role.name == requiredRoles[i])) { - message.reply(`You require the \`${requiredRoles[i]}\` role to run this command`); + if (message.guild) { + const setting = await SettingsHelper.GetSetting(`role.${requiredRoles[i]}`, message.guild?.id); - return { - valid: false, - message: `You require the \`${requiredRoles[i]}\` role to run this command` - }; + if (!setting) { + message.reply("Unable to verify if you have this role, please contact your bot administrator"); + + return { + valid: false, + message: "Unable to verify if you have this role, please contact your bot administrator" + }; + } + + if (!message.member.roles.cache.find(role => role.name == setting)) { + message.reply(`You require the \`${StringTools.Capitalise(setting)}\` role to run this command`); + + return { + valid: false, + message: `You require the \`${StringTools.Capitalise(setting)}\` role to run this command` + }; + } } } @@ -67,6 +84,27 @@ export class Util { message: message }; + const precheckResponse = item.Command.precheck(context); + const precheckAsyncResponse = await item.Command.precheckAsync(context); + + if (precheckResponse != CommandResponse.Ok) { + message.reply(ErrorMessages.GetErrorMessage(precheckResponse)); + + return { + valid: false, + message: ErrorMessages.GetErrorMessage(precheckResponse) + }; + } + + if (precheckAsyncResponse != CommandResponse.Ok) { + message.reply(ErrorMessages.GetErrorMessage(precheckAsyncResponse)); + + return { + valid: false, + message: ErrorMessages.GetErrorMessage(precheckAsyncResponse) + }; + } + item.Command.execute(context); return { diff --git a/src/commands/ban.ts b/src/commands/ban.ts index c80deb8..865a0cf 100644 --- a/src/commands/ban.ts +++ b/src/commands/ban.ts @@ -12,7 +12,7 @@ export default class Ban extends Command { super._category = "Moderation"; super._roles = [ - process.env.ROLES_MODERATOR! + "moderator" ]; } diff --git a/src/commands/clear.ts b/src/commands/clear.ts index 4f8f893..f8e1534 100644 --- a/src/commands/clear.ts +++ b/src/commands/clear.ts @@ -11,7 +11,7 @@ export default class Clear extends Command { super._category = "Moderation"; super._roles = [ - process.env.ROLES_MODERATOR! + "moderator" ]; } diff --git a/src/commands/config.ts b/src/commands/config.ts index 7e719da..5a6a6ef 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -1,5 +1,6 @@ import { Guild } from "discord.js"; import { readFileSync } from "fs"; +import { CommandResponse } from "../constants/CommandResponse"; import DefaultValues from "../constants/DefaultValues"; import { ICommandContext } from "../contracts/ICommandContext"; import ICommandReturnContext from "../contracts/ICommandReturnContext"; @@ -13,6 +14,25 @@ export default class Config extends Command { constructor() { super(); super._category = "Administration"; + super._roles = [ + "administrator" + ] + } + + public override async precheckAsync(context: ICommandContext): Promise { + if (!context.message.guild) { + return CommandResponse.ServerNotSetup; + } + + const server = await Server.FetchOneById(Server, context.message.guild?.id, [ + "Settings", + ]); + + if (!server) { + return CommandResponse.ServerNotSetup; + } + + return CommandResponse.Ok; } public override async execute(context: ICommandContext) { @@ -25,8 +45,6 @@ export default class Config extends Command { ]); if (!server) { - const embed = new ErrorEmbed(context, "This server hasn't been setup yet, please run the setup command"); - embed.SendToCurrentChannel(); return; } diff --git a/src/commands/kick.ts b/src/commands/kick.ts index ae6a55d..6a741a7 100644 --- a/src/commands/kick.ts +++ b/src/commands/kick.ts @@ -12,7 +12,7 @@ export default class Kick extends Command { super._category = "Moderation"; super._roles = [ - process.env.ROLES_MODERATOR! + "moderator" ]; } diff --git a/src/commands/mute.ts b/src/commands/mute.ts index e12b756..79a21ba 100644 --- a/src/commands/mute.ts +++ b/src/commands/mute.ts @@ -12,7 +12,7 @@ export default class Mute extends Command { super._category = "Moderation"; super._roles = [ - process.env.ROLES_MODERATOR! + "moderator" ]; } diff --git a/src/commands/rules.ts b/src/commands/rules.ts index c41f381..a5066c7 100644 --- a/src/commands/rules.ts +++ b/src/commands/rules.ts @@ -18,7 +18,7 @@ export default class Rules extends Command { super._category = "Admin"; super._roles = [ - process.env.ROLES_MODERATOR! + "administrator" ]; } diff --git a/src/commands/setup.ts b/src/commands/setup.ts index 73ae1ab..ee20012 100644 --- a/src/commands/setup.ts +++ b/src/commands/setup.ts @@ -9,6 +9,9 @@ export default class Setup extends Command { constructor() { super(); super._category = "Administration"; + super._roles = [ + "moderator" + ] } public override async execute(context: ICommandContext) { diff --git a/src/commands/unmute.ts b/src/commands/unmute.ts index 70a1363..243d772 100644 --- a/src/commands/unmute.ts +++ b/src/commands/unmute.ts @@ -12,7 +12,7 @@ export default class Unmute extends Command { super._category = "Moderation"; super._roles = [ - process.env.ROLES_MODERATOR! + "moderator" ]; } diff --git a/src/commands/warn.ts b/src/commands/warn.ts index be15344..858f7c3 100644 --- a/src/commands/warn.ts +++ b/src/commands/warn.ts @@ -11,7 +11,7 @@ export default class Warn extends Command { super._category = "Moderation"; super._roles = [ - process.env.ROLES_MODERATOR! + "moderator" ]; } diff --git a/src/constants/CommandResponse.ts b/src/constants/CommandResponse.ts new file mode 100644 index 0000000..968f352 --- /dev/null +++ b/src/constants/CommandResponse.ts @@ -0,0 +1,5 @@ +export enum CommandResponse { + Ok, + Unauthorised, + ServerNotSetup, +} \ No newline at end of file diff --git a/src/constants/DefaultValues.ts b/src/constants/DefaultValues.ts index 12f8142..2f33b12 100644 --- a/src/constants/DefaultValues.ts +++ b/src/constants/DefaultValues.ts @@ -1,5 +1,5 @@ export default class DefaultValues { - public static readonly values: ISettingValue[] = []; + public static values: ISettingValue[] = []; public static GetValue(key: string): string | undefined { this.SetValues(); @@ -22,6 +22,7 @@ export default class DefaultValues { // Role (Command) this.values.push({ Key: "role.assignable", Value: "" }); this.values.push({ Key: "role.moderator", Value: "Moderator" }); + this.values.push({ Key: "role.administrator", Value: "Administrator"}); this.values.push({ Key: "role.muted", Value: "Muted" }); // Rules (Command) diff --git a/src/constants/ErrorMessages.ts b/src/constants/ErrorMessages.ts index a397dcf..f096a8a 100644 --- a/src/constants/ErrorMessages.ts +++ b/src/constants/ErrorMessages.ts @@ -1,5 +1,21 @@ +import { CommandResponse } from "./CommandResponse"; + export default class ErrorMessages { public static readonly InsufficientBotPermissions = "Unable to do this action, am I missing permissions?"; public static readonly ChannelNotFound = "Unable to find channel"; public static readonly RoleNotFound = "Unable to find role"; + + public static readonly UserUnauthorised = "You are not authorised to use this command"; + public static readonly ServerNotSetup = "This server hasn't been setup yet, please run the setup command"; + + public static GetErrorMessage(response: CommandResponse): string { + switch (response) { + case CommandResponse.Unauthorised: + return this.UserUnauthorised; + case CommandResponse.ServerNotSetup: + return this.ServerNotSetup; + default: + return ""; + } + } } \ No newline at end of file diff --git a/src/type/command.ts b/src/type/command.ts index 5a3ae45..a8cfdd7 100644 --- a/src/type/command.ts +++ b/src/type/command.ts @@ -1,3 +1,4 @@ +import { CommandResponse } from "../constants/CommandResponse"; import { ICommandContext } from "../contracts/ICommandContext"; export class Command { @@ -9,6 +10,14 @@ export class Command { this._roles = []; } + public precheck(context: ICommandContext): CommandResponse { + return CommandResponse.Ok; + } + + public async precheckAsync(context: ICommandContext): Promise { + return CommandResponse.Ok; + } + public execute(context: ICommandContext) { } -- 2.43.4 From a0cf29d586d51c3e6f02f89b4383ee2ff6de3f95 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Sat, 19 Mar 2022 16:13:03 +0000 Subject: [PATCH 06/12] Different rules per server Signed-off-by: Ethan Lane --- .../{rules.json => 290149509969739776.json} | 0 data/rules/442730357897429002.json | 88 +++++++++++++++++++ data/rules/501231711271780357.json | 70 +++++++++++++++ src/commands/rules.ts | 4 +- 4 files changed, 160 insertions(+), 2 deletions(-) rename data/rules/{rules.json => 290149509969739776.json} (100%) create mode 100644 data/rules/442730357897429002.json create mode 100644 data/rules/501231711271780357.json diff --git a/data/rules/rules.json b/data/rules/290149509969739776.json similarity index 100% rename from data/rules/rules.json rename to data/rules/290149509969739776.json diff --git a/data/rules/442730357897429002.json b/data/rules/442730357897429002.json new file mode 100644 index 0000000..6fef8cd --- /dev/null +++ b/data/rules/442730357897429002.json @@ -0,0 +1,88 @@ +[ + { + "image": "https://i.imgur.com/bjH1gza.png" + }, + { + "title": "Bot Testing Ground", + "description": [ + "Welcome to Vylpes' Den! Make sure to say hi!", + "Invite link: https://discord.gg/UyAhAVp" + ] + }, + { + "title": "Discord TOS", + "description": [ + "All servers are required to follow the Discord Terms of Service. This includes minimum age requirements (13+). If the moderation team discover a breach of TOS we are required by discord to ban. Make sure you know them!", + "https://discord.com/terms" + ] + }, + { + "title": "Rules", + "description": [ + "**English Only**", + "In order for everyone to understand each other we would like to ask everyone to speak in English only.", + "", + "**No NSFW or Obscene Content**", + "This includes text, images, or links featuring nudity, sex, hard violence, or other graphically disturbing content.", + "", + "**Treat Everyone with Respect**", + "Absolutely no harassment, witch hunting, sexism, racism, or hate speech will be tolerated.", + "", + "**No spam or self promotion**", + "Outside of #self-promo. This includes DMing fellow members.", + "", + "**Keep Politics to #general**", + "And make sure it doesn't become too heated. Debate don't argue.", + "", + "**Drama From Other Servers**", + "Please don't bring up drama from other servers, keep that to DMs", + "", + "**Bot Abuse**", + "Don't abuse the bots or you will be blocked from using them", + "", + "**Event Spoilers**", + "Contents of events and keynotes, such as the Nintendo Direct, must be spoken about in events, this rule applies for up to 24 hours after the event ends. Even though we will only enforce talking there for a set time, please be considerate of those who haven't watched the event yet." + ] + }, + { + "title": "Moderators Discretion", + "description": [ + "Don't argue with a mod's decision. A moderator's choice is final. If you have an issue with a member of the mod team DM me (Vylpes#0001)." + ] + }, + { + "title": "Supporters", + "description": [ + "If you are a Twitch Subscriber or a Patreon Member and have linked your profiles to your discord account you will get exclusive access to the Vylpes Plus channels, including early access to videos!" + ] + }, + { + "title": "Self-Assignable Roles", + "description": [ + "If you want to assign yourself roles, go to #bot-stuff and type v!role . The current roles you can get are:", + "Notify: Get pinged when a new stream or video releases.", + "VotePings: Get pinged when I start a new poll", + "ProjectUpdates: Get pinged when I update my projects as well as new for them" + ] + }, + { + "title": "VylBot", + "description": [ + "This server uses a bot made by me, VylBot, to help moderate the server.", + "For more information on it, see the GitHub repositories:", + "https://github.com/Vylpes/vylbot-core", + "https://github.com/Vylpes/vylbot-app" + ] + }, + { + "title": "Links", + "description": [ + "YouTube: https://www.youtube.com/channel/UCwPlzKwCmP5Q9bCX3fHk2BA", + "Patreon: https://www.patreon.com/vylpes", + "Twitch: https://www.twitch.tv/vylpes_", + "Twitter: https://twitter.com/vylpes", + "Blog: https://vylpes.xyz" + ], + "footer": "Last updated 01/02/2022" + } +] \ No newline at end of file diff --git a/data/rules/501231711271780357.json b/data/rules/501231711271780357.json new file mode 100644 index 0000000..324c40e --- /dev/null +++ b/data/rules/501231711271780357.json @@ -0,0 +1,70 @@ +[ + { + "title": "Welcome to Mankalor's Discord Server!", + "description": [ + "*You must follow Discord's TOS, including the rule where", "you must be 13 years or older.", + "If moderators know you're under 13, we will have to ban you!*", + "", + "You need to input a code in *#entry* which is somewhere in this message before you can start chatting, so read the server rules and info below.", + "If you still don't see the other channels after writing this code, message a moderator. For any issues with this bot, message Vylpes#5725." + ] + }, + { + "title": "Server Rules", + "description": [ + "1. We allow most things in *#general-off-topic*, but if it pertains to a topic that has a channel, post it in the correct chat.", + "", + "2. No spamming, except in *#bot-craziness* and *#spam* ", + " 2a. Those 'Hacker Warning!!! Copy Paste this to all servers!' messages and other copypastas are considered spam.", + "", + "3. Do not insult or harass anyone for race, religion, gender, gaming skills, social skills, etc.", + " 3a. Some people may be new to Discord or a game. Politely educate, don't belittle.", + "", + "4. Absolutely no NSFW content on this server. (Porn, Rule 34, etc.)", + "", + "5. Swearing is allowed, but certain words such as racial/homophobic/disability slurs or sex terms will still be filtered out. Bypassing this will result in punishment.", + " 5a. Swearing past the point of typical rager is still not allowed.", + " 5b. If you're unsure if a word is allowed, then don't use it.", + "", + "6. Avoid unnecessarily & excessively @ mentioning anyone, even in *#bot-craziness* and *#spam*", + "", + "7. Advertising your own content (videos, channels, servers, etc.) should only go in *#self-promo*", + "", + "8. Do not bring up drama from other places here, keep that to DMs.", + "", + "9. Keep it serious in the venting chats, don't joke around.", + " 9a. To access the vent channels, assign yourself the role from *#self-assign-roles*", + "", + "10. Do not ask to become a moderator.", + "", + "11. Please don't join to ask about how to download hacks, where to find them, etc. Requests will be ignored and if you continue to ask you will be muted.", + " 11a. Watch Mankalor's video on it here: https://www.youtube.com/watch?v=wps_4DBlEyM" + ] + }, + { + "description": [ + "1. You can assign yourself a game role in *#self-assign-roles*. When you want to setup a lobby type m!lobby into the game's channel.", + " 1a. Do not ping a role excessively in a short time. The command's cooldown is 20 minutes.", + " 1b. Only use the ping to set up lobbies. Not for advertising, pointless announcements, etc.", + " 1c. Only give yourself a role if you don't mind Discord pings.", + " 1d. Do not complain about the pings if they're being used correctly. Remove the role, or you will be punished by moderators.", + "", + "2. Only server staff can use @ everyone & @ here.", + "3. If you are a Youtube Sponsor or Twitch Subscriber, you can get the role if you sync your Twitch/Youtube account with your Discord account by going into User Settings > Connections > Twitch/Youtube.", + " 3a. This might not work on mobile.", + " 3b. We cannot assign SponSub roles manually.", + "", + "4. Mankalor will ping @ notificationsquad for new videos and streams in #new-videos-streams. If you want these pings, type `m!role Notification Squad` in #self-assign-roles", + "", + "5. Server link in case you want to invite someone. This link is in the description of my videos, too: https://discord.gg/DQkWVbz", + "", + "Not following these rules will result in a warning, mute, or ban, depending on the severity and number of offenses.", + "", + "If you notice anything wrong, notify the *Server Staff*!", + "", + "Once you've sent the code, go say hi in *#general-off-topic*!", + "", + "**Update 01 Oct 2021:** Added `11.` and `11a.` to rules" + ] + } +] \ No newline at end of file diff --git a/src/commands/rules.ts b/src/commands/rules.ts index a5066c7..7860288 100644 --- a/src/commands/rules.ts +++ b/src/commands/rules.ts @@ -23,7 +23,7 @@ export default class Rules extends Command { } public override execute(context: ICommandContext): ICommandReturnContext { - if (!existsSync(`${process.cwd()}/${process.env.COMMANDS_RULES_FILE!}`)) { + if (!existsSync(`${process.cwd()}/data/rules/${context.message.guild?.id}.json`)) { const errorEmbed = new ErrorEmbed(context, "Rules file doesn't exist"); errorEmbed.SendToCurrentChannel(); @@ -33,7 +33,7 @@ export default class Rules extends Command { }; } - const rulesFile = readFileSync(`${process.cwd()}/${process.env.COMMANDS_RULES_FILE}`).toString(); + const rulesFile = readFileSync(`${process.cwd()}/data/rules/${context.message.guild?.id}.json`).toString(); const rules = JSON.parse(rulesFile) as IRules[]; const embeds: PublicEmbed[] = []; -- 2.43.4 From 5d629c3b8f40e6410f59c7f7e8677b589de671e0 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Sat, 19 Mar 2022 16:22:03 +0000 Subject: [PATCH 07/12] Different prefix per server Signed-off-by: Ethan Lane --- data/config.txt | 2 ++ src/client/events.ts | 10 +++++++++- src/constants/DefaultValues.ts | 3 +++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/data/config.txt b/data/config.txt index 37365fa..59f1105 100644 --- a/data/config.txt +++ b/data/config.txt @@ -1,6 +1,8 @@ USAGE: [value] ===[ KEYS ]=== +bot.prefix: The bot prefix for the server (Default: "v!") + commands.disabled: Disabled commands (Default: "") commands.disabled.message: The message to show when a disabled command is ran (Default: "This command is disabled.") diff --git a/src/client/events.ts b/src/client/events.ts index 6752b82..a8d5085 100644 --- a/src/client/events.ts +++ b/src/client/events.ts @@ -1,6 +1,7 @@ import { Message } from "discord.js"; import { IBaseResponse } from "../contracts/IBaseResponse"; import ICommandItem from "../contracts/ICommandItem"; +import SettingsHelper from "../helpers/SettingsHelper"; import { Util } from "./util"; export interface IEventResponse extends IBaseResponse { @@ -32,7 +33,14 @@ export class Events { message: "Message was sent by a bot, ignoring.", }; - const prefix = process.env.BOT_PREFIX as string; + const prefix = await SettingsHelper.GetSetting("bot.prefix", message.guild.id); + + if (!prefix) { + return { + valid: false, + message: "Prefix not found", + }; + } if (message.content.substring(0, prefix.length).toLowerCase() == prefix.toLowerCase()) { const args = message.content.substring(prefix.length).split(" "); diff --git a/src/constants/DefaultValues.ts b/src/constants/DefaultValues.ts index 2f33b12..c1348af 100644 --- a/src/constants/DefaultValues.ts +++ b/src/constants/DefaultValues.ts @@ -15,6 +15,9 @@ export default class DefaultValues { private static SetValues() { if (this.values.length == 0) { + // Bot + this.values.push({ Key: "bot.prefix", Value: "v!" }); + // Commands this.values.push({ Key: "commands.disabled", Value: "" }); this.values.push({ Key: "commands.disabled.message", Value: "This command is disabled." }); -- 2.43.4 From 4c7208c19208e48f8f49d77603aab2dfbc7db665 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Sun, 20 Mar 2022 15:59:23 +0000 Subject: [PATCH 08/12] Add verification system Signed-off-by: Ethan Lane --- data/config.txt | 7 +- src/commands/code.ts | 94 +++++++++++++++++++++++++++ src/constants/CommandResponse.ts | 2 + src/constants/DefaultValues.ts | 6 ++ src/constants/ErrorMessages.ts | 6 ++ src/events/MessageEvents.ts | 13 ++++ src/events/MessageEvents/OnMessage.ts | 59 +++++++++++++++++ src/helpers/StringTools.ts | 13 ++++ src/{Register.ts => registry.ts} | 4 +- src/vylbot.ts | 6 +- 10 files changed, 205 insertions(+), 5 deletions(-) create mode 100644 src/commands/code.ts create mode 100644 src/events/MessageEvents/OnMessage.ts rename src/{Register.ts => registry.ts} (93%) diff --git a/data/config.txt b/data/config.txt index 59f1105..4ce8aaf 100644 --- a/data/config.txt +++ b/data/config.txt @@ -18,4 +18,9 @@ embed.colour.error: The HEX value of the error embeds (Default: "0xd52803") channels.logs.message: The channel message events will be logged to (Default: "message-logs") channels.logs.member: The channel member events will be logged to (Default: "member-logs") -channels.logs.mod: The channel mod events will be logged to (Default: "mod-logs") \ No newline at end of file +channels.logs.mod: The channel mod events will be logged to (Default: "mod-logs") + +verification.enabled: Enables/Disables the verification feature (Default: "false") +verification.channel: The channel to listen to for entry codes (Default: "entry") +verification.role: The server access role (Default: "Entry") +verification.code: The entry code for the channel (Default: "") \ No newline at end of file diff --git a/src/commands/code.ts b/src/commands/code.ts new file mode 100644 index 0000000..d2438fb --- /dev/null +++ b/src/commands/code.ts @@ -0,0 +1,94 @@ +import { CommandResponse } from "../constants/CommandResponse"; +import { ICommandContext } from "../contracts/ICommandContext"; +import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; +import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import SettingsHelper from "../helpers/SettingsHelper"; +import StringTools from "../helpers/StringTools"; +import { Command } from "../type/command"; + +export default class Code extends Command { + constructor() { + super(); + + super._category = "Moderation"; + super._roles = [ + "moderator" + ]; + } + + public override async precheckAsync(context: ICommandContext): Promise { + if (!context.message.guild){ + return CommandResponse.NotInServer; + } + + const isEnabled = await SettingsHelper.GetSetting("verification.enabled", context.message.guild?.id); + + if (!isEnabled) { + return CommandResponse.FeatureDisabled; + } + + if (isEnabled.toLocaleLowerCase() != 'true') { + return CommandResponse.FeatureDisabled; + } + + return CommandResponse.Ok; + } + + public override async execute(context: ICommandContext) { + const action = context.args[0]; + + switch (action) { + case "randomise": + await this.Randomise(context); + break; + case "embed": + await this.SendEmbed(context); + break; + default: + await this.SendUsage(context); + } + } + + private async SendUsage(context: ICommandContext) { + const description = [ + "USAGE: ", + "", + "randomise: Sets the server's entry code to a random code", + "embed: Sends an embed with the server's entry code" + ].join("\n"); + + const embed = new PublicEmbed(context, "", description); + embed.SendToCurrentChannel(); + } + + private async Randomise(context: ICommandContext) { + if (!context.message.guild) { + return; + } + + const randomCode = StringTools.RandomString(5); + + await SettingsHelper.SetSetting("verification.code", context.message.guild.id, randomCode); + + const embed = new PublicEmbed(context, "Code", `Entry code has been set to \`${randomCode}\``); + embed.SendToCurrentChannel(); + } + + private async SendEmbed(context: ICommandContext) { + if (!context.message.guild) { + return; + } + + const code = await SettingsHelper.GetSetting("verification.code", context.message.guild.id); + + if (!code || code == "") { + const errorEmbed = new ErrorEmbed(context, "There is no code for this server setup."); + errorEmbed.SendToCurrentChannel(); + + return; + } + + const embed = new PublicEmbed(context, "Entry Code", code!); + embed.SendToCurrentChannel(); + } +} \ No newline at end of file diff --git a/src/constants/CommandResponse.ts b/src/constants/CommandResponse.ts index 968f352..b876ce8 100644 --- a/src/constants/CommandResponse.ts +++ b/src/constants/CommandResponse.ts @@ -2,4 +2,6 @@ export enum CommandResponse { Ok, Unauthorised, ServerNotSetup, + NotInServer, + FeatureDisabled, } \ No newline at end of file diff --git a/src/constants/DefaultValues.ts b/src/constants/DefaultValues.ts index c1348af..d091029 100644 --- a/src/constants/DefaultValues.ts +++ b/src/constants/DefaultValues.ts @@ -39,6 +39,12 @@ export default class DefaultValues { this.values.push({ Key: "channels.logs.message", Value: "message-logs" }); this.values.push({ Key: "channels.logs.member", Value: "member-logs" }); this.values.push({ Key: "channels.logs.mod", Value: "mod-logs" }); + + // Verification + this.values.push({ Key: "verification.enabled", Value: "false" }); + this.values.push({ Key: "verification.channel", Value: "entry" }); + this.values.push({ Key: "verification.role", Value: "Entry" }); + this.values.push({ Key: "verification.code", Value: "" }); } } } diff --git a/src/constants/ErrorMessages.ts b/src/constants/ErrorMessages.ts index f096a8a..588a143 100644 --- a/src/constants/ErrorMessages.ts +++ b/src/constants/ErrorMessages.ts @@ -7,6 +7,8 @@ export default class ErrorMessages { public static readonly UserUnauthorised = "You are not authorised to use this command"; public static readonly ServerNotSetup = "This server hasn't been setup yet, please run the setup command"; + public static readonly NotInServer = "This command requires to be ran inside of a server"; + public static readonly FeatureDisabled = "This feature is currently disabled by a server moderator"; public static GetErrorMessage(response: CommandResponse): string { switch (response) { @@ -14,6 +16,10 @@ export default class ErrorMessages { return this.UserUnauthorised; case CommandResponse.ServerNotSetup: return this.ServerNotSetup; + case CommandResponse.NotInServer: + return this.NotInServer; + case CommandResponse.FeatureDisabled: + return this.FeatureDisabled; default: return ""; } diff --git a/src/events/MessageEvents.ts b/src/events/MessageEvents.ts index a664d5e..17797b4 100644 --- a/src/events/MessageEvents.ts +++ b/src/events/MessageEvents.ts @@ -2,6 +2,8 @@ import { Event } from "../type/event"; import { Message } from "discord.js"; import EventEmbed from "../helpers/embeds/EventEmbed"; import IEventReturnContext from "../contracts/IEventReturnContext"; +import SettingsHelper from "../helpers/SettingsHelper"; +import OnMessage from "./MessageEvents/OnMessage"; export default class MessageEvents extends Event { constructor() { @@ -68,4 +70,15 @@ export default class MessageEvents extends Event { embeds: [embed] }; } + + public override async message(message: Message) { + if (!message.guild) return; + if (message.author.bot) return; + + const isVerificationEnabled = await SettingsHelper.GetSetting("verification.enabled", message.guild.id); + + if (isVerificationEnabled && isVerificationEnabled.toLocaleLowerCase() == "true") { + await OnMessage.VerificationCheck(message); + } + } } \ No newline at end of file diff --git a/src/events/MessageEvents/OnMessage.ts b/src/events/MessageEvents/OnMessage.ts new file mode 100644 index 0000000..18c2a57 --- /dev/null +++ b/src/events/MessageEvents/OnMessage.ts @@ -0,0 +1,59 @@ +import { Message as Message } from "discord.js"; +import SettingsHelper from "../../helpers/SettingsHelper"; + +export default class OnMessage { + public static async VerificationCheck(message: Message) { + if (!message.guild) return; + + const verificationChannel = await SettingsHelper.GetSetting("verification.channel", message.guild.id); + + if (!verificationChannel) { + return; + } + + const channel = message.guild.channels.cache.find(x => x.name == verificationChannel); + + if (!channel) { + return; + } + + const currentChannel = message.guild.channels.cache.find(x => x == message.channel); + + if (!currentChannel || currentChannel.name != verificationChannel) { + return; + } + + const verificationCode = await SettingsHelper.GetSetting("verification.code", message.guild.id); + + if (!verificationCode || verificationCode == "") { + await message.reply("`verification.code` is not set inside of the server's config. Please contact the server's mod team."); + await message.delete(); + + return; + } + + const verificationRoleName = await SettingsHelper.GetSetting("verification.role", message.guild.id); + + if (!verificationRoleName) { + await message.reply("`verification.role` is not set inside of the server's config. Please contact the server's mod team."); + await message.delete(); + return; + } + + const role = message.guild.roles.cache.find(x => x.name == verificationRoleName); + + if (!role) { + await message.reply("The entry role configured for this server does not exist. Please contact the server's mod team."); + await message.delete(); + return; + } + + if (message.content.toLocaleLowerCase() != verificationCode.toLocaleLowerCase()) { + await message.delete(); + return; + } + + await message.member?.roles.add(role); + await message.delete(); + } +} \ No newline at end of file diff --git a/src/helpers/StringTools.ts b/src/helpers/StringTools.ts index b42eb90..dab3571 100644 --- a/src/helpers/StringTools.ts +++ b/src/helpers/StringTools.ts @@ -12,4 +12,17 @@ export default class StringTools { return result.join(" "); } + + public static RandomString(length: number) { + let result = ""; + + const characters = 'abcdefghkmnpqrstuvwxyz23456789'; + const charactersLength = characters.length; + + for ( var i = 0; i < length; i++ ) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + + return result; + } } \ No newline at end of file diff --git a/src/Register.ts b/src/registry.ts similarity index 93% rename from src/Register.ts rename to src/registry.ts index 452c6c4..81afe62 100644 --- a/src/Register.ts +++ b/src/registry.ts @@ -2,6 +2,7 @@ import { CoreClient } from "./client/client"; import About from "./commands/about"; import Ban from "./commands/ban"; import Clear from "./commands/clear"; +import Code from "./commands/code"; import Config from "./commands/config"; import Evaluate from "./commands/eval"; import Help from "./commands/help"; @@ -16,7 +17,7 @@ import Warn from "./commands/warn"; import MemberEvents from "./events/MemberEvents"; import MessageEvents from "./events/MessageEvents"; -export default class Register { +export default class Registry { public static RegisterCommands(client: CoreClient) { client.RegisterCommand("about", new About()); client.RegisterCommand("ban", new Ban()); @@ -32,6 +33,7 @@ export default class Register { client.RegisterCommand("warn", new Warn()); client.RegisterCommand("setup", new Setup()); client.RegisterCommand("config", new Config()); + client.RegisterCommand("code", new Code()); } public static RegisterEvents(client: CoreClient) { diff --git a/src/vylbot.ts b/src/vylbot.ts index 3437d89..d704cd4 100644 --- a/src/vylbot.ts +++ b/src/vylbot.ts @@ -1,6 +1,6 @@ import { CoreClient } from "./client/client"; import * as dotenv from "dotenv"; -import Register from "./Register"; +import registry from "./registry"; dotenv.config(); @@ -15,7 +15,7 @@ requiredConfigs.forEach(config => { const client = new CoreClient(); -Register.RegisterCommands(client); -Register.RegisterEvents(client); +registry.RegisterCommands(client); +registry.RegisterEvents(client); client.start(); \ No newline at end of file -- 2.43.4 From d5b3daaec51c8ec5279af41b08e3d08b20f03590 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Mon, 21 Mar 2022 18:38:03 +0000 Subject: [PATCH 09/12] Disabled commands per server --- .env.template | 6 +-- data/config.txt | 3 +- package.json | 2 +- src/client/client.ts | 3 -- src/client/util.ts | 8 +++- src/commands/disable.ts | 96 +++++++++++++++++++++++++++++++++++++++++ src/registry.ts | 2 + src/vylbot.ts | 5 +++ 8 files changed, 113 insertions(+), 12 deletions(-) create mode 100644 src/commands/disable.ts diff --git a/.env.template b/.env.template index 4c776c2..d512aac 100644 --- a/.env.template +++ b/.env.template @@ -7,11 +7,7 @@ # any secret values. BOT_TOKEN= -BOT_PREFIX=v! BOT_VER=3.0 BOT_AUTHOR=Vylpes BOT_DATE=28 Nov 2021 -BOT_OWNERID=147392775707426816 - -FOLDERS_COMMANDS=src/commands -FOLDERS_EVENTS=src/events \ No newline at end of file +BOT_OWNERID=147392775707426816 \ No newline at end of file diff --git a/data/config.txt b/data/config.txt index 4ce8aaf..9cb718a 100644 --- a/data/config.txt +++ b/data/config.txt @@ -3,8 +3,7 @@ USAGE: [value] ===[ KEYS ]=== bot.prefix: The bot prefix for the server (Default: "v!") -commands.disabled: Disabled commands (Default: "") -commands.disabled.message: The message to show when a disabled command is ran (Default: "This command is disabled.") +commands.disabled: Disabled commands, separated by commas (Default: "") role.assignable: List of roles assignable to user (Default: []) role.moderator: The moderator role name (Default: "Moderator") diff --git a/package.json b/package.json index e455a26..19b7b24 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "typings": "./dist", "scripts": { "build": "tsc", - "start": "node ./dist/vylbot", + "start": "tsc && node ./dist/vylbot", "test": "jest" }, "repository": { diff --git a/src/client/client.ts b/src/client/client.ts index f5a8a6e..4af3b87 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -37,9 +37,6 @@ export class CoreClient extends Client { public async start() { if (!process.env.BOT_TOKEN) throw "BOT_TOKEN is not defined in .env"; - if (!process.env.BOT_PREFIX) throw "BOT_PREFIX is not defined in .env"; - if (!process.env.FOLDERS_COMMANDS) throw "FOLDERS_COMMANDS is not defined in .env"; - if (!process.env.FOLDERS_EVENTS) throw "FOLDERS_EVENTS is not defined in .env"; await createConnection().catch(e => { throw e; diff --git a/src/client/util.ts b/src/client/util.ts index 07d1fef..951a632 100644 --- a/src/client/util.ts +++ b/src/client/util.ts @@ -28,7 +28,13 @@ export class Util { message: "Member is not part of message", }; - const disabledCommands = process.env.COMMANDS_DISABLED?.split(','); + if (!message.guild) return { + valid: false, + message: "Message is not part of a guild", + }; + + const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", message.guild?.id); + const disabledCommands = disabledCommandsString?.split(","); if (disabledCommands?.find(x => x == name)) { message.reply(process.env.COMMANDS_DISABLED_MESSAGE || "This command is disabled."); diff --git a/src/commands/disable.ts b/src/commands/disable.ts new file mode 100644 index 0000000..a1064c5 --- /dev/null +++ b/src/commands/disable.ts @@ -0,0 +1,96 @@ +import { CommandResponse } from "../constants/CommandResponse"; +import { ICommandContext } from "../contracts/ICommandContext"; +import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; +import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import SettingsHelper from "../helpers/SettingsHelper"; +import StringTools from "../helpers/StringTools"; +import { Command } from "../type/command"; + +export default class Disable extends Command { + constructor() { + super(); + + super._category = "Moderation"; + super._roles = [ + "moderator" + ]; + } + + public override async execute(context: ICommandContext) { + const action = context.args[0]; + + switch (action) { + case "add": + await this.Add(context); + break; + case "remove": + await this.Remove(context); + break; + default: + await this.SendUsage(context); + } + } + + private async SendUsage(context: ICommandContext) { + const description = [ + "USAGE: ", + "", + "add: Adds the command name to the server's disabled command string", + "remove: Removes the command name from the server's disabled command string", + "name: The name of the command to enable/disable" + ].join("\n"); + + const embed = new PublicEmbed(context, "", description); + embed.SendToCurrentChannel(); + } + + private async Add(context: ICommandContext) { + if (!context.message.guild) { + return; + } + + const commandName = context.args[1]; + + if (!commandName) { + this.SendUsage(context); + return; + } + + const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", context.message.guild.id); + const disabledCommands = disabledCommandsString != "" ? disabledCommandsString?.split(",") : []; + + disabledCommands?.push(commandName); + + await SettingsHelper.SetSetting("commands.disabled", context.message.guild.id, disabledCommands!.join(",")); + + const embed = new PublicEmbed(context, "", `Disabled command: ${commandName}`); + embed.SendToCurrentChannel(); + } + + private async Remove(context: ICommandContext) { + if (!context.message.guild) { + return; + } + + const commandName = context.args[1]; + + if (!commandName) { + this.SendUsage(context); + return; + } + + const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", context.message.guild.id); + const disabledCommands = disabledCommandsString != "" ? disabledCommandsString?.split(",") : []; + + const disabledCommandsInstance = disabledCommands?.findIndex(x => x == commandName); + + if (disabledCommandsInstance! > -1) { + disabledCommands?.splice(disabledCommandsInstance!, 1); + } + + await SettingsHelper.SetSetting("commands.disabled", context.message.guild.id, disabledCommands!.join(",")); + + const embed = new PublicEmbed(context, "", `Enabled command: ${commandName}`); + embed.SendToCurrentChannel(); + } +} \ No newline at end of file diff --git a/src/registry.ts b/src/registry.ts index 81afe62..0792dc3 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -4,6 +4,7 @@ import Ban from "./commands/ban"; import Clear from "./commands/clear"; import Code from "./commands/code"; import Config from "./commands/config"; +import Disable from "./commands/disable"; import Evaluate from "./commands/eval"; import Help from "./commands/help"; import Kick from "./commands/kick"; @@ -34,6 +35,7 @@ export default class Registry { client.RegisterCommand("setup", new Setup()); client.RegisterCommand("config", new Config()); client.RegisterCommand("code", new Code()); + client.RegisterCommand("disable", new Disable()) } public static RegisterEvents(client: CoreClient) { diff --git a/src/vylbot.ts b/src/vylbot.ts index d704cd4..5835506 100644 --- a/src/vylbot.ts +++ b/src/vylbot.ts @@ -5,6 +5,11 @@ import registry from "./registry"; dotenv.config(); const requiredConfigs: string[] = [ + "BOT_TOKEN", + "BOT_VER", + "BOT_AUTHOR", + "BOT_DATE", + "BOT_OWNERID", ]; requiredConfigs.forEach(config => { -- 2.43.4 From f7aa6a68b01bc1c5ca6755535358077d2719484c Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Wed, 23 Mar 2022 18:22:37 +0000 Subject: [PATCH 10/12] Add devmode for default prefix --- package.json | 2 +- src/client/client.ts | 5 ++++- src/constants/DefaultValues.ts | 7 ++++++- src/vylbot.ts | 4 +++- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 19b7b24..e455a26 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "typings": "./dist", "scripts": { "build": "tsc", - "start": "tsc && node ./dist/vylbot", + "start": "node ./dist/vylbot", "test": "jest" }, "repository": { diff --git a/src/client/client.ts b/src/client/client.ts index 4af3b87..da09a04 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -1,6 +1,7 @@ import { Client } from "discord.js"; import * as dotenv from "dotenv"; import { createConnection } from "typeorm"; +import DefaultValues from "../constants/DefaultValues"; import ICommandItem from "../contracts/ICommandItem"; import IEventItem from "../contracts/IEventItem"; import { Command } from "../type/command"; @@ -24,10 +25,12 @@ export class CoreClient extends Client { return this._eventItems; } - constructor() { + constructor(devmode: boolean = false) { super(); dotenv.config(); + DefaultValues.useDevPrefix = devmode; + this._commandItems = []; this._eventItems = []; diff --git a/src/constants/DefaultValues.ts b/src/constants/DefaultValues.ts index d091029..8e8e189 100644 --- a/src/constants/DefaultValues.ts +++ b/src/constants/DefaultValues.ts @@ -1,5 +1,6 @@ export default class DefaultValues { public static values: ISettingValue[] = []; + public static useDevPrefix: boolean = false; public static GetValue(key: string): string | undefined { this.SetValues(); @@ -16,7 +17,11 @@ export default class DefaultValues { private static SetValues() { if (this.values.length == 0) { // Bot - this.values.push({ Key: "bot.prefix", Value: "v!" }); + if (this.useDevPrefix) { + this.values.push({ Key: "bot.prefix", Value: "d!" }); + } else { + this.values.push({ Key: "bot.prefix", Value: "v!" }); + } // Commands this.values.push({ Key: "commands.disabled", Value: "" }); diff --git a/src/vylbot.ts b/src/vylbot.ts index 5835506..862171b 100644 --- a/src/vylbot.ts +++ b/src/vylbot.ts @@ -18,7 +18,9 @@ requiredConfigs.forEach(config => { } }); -const client = new CoreClient(); +const devmode = process.argv.find(x => x.toLowerCase() == "--dev") != null; + +const client = new CoreClient(devmode); registry.RegisterCommands(client); registry.RegisterEvents(client); -- 2.43.4 From f0b8025ebc709a414a00e621c99486b91eb59ef3 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Wed, 23 Mar 2022 18:30:02 +0000 Subject: [PATCH 11/12] Update embeds --- data/config.txt | 3 --- src/constants/DefaultValues.ts | 4 ---- src/helpers/embeds/ErrorEmbed.ts | 2 +- src/helpers/embeds/EventEmbed.ts | 2 +- src/helpers/embeds/LogEmbed.ts | 2 +- src/helpers/embeds/PublicEmbed.ts | 2 +- 6 files changed, 4 insertions(+), 11 deletions(-) diff --git a/data/config.txt b/data/config.txt index 9cb718a..3156fb2 100644 --- a/data/config.txt +++ b/data/config.txt @@ -12,9 +12,6 @@ role.muted: The muted role name (Default: "Muted") rules.file: The location of the rules file (Default: "data/rules/rules") -embed.colour.info: The HEX value of the info embeds (Default: "0x3050ba") -embed.colour.error: The HEX value of the error embeds (Default: "0xd52803") - channels.logs.message: The channel message events will be logged to (Default: "message-logs") channels.logs.member: The channel member events will be logged to (Default: "member-logs") channels.logs.mod: The channel mod events will be logged to (Default: "mod-logs") diff --git a/src/constants/DefaultValues.ts b/src/constants/DefaultValues.ts index 8e8e189..6ed44dc 100644 --- a/src/constants/DefaultValues.ts +++ b/src/constants/DefaultValues.ts @@ -36,10 +36,6 @@ export default class DefaultValues { // Rules (Command) this.values.push({ Key: "rules.file", Value: "data/rules/rules" }); - // Embed - this.values.push({ Key: "embed.colour.info", Value: "0x3050ba" }); - this.values.push({ Key: "embed.colour.error", Value: "0xd52803" }); - // Channels this.values.push({ Key: "channels.logs.message", Value: "message-logs" }); this.values.push({ Key: "channels.logs.member", Value: "member-logs" }); diff --git a/src/helpers/embeds/ErrorEmbed.ts b/src/helpers/embeds/ErrorEmbed.ts index 6b2c06d..ab8460a 100644 --- a/src/helpers/embeds/ErrorEmbed.ts +++ b/src/helpers/embeds/ErrorEmbed.ts @@ -7,7 +7,7 @@ export default class ErrorEmbed extends MessageEmbed { constructor(context: ICommandContext, message: String) { super(); - super.setColor(process.env.EMBED_COLOUR_ERROR!); + super.setColor(0xd52803); super.setDescription(message); this.context = context; diff --git a/src/helpers/embeds/EventEmbed.ts b/src/helpers/embeds/EventEmbed.ts index a745856..7cc5522 100644 --- a/src/helpers/embeds/EventEmbed.ts +++ b/src/helpers/embeds/EventEmbed.ts @@ -8,7 +8,7 @@ export default class EventEmbed extends MessageEmbed { constructor(guild: Guild, title: string) { super(); - super.setColor(process.env.EMBED_COLOUR!); + super.setColor(0x3050ba); super.setTitle(title); this.guild = guild; diff --git a/src/helpers/embeds/LogEmbed.ts b/src/helpers/embeds/LogEmbed.ts index 3aa1620..4250780 100644 --- a/src/helpers/embeds/LogEmbed.ts +++ b/src/helpers/embeds/LogEmbed.ts @@ -10,7 +10,7 @@ export default class LogEmbed extends MessageEmbed { constructor(context: ICommandContext, title: string) { super(); - super.setColor(process.env.EMBED_COLOUR!); + super.setColor(0x3050ba); super.setTitle(title); this.context = context; diff --git a/src/helpers/embeds/PublicEmbed.ts b/src/helpers/embeds/PublicEmbed.ts index 2bd40cd..8b3e832 100644 --- a/src/helpers/embeds/PublicEmbed.ts +++ b/src/helpers/embeds/PublicEmbed.ts @@ -7,7 +7,7 @@ export default class PublicEmbed extends MessageEmbed { constructor(context: ICommandContext, title: string, description: string) { super(); - super.setColor(process.env.EMBED_COLOUR!); + super.setColor(0x3050ba); super.setTitle(title); super.setDescription(description); -- 2.43.4 From 29b07e35c56622cdbb81137c527ac84d7683b9f1 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Sun, 27 Mar 2022 10:12:19 +0100 Subject: [PATCH 12/12] Fix broken tests --- jest.config.js | 6 - jest.config.json | 5 + src/client/client.ts | 8 +- tests/client/client.test.ts | 190 ++++++++--------- tests/client/util.test.ts | 32 +-- tests/events/MemberEvents.test.ts | 16 +- .../MemberEvents/GuildMemberUpdate.test.ts | 12 +- tests/events/MessageEvents.test.ts | 40 ++-- tests/helpers/embeds/EventEmbed.test.ts | 160 +++++++++++---- tests/helpers/embeds/LogEmbed.test.ts | 192 ++++++++++++------ 10 files changed, 398 insertions(+), 263 deletions(-) delete mode 100644 jest.config.js create mode 100644 jest.config.json diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 641ea7d..0000000 --- a/jest.config.js +++ /dev/null @@ -1,6 +0,0 @@ -/** @type {import('@ts-jest/dist/types').InitialOptionsTsJest} */ -module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', - setupFiles: ["./jest.setup.js"] -}; \ No newline at end of file diff --git a/jest.config.json b/jest.config.json new file mode 100644 index 0000000..782f4c2 --- /dev/null +++ b/jest.config.json @@ -0,0 +1,5 @@ +{ + "preset": "ts-jest", + "testEnvironment": "node", + "setupFiles": ["./jest.setup.js"] + } \ No newline at end of file diff --git a/src/client/client.ts b/src/client/client.ts index da09a04..36b915b 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -39,10 +39,14 @@ export class CoreClient extends Client { } public async start() { - if (!process.env.BOT_TOKEN) throw "BOT_TOKEN is not defined in .env"; + if (!process.env.BOT_TOKEN) { + console.error("BOT_TOKEN is not defined in .env"); + return; + } await createConnection().catch(e => { - throw e; + console.error(e); + return; }); super.on("message", (message) => this._events.onMessage(message, this._commandItems)); diff --git a/tests/client/client.test.ts b/tests/client/client.test.ts index 46879ba..7f0086f 100644 --- a/tests/client/client.test.ts +++ b/tests/client/client.test.ts @@ -1,3 +1,44 @@ +import { mock } from "jest-mock-extended"; + +const connectionMock = mock(); +const qbuilderMock = mock>(); + +let repositoryMock = mock>(); +let settingMock = mock(); + +jest.mock('typeorm', () => { + qbuilderMock.where.mockReturnThis(); + qbuilderMock.select.mockReturnThis(); + repositoryMock.createQueryBuilder.mockReturnValue(qbuilderMock); + repositoryMock.findOne.mockImplementation(async () => { + return settingMock; + }); + connectionMock.getRepository.mockReturnValue(repositoryMock); + + return { + getConnection: () => connectionMock, + createConnection: () => connectionMock, + + BaseEntity: class Mock {}, + ObjectType: () => {}, + Entity: () => {}, + InputType: () => {}, + Index: () => {}, + PrimaryColumn: () => {}, + Column: () => {}, + CreateDateColumn: () => {}, + UpdateDateColumn: () => {}, + OneToMany: () => {}, + ManyToOne: () => {}, + } +}); + +jest.mock("discord.js"); +jest.mock("dotenv"); +jest.mock("../../src/client/events"); +jest.mock("../../src/client/util"); +jest.mock("../../src/constants/DefaultValues"); + import { CoreClient } from "../../src/client/client"; import { Client } from "discord.js"; @@ -5,139 +46,84 @@ import * as dotenv from "dotenv"; import { Events } from "../../src/client/events"; import { Util } from "../../src/client/util"; import { Command } from "../../src/type/command"; -import { mock } from "jest-mock-extended"; import { Event } from "../../src/type/event"; +import DefaultValues from "../../src/constants/DefaultValues"; +import { Connection, Repository, SelectQueryBuilder } from "typeorm"; +import Setting from "../../src/entity/Setting"; -jest.mock("discord.js"); -jest.mock("dotenv"); -jest.mock("../../src/client/events"); -jest.mock("../../src/client/util"); +beforeEach(() => { + jest.resetAllMocks(); + jest.resetModules(); +}) describe('Constructor', () => { - test('Constructor_ExpectSuccessfulInitialisation', () => { + test('Expect Successful Initialisation', () => { const coreClient = new CoreClient(); expect(coreClient).toBeInstanceOf(Client); expect(dotenv.config).toBeCalledTimes(1); expect(Events).toBeCalledTimes(1); expect(Util).toBeCalledTimes(1); + expect(DefaultValues.useDevPrefix).toBe(false); + }); + + test('Given devmode parameter is true, Expect devmode prefix to be true', () => { + const coreClient = new CoreClient(true); + + expect(coreClient).toBeInstanceOf(Client); + expect(dotenv.config).toBeCalledTimes(1); + expect(Events).toBeCalledTimes(1); + expect(Util).toBeCalledTimes(1); + expect(DefaultValues.useDevPrefix).toBe(true); }); }); describe('Start', () => { - test('Given Env Is Valid, Expect Successful Start', () => { + test('Given Env Is Valid, Expect Successful Start', async () => { process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '!', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: 'events', - } + BOT_TOKEN: "TOKEN", + }; const coreClient = new CoreClient(); + + await coreClient.start(); - expect(() => coreClient.start()).not.toThrow(); expect(coreClient.on).toBeCalledWith("message", expect.any(Function)); expect(coreClient.on).toBeCalledWith("ready", expect.any(Function)); }); - test('Given BOT_TOKEN Is Null, Expect Failure', () => { - process.env = { - BOT_PREFIX: '!', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: 'events', - } + test('Given BOT_TOKEN Is Null, Expect Failure', async () => { + process.env = {}; + + const consoleError = jest.fn(); + + console.error = consoleError; const coreClient = new CoreClient(); - - expect(() => coreClient.start()).toThrow("BOT_TOKEN is not defined in .env"); + + await coreClient.start(); + + expect(consoleError).toBeCalledWith("BOT_TOKEN is not defined in .env"); + expect(coreClient.on).not.toBeCalled(); + expect(coreClient.login).not.toBeCalled(); }); - test('Given BOT_TOKEN Is Empty, Expect Failure', () => { + test('Given BOT_TOKEN Is Empty, Expect Failure', async () => { process.env = { BOT_TOKEN: '', - BOT_PREFIX: '!', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: 'events', } + + const consoleError = jest.fn(); + + console.error = consoleError; const coreClient = new CoreClient(); - - expect(() => coreClient.start()).toThrow("BOT_TOKEN is not defined in .env"); - }); - - test('Given BOT_PREFIX Is Null, Expect Failure', () => { - process.env = { - BOT_TOKEN: 'TOKEN', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: 'events', - } - - const coreClient = new CoreClient(); - - expect(() => coreClient.start()).toThrow("BOT_PREFIX is not defined in .env"); - }); - - test('Given BOT_PREFIX Is Empty, Expect Failure', () => { - process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: 'events', - } - - const coreClient = new CoreClient(); - - expect(() => coreClient.start()).toThrow("BOT_PREFIX is not defined in .env"); - }); - - test('Given FOLDERS_COMMANDS Is Null, Expect Failure', () => { - process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '!', - FOLDERS_EVENTS: 'events', - } - - const coreClient = new CoreClient(); - - expect(() => coreClient.start()).toThrow("FOLDERS_COMMANDS is not defined in .env"); - }); - - test('Given FOLDERS_COMMANDS Is Empty, Expect Failure', () => { - process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '!', - FOLDERS_COMMANDS: '', - FOLDERS_EVENTS: 'events', - } - - const coreClient = new CoreClient(); - - expect(() => coreClient.start()).toThrow("FOLDERS_COMMANDS is not defined in .env"); - }); - - test('Given FOLDERS_EVENTS Is Null, Expect Failure', () => { - process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '!', - FOLDERS_COMMANDS: 'commands', - } - - const coreClient = new CoreClient(); - - expect(() => coreClient.start()).toThrow("FOLDERS_EVENTS is not defined in .env"); - }); - - test('Given FOLDERS_EVENTS Is Empty, Expect Failure', () => { - process.env = { - BOT_TOKEN: 'TOKEN', - BOT_PREFIX: '!', - FOLDERS_COMMANDS: 'commands', - FOLDERS_EVENTS: '', - } - - const coreClient = new CoreClient(); - - expect(() => coreClient.start()).toThrow("FOLDERS_EVENTS is not defined in .env"); + + await coreClient.start(); + + expect(consoleError).toBeCalledWith("BOT_TOKEN is not defined in .env"); + expect(coreClient.on).not.toBeCalled(); + expect(coreClient.login).not.toBeCalled(); }); }); diff --git a/tests/client/util.test.ts b/tests/client/util.test.ts index 6429e24..fe59118 100644 --- a/tests/client/util.test.ts +++ b/tests/client/util.test.ts @@ -15,7 +15,7 @@ beforeEach(() => { }); describe('LoadCommand', () => { - test('Given Successful Exection, Expect Successful Result', () => { + test('Given Successful Exection, Expect Successful Result', async () => { process.env = { BOT_TOKEN: 'TOKEN', BOT_PREFIX: '!', @@ -45,13 +45,13 @@ describe('LoadCommand', () => { const util = new Util(); - const result = util.loadCommand("test", [ "first" ], message, commands); + const result = await util.loadCommand("test", [ "first" ], message, commands); expect(result.valid).toBeTruthy(); expect(cmd.execute).toBeCalled(); }); - test('Given Member Is Null, Expect Failed Result', () => { + test('Given Member Is Null, Expect Failed Result', async () => { process.env = { BOT_TOKEN: 'TOKEN', BOT_PREFIX: '!', @@ -74,14 +74,14 @@ describe('LoadCommand', () => { const util = new Util(); - const result = util.loadCommand("test", [ "first" ], message, commands); + const result = await util.loadCommand("test", [ "first" ], message, commands); expect(result.valid).toBeFalsy(); expect(result.message).toBe("Member is not part of message"); expect(cmd.execute).not.toBeCalled(); }); - test('Given User Does Have Role, Expect Successful Result', () => { + test('Given User Does Have Role, Expect Successful Result', async () => { process.env = { BOT_TOKEN: 'TOKEN', BOT_PREFIX: '!', @@ -111,13 +111,13 @@ describe('LoadCommand', () => { const util = new Util(); - const result = util.loadCommand("test", [ "first" ], message, commands); + const result = await util.loadCommand("test", [ "first" ], message, commands); expect(result.valid).toBeTruthy(); expect(cmd.execute).toBeCalled(); }); - test('Given User Does Not Have Role, Expect Failed Result', () => { + test('Given User Does Not Have Role, Expect Failed Result', async () => { process.env = { BOT_TOKEN: 'TOKEN', BOT_PREFIX: '!', @@ -148,14 +148,14 @@ describe('LoadCommand', () => { const util = new Util(); - const result = util.loadCommand("test", [ "first" ], message, commands); + const result = await util.loadCommand("test", [ "first" ], message, commands); expect(result.valid).toBeFalsy(); expect(result.message).toBe("You require the `Moderator` role to run this command"); expect(cmd.execute).not.toBeCalled(); }); - test('Given command is set to disabled, Expect command to not fire', () => { + test('Given command is set to disabled, Expect command to not fire', async () => { process.env = { BOT_TOKEN: 'TOKEN', BOT_PREFIX: '!', @@ -189,7 +189,7 @@ describe('LoadCommand', () => { const util = new Util(); - const result = util.loadCommand("test", [ "first" ], message, commands); + const result = await util.loadCommand("test", [ "first" ], message, commands); expect(result.valid).toBeFalsy(); expect(result.message).toBe("Command is disabled"); @@ -197,7 +197,7 @@ describe('LoadCommand', () => { expect(cmd.execute).not.toBeCalled(); }); - test('Given command COMMANDS_DISABLED_MESSAGE is empty, Expect default message sent', () => { + test('Given command COMMANDS_DISABLED_MESSAGE is empty, Expect default message sent', async () => { process.env = { BOT_TOKEN: 'TOKEN', BOT_PREFIX: '!', @@ -230,7 +230,7 @@ describe('LoadCommand', () => { const util = new Util(); - const result = util.loadCommand("test", [ "first" ], message, commands); + const result = await util.loadCommand("test", [ "first" ], message, commands); expect(result.valid).toBeFalsy(); expect(result.message).toBe("Command is disabled"); @@ -238,7 +238,7 @@ describe('LoadCommand', () => { expect(cmd.execute).not.toBeCalled(); }); - test('Given a different command is disabled, Expect command to still fire', () => { + test('Given a different command is disabled, Expect command to still fire', async () => { process.env = { BOT_TOKEN: 'TOKEN', BOT_PREFIX: '!', @@ -275,14 +275,14 @@ describe('LoadCommand', () => { const util = new Util(); - const result = util.loadCommand("test", [ "first" ], message, commands); + const result = await util.loadCommand("test", [ "first" ], message, commands); expect(result.valid).toBeTruthy(); expect(cmd.execute).toBeCalled(); expect(otherCmd.execute).not.toBeCalled(); }); - test('Given command is not found in register, expect command not found error', () => { + test('Given command is not found in register, expect command not found error', async () => { process.env = { BOT_TOKEN: 'TOKEN', BOT_PREFIX: '!', @@ -305,7 +305,7 @@ describe('LoadCommand', () => { const util = new Util(); - const result = util.loadCommand("test", [ "first" ], message, commands); + const result = await util.loadCommand("test", [ "first" ], message, commands); expect(result.valid).toBeFalsy(); expect(result.message).toBe('Command not found'); diff --git a/tests/events/MemberEvents.test.ts b/tests/events/MemberEvents.test.ts index ad16485..2d4581a 100644 --- a/tests/events/MemberEvents.test.ts +++ b/tests/events/MemberEvents.test.ts @@ -3,7 +3,7 @@ import MemberEvents from "../../src/events/MemberEvents"; import GuildMemberUpdate from "../../src/events/MemberEvents/GuildMemberUpdate"; describe('GuildMemberAdd', () => { - test('When event is fired, expect embed to be sent to logs channel', () => { + test('When event is fired, expect embed to be sent to logs channel', async () => { const currentDate = new Date(); const textChannel = { @@ -34,7 +34,7 @@ describe('GuildMemberAdd', () => { const memberEvents = new MemberEvents(); - const result = memberEvents.guildMemberAdd(guildMember); + const result = await memberEvents.guildMemberAdd(guildMember); expect(textChannel.send).toBeCalledTimes(1); expect(userDisplayAvatarURL).toBeCalledTimes(1); @@ -63,7 +63,7 @@ describe('GuildMemberAdd', () => { }); describe('GuildMemberRemove', () => { - test('When event is fired, expect embed to be sent to logs channel', () => { + test('When event is fired, expect embed to be sent to logs channel', async () => { const currentDate = new Date(); const textChannel = { @@ -95,7 +95,7 @@ describe('GuildMemberRemove', () => { const memberEvents = new MemberEvents(); - const result = memberEvents.guildMemberRemove(guildMember); + const result = await memberEvents.guildMemberRemove(guildMember); expect(textChannel.send).toBeCalledTimes(1); expect(userDisplayAvatarURL).toBeCalledTimes(1); @@ -124,7 +124,7 @@ describe('GuildMemberRemove', () => { }); describe('GuildMemberUpdate', () => { - test('Given nicknames are the same, expect NicknameChanged NOT to be called', () => { + test('Given nicknames are the same, expect NicknameChanged NOT to be called', async () => { const member = { nickname: 'member' } as unknown as GuildMember; @@ -135,13 +135,13 @@ describe('GuildMemberUpdate', () => { const memberEvents = new MemberEvents(); - const result = memberEvents.guildMemberUpdate(member, member); + const result = await memberEvents.guildMemberUpdate(member, member); expect(result.embeds.length).toBe(0); expect(nicknameChanged).not.toBeCalled(); }); - test('Given nicknames are the different, expect NicknameChanged to be called', () => { + test('Given nicknames are the different, expect NicknameChanged to be called', async () => { const oldMember = { nickname: 'oldMember' } as unknown as GuildMember; @@ -156,7 +156,7 @@ describe('GuildMemberUpdate', () => { const memberEvents = new MemberEvents(); - const result = memberEvents.guildMemberUpdate(oldMember, newMember); + const result = await memberEvents.guildMemberUpdate(oldMember, newMember); expect(result.embeds.length).toBe(0); expect(nicknameChanged).toBeCalledTimes(1); diff --git a/tests/events/MemberEvents/GuildMemberUpdate.test.ts b/tests/events/MemberEvents/GuildMemberUpdate.test.ts index 1697cf9..8acec92 100644 --- a/tests/events/MemberEvents/GuildMemberUpdate.test.ts +++ b/tests/events/MemberEvents/GuildMemberUpdate.test.ts @@ -23,7 +23,7 @@ describe('Constructor', () => { }); describe('NicknameChanged', () => { - test('Given nickname has changed from one to another, expect embed to be sent with both', () => { + test('Given nickname has changed from one to another, expect embed to be sent with both', async () => { process.env = { CHANNELS_LOGS_MOD: 'mod-logs' } @@ -61,7 +61,7 @@ describe('NicknameChanged', () => { const guildMemberUpdate = new GuildMemberUpdate(oldMember, newMember); - const result = guildMemberUpdate.NicknameChanged(); + const result = await guildMemberUpdate.NicknameChanged(); expect(channelSend).toBeCalledTimes(1); expect(memberGuildChannelsCacheFind).toBeCalledTimes(1); @@ -94,7 +94,7 @@ describe('NicknameChanged', () => { expect(embedFieldAfter.value).toBe('New Nickname'); }); - test('Given old nickname was null, expect embed to say old nickname was none', () => { + test('Given old nickname was null, expect embed to say old nickname was none', async () => { process.env = { CHANNELS_LOGS_MOD: 'mod-logs' } @@ -130,7 +130,7 @@ describe('NicknameChanged', () => { const guildMemberUpdate = new GuildMemberUpdate(oldMember, newMember); - const result = guildMemberUpdate.NicknameChanged(); + const result = await guildMemberUpdate.NicknameChanged(); expect(channelSend).toBeCalledTimes(1); expect(memberGuildChannelsCacheFind).toBeCalledTimes(1); @@ -163,7 +163,7 @@ describe('NicknameChanged', () => { expect(embedFieldAfter.value).toBe('New Nickname'); }); - test('Given new nickname was null, expect embed to say new nickname was none', () => { + test('Given new nickname was null, expect embed to say new nickname was none', async () => { process.env = { CHANNELS_LOGS_MOD: 'mod-logs' } @@ -200,7 +200,7 @@ describe('NicknameChanged', () => { const guildMemberUpdate = new GuildMemberUpdate(oldMember, newMember); - const result = guildMemberUpdate.NicknameChanged(); + const result = await guildMemberUpdate.NicknameChanged(); expect(channelSend).toBeCalledTimes(1); expect(memberGuildChannelsCacheFind).toBeCalledTimes(1); diff --git a/tests/events/MessageEvents.test.ts b/tests/events/MessageEvents.test.ts index abecc57..d509bdc 100644 --- a/tests/events/MessageEvents.test.ts +++ b/tests/events/MessageEvents.test.ts @@ -6,7 +6,7 @@ beforeEach(() => { }); describe('MessageDelete', () => { - test('Given message was in a guild AND user was NOT a bot, expect message deleted embed to be sent', () => { + test('Given message was in a guild AND user was NOT a bot, expect message deleted embed to be sent', async () => { process.env = { CHANNELS_LOGS_MOD: 'mod-logs' } @@ -57,7 +57,7 @@ describe('MessageDelete', () => { const messageEvents = new MessageEvents(); - const result = messageEvents.messageDelete(message); + const result = await messageEvents.messageDelete(message); expect(channelSend).toBeCalledTimes(1); expect(memberGuildChannelsCacheFind).toBeCalledTimes(1); @@ -95,7 +95,7 @@ describe('MessageDelete', () => { expect(embedFieldAttachments.value).toBe('```image0.png\nimage1.png```'); }); - test('Given message was not sent in a guild, expect execution stopped', () => { + test('Given message was not sent in a guild, expect execution stopped', async () => { process.env = { CHANNELS_LOGS_MOD: 'mod-logs' } @@ -139,7 +139,7 @@ describe('MessageDelete', () => { const messageEvents = new MessageEvents(); - const result = messageEvents.messageDelete(message); + const result = await messageEvents.messageDelete(message); expect(channelSend).not.toBeCalled(); expect(memberGuildChannelsCacheFind).not.toBeCalled(); @@ -147,7 +147,7 @@ describe('MessageDelete', () => { expect(result.embeds.length).toBe(0); }); - test('Given author is a bot, expect execution stopped', () => { + test('Given author is a bot, expect execution stopped', async () => { process.env = { CHANNELS_LOGS_MOD: 'mod-logs' } @@ -198,7 +198,7 @@ describe('MessageDelete', () => { const messageEvents = new MessageEvents(); - const result = messageEvents.messageDelete(message); + const result = await messageEvents.messageDelete(message); expect(channelSend).not.toBeCalled(); expect(memberGuildChannelsCacheFind).not.toBeCalled(); @@ -206,7 +206,7 @@ describe('MessageDelete', () => { expect(result.embeds.length).toBe(0); }); - test('Given message does not contain any attachments, expect attachments field to be omitted', () => { + test('Given message does not contain any attachments, expect attachments field to be omitted', async () => { process.env = { CHANNELS_LOGS_MOD: 'mod-logs' } @@ -244,7 +244,7 @@ describe('MessageDelete', () => { const messageEvents = new MessageEvents(); - const result = messageEvents.messageDelete(message); + const result = await messageEvents.messageDelete(message); expect(channelSend).toBeCalledTimes(1); expect(memberGuildChannelsCacheFind).toBeCalledTimes(1); @@ -278,7 +278,7 @@ describe('MessageDelete', () => { }); describe('MessageUpdate', () => { - test('Given message is in a guild AND user is not a bot AND the content has actually changed, e xpect log embed to be sent', () => { + test('Given message is in a guild AND user is not a bot AND the content has actually changed, e xpect log embed to be sent', async () => { process.env = { CHANNELS_LOGS_MOD: 'mod-logs' } @@ -317,7 +317,7 @@ describe('MessageUpdate', () => { const messageEvents = new MessageEvents(); - const result = messageEvents.messageUpdate(oldMessage, newMessage); + const result = await messageEvents.messageUpdate(oldMessage, newMessage); expect(channelSend).toBeCalledTimes(1); expect(memberGuildChannelsCacheFind).toBeCalledTimes(1); @@ -357,7 +357,7 @@ describe('MessageUpdate', () => { expect(embedFieldAfter.value).toBe('```New Message```'); }); - test('Given message was not in a guild, expect execution stopped', () => { + test('Given message was not in a guild, expect execution stopped', async () => { process.env = { CHANNELS_LOGS_MOD: 'mod-logs' } @@ -389,7 +389,7 @@ describe('MessageUpdate', () => { const messageEvents = new MessageEvents(); - const result = messageEvents.messageUpdate(oldMessage, newMessage); + const result = await messageEvents.messageUpdate(oldMessage, newMessage); expect(channelSend).not.toBeCalled(); expect(memberGuildChannelsCacheFind).not.toBeCalled(); @@ -397,7 +397,7 @@ describe('MessageUpdate', () => { expect(result.embeds.length).toBe(0); }); - test('Given author is a bot, expect execution stopped', () => { + test('Given author is a bot, expect execution stopped', async () => { process.env = { CHANNELS_LOGS_MOD: 'mod-logs' } @@ -436,7 +436,7 @@ describe('MessageUpdate', () => { const messageEvents = new MessageEvents(); - const result = messageEvents.messageUpdate(oldMessage, newMessage); + const result = await messageEvents.messageUpdate(oldMessage, newMessage); expect(channelSend).not.toBeCalled(); expect(memberGuildChannelsCacheFind).not.toBeCalled(); @@ -444,7 +444,7 @@ describe('MessageUpdate', () => { expect(result.embeds.length).toBe(0); }); - test('Given the message contents are the same, expect execution stopped', () => { + test('Given the message contents are the same, expect execution stopped', async () => { process.env = { CHANNELS_LOGS_MOD: 'mod-logs' } @@ -483,7 +483,7 @@ describe('MessageUpdate', () => { const messageEvents = new MessageEvents(); - const result = messageEvents.messageUpdate(oldMessage, newMessage); + const result = await messageEvents.messageUpdate(oldMessage, newMessage); expect(channelSend).not.toBeCalled(); expect(memberGuildChannelsCacheFind).not.toBeCalled(); @@ -491,7 +491,7 @@ describe('MessageUpdate', () => { expect(result.embeds.length).toBe(0); }); - test('Given Old Message did not have a content, expect field to account for this', () => { + test('Given Old Message did not have a content, expect field to account for this', async () => { process.env = { CHANNELS_LOGS_MOD: 'mod-logs' } @@ -528,7 +528,7 @@ describe('MessageUpdate', () => { const messageEvents = new MessageEvents(); - const result = messageEvents.messageUpdate(oldMessage, newMessage); + const result = await messageEvents.messageUpdate(oldMessage, newMessage); expect(channelSend).toBeCalledTimes(1); expect(memberGuildChannelsCacheFind).toBeCalledTimes(1); @@ -568,7 +568,7 @@ describe('MessageUpdate', () => { expect(embedFieldAfter.value).toBe('```New Message```'); }); - test('Given New Message does not have a content, expect field to account for this', () => { + test('Given New Message does not have a content, expect field to account for this', async () => { process.env = { CHANNELS_LOGS_MOD: 'mod-logs' } @@ -606,7 +606,7 @@ describe('MessageUpdate', () => { const messageEvents = new MessageEvents(); - const result = messageEvents.messageUpdate(oldMessage, newMessage); + const result = await messageEvents.messageUpdate(oldMessage, newMessage); expect(channelSend).toBeCalledTimes(1); expect(memberGuildChannelsCacheFind).toBeCalledTimes(1); diff --git a/tests/helpers/embeds/EventEmbed.test.ts b/tests/helpers/embeds/EventEmbed.test.ts index f5932e3..7cb9251 100644 --- a/tests/helpers/embeds/EventEmbed.test.ts +++ b/tests/helpers/embeds/EventEmbed.test.ts @@ -1,6 +1,7 @@ import { Guild, Message, TextChannel, User } from "discord.js"; import { ICommandContext } from "../../../src/contracts/ICommandContext"; import EventEmbed from "../../../src/helpers/embeds/EventEmbed"; +import SettingsHelper from "../../../src/helpers/SettingsHelper"; beforeEach(() => { process.env = {}; @@ -9,18 +10,11 @@ beforeEach(() => { describe('Constructor', () => { test('Expect properties to be set', () => { - process.env = { - EMBED_COLOUR: '0xd52803', - CHANNELS_LOGS_MESSAGE: 'message-logs', - CHANNELS_LOGS_MEMBER: 'member-logs', - CHANNELS_LOGS_MOD: 'mod-logs' - } - const guild = {} as unknown as Guild; const errorEmbed = new EventEmbed(guild, 'Event Message'); - expect(errorEmbed.color?.toString()).toBe('13969411'); // 0xd52803 in decimal + expect(errorEmbed.color?.toString()).toBe('3166394'); // 0x3050ba in decimal expect(errorEmbed.title).toBe('Event Message'); expect(errorEmbed.guild).toBe(guild); }); @@ -88,15 +82,34 @@ describe('AddUser', () => { }); }); +describe('AddReason', () => { + test('Given a non-empty string is supplied, expect field with message', () => { + const guild = {} as Guild; + + const eventEmbed = new EventEmbed(guild, "Event Embed"); + + eventEmbed.addField = jest.fn(); + + eventEmbed.AddReason("Test reason"); + + expect(eventEmbed.addField).toBeCalledWith("Reason", "Test reason"); + }); + + test('Given an empty string is supplied, expect field with default message', () => { + const guild = {} as Guild; + + const eventEmbed = new EventEmbed(guild, "Event Embed"); + + eventEmbed.addField = jest.fn(); + + eventEmbed.AddReason(""); + + expect(eventEmbed.addField).toBeCalledWith("Reason", "*none*"); + }); +}); + describe('SendToChannel', () => { test('Given channel can be found, expect embed to be sent to that channel', () => { - process.env = { - EMBED_COLOUR: '0xd52803', - CHANNELS_LOGS_MESSAGE: 'message-logs', - CHANNELS_LOGS_MEMBER: 'member-logs', - CHANNELS_LOGS_MOD: 'mod-logs' - } - const channelSend = jest.fn(); const channel = { @@ -153,70 +166,127 @@ describe('SendToChannel', () => { }); describe('SendToMessageLogsChannel', () => { - describe('Expect SendToChannel caleld with CHANNELS_LOGS_MESSAGE as parameter', () => { - process.env = { - EMBED_COLOUR: '0xd52803', - CHANNELS_LOGS_MESSAGE: 'message-logs', - CHANNELS_LOGS_MEMBER: 'member-logs', - CHANNELS_LOGS_MOD: 'mod-logs' - } - + test('Given setting is set, expect SendToChannel to be called with value', async () => { const sendToChannel = jest.fn(); + const getSetting = jest.fn().mockResolvedValue("message-logs"); - const guild = {} as unknown as Guild; + const guild = { + id: "guildId" + } as unknown as Guild; + + SettingsHelper.GetSetting = getSetting; const errorEmbed = new EventEmbed(guild, 'Event Message'); errorEmbed.SendToChannel = sendToChannel; - errorEmbed.SendToMessageLogsChannel(); + await errorEmbed.SendToMessageLogsChannel(); expect(sendToChannel).toBeCalledWith('message-logs'); + expect(getSetting).toBeCalledWith("channels.logs.message", "guildId"); + }); + + test('Given setting is not set, expect function to return', async () => { + const sendToChannel = jest.fn(); + const getSetting = jest.fn().mockResolvedValue(undefined); + + const guild = { + id: "guildId" + } as unknown as Guild; + + SettingsHelper.GetSetting = getSetting; + + const errorEmbed = new EventEmbed(guild, 'Event Message'); + + errorEmbed.SendToChannel = sendToChannel; + + await errorEmbed.SendToMessageLogsChannel(); + + expect(sendToChannel).not.toBeCalled(); + expect(getSetting).toBeCalledWith("channels.logs.message", "guildId"); }); }); describe('SendToMemberLogsChannel', () => { - describe('Expect SendToChannel caleld with CHANNELS_LOGS_MEMBER as parameter', () => { - process.env = { - EMBED_COLOUR: '0xd52803', - CHANNELS_LOGS_MESSAGE: 'message-logs', - CHANNELS_LOGS_MEMBER: 'member-logs', - CHANNELS_LOGS_MOD: 'mod-logs' - } - + test('Given setting is set, expect SendToChannel to be called with value', async () => { const sendToChannel = jest.fn(); + const getSetting = jest.fn().mockResolvedValue("member-logs"); - const guild = {} as unknown as Guild; + const guild = { + id: "guildId" + } as unknown as Guild; + + SettingsHelper.GetSetting = getSetting; const errorEmbed = new EventEmbed(guild, 'Event Message'); errorEmbed.SendToChannel = sendToChannel; - errorEmbed.SendToMemberLogsChannel(); + await errorEmbed.SendToMemberLogsChannel(); expect(sendToChannel).toBeCalledWith('member-logs'); + expect(getSetting).toBeCalledWith("channels.logs.member", "guildId"); + }); + + test('Given setting is not set, expect function to return', async () => { + const sendToChannel = jest.fn(); + const getSetting = jest.fn().mockResolvedValue(undefined); + + const guild = { + id: "guildId" + } as unknown as Guild; + + SettingsHelper.GetSetting = getSetting; + + const errorEmbed = new EventEmbed(guild, 'Event Message'); + + errorEmbed.SendToChannel = sendToChannel; + + await errorEmbed.SendToMemberLogsChannel(); + + expect(sendToChannel).not.toBeCalled(); + expect(getSetting).toBeCalledWith("channels.logs.member", "guildId"); }); }); describe('SendToModLogsChannel', () => { - describe('Expect SendToChannel caleld with CHANNELS_LOGS_MOD as parameter', () => { - process.env = { - EMBED_COLOUR: '0xd52803', - CHANNELS_LOGS_MESSAGE: 'message-logs', - CHANNELS_LOGS_MEMBER: 'member-logs', - CHANNELS_LOGS_MOD: 'mod-logs' - } - + test('Given setting is set, expect SendToChannel to be called with value', async () => { const sendToChannel = jest.fn(); + const getSetting = jest.fn().mockResolvedValue("mod-logs"); - const guild = {} as unknown as Guild; + const guild = { + id: "guildId" + } as unknown as Guild; + + SettingsHelper.GetSetting = getSetting; const errorEmbed = new EventEmbed(guild, 'Event Message'); errorEmbed.SendToChannel = sendToChannel; - errorEmbed.SendToModLogsChannel(); + await errorEmbed.SendToModLogsChannel(); expect(sendToChannel).toBeCalledWith('mod-logs'); + expect(getSetting).toBeCalledWith("channels.logs.mod", "guildId"); + }); + + test('Given setting is not set, expect function to return', async () => { + const sendToChannel = jest.fn(); + const getSetting = jest.fn().mockResolvedValue(undefined); + + const guild = { + id: "guildId" + } as unknown as Guild; + + SettingsHelper.GetSetting = getSetting; + + const errorEmbed = new EventEmbed(guild, 'Event Message'); + + errorEmbed.SendToChannel = sendToChannel; + + await errorEmbed.SendToModLogsChannel(); + + expect(sendToChannel).not.toBeCalled(); + expect(getSetting).toBeCalledWith("channels.logs.mod", "guildId"); }); }); \ No newline at end of file diff --git a/tests/helpers/embeds/LogEmbed.test.ts b/tests/helpers/embeds/LogEmbed.test.ts index 40abd19..4bad4ef 100644 --- a/tests/helpers/embeds/LogEmbed.test.ts +++ b/tests/helpers/embeds/LogEmbed.test.ts @@ -1,6 +1,7 @@ import { Guild, Message, TextChannel, User } from "discord.js"; import { ICommandContext } from "../../../src/contracts/ICommandContext"; import LogEmbed from "../../../src/helpers/embeds/LogEmbed"; +import SettingsHelper from "../../../src/helpers/SettingsHelper"; beforeEach(() => { process.env = {}; @@ -32,7 +33,7 @@ describe('Constructor', () => { const errorEmbed = new LogEmbed(context, 'Log Message'); - expect(errorEmbed.color?.toString()).toBe('13969411'); // 0xd52803 in decimal + expect(errorEmbed.color?.toString()).toBe('3166394'); // 0x3050ba in decimal expect(errorEmbed.title).toBe('Log Message'); expect(errorEmbed.context).toBe(context); }); @@ -220,112 +221,187 @@ describe('SendToChannel', () => { }); describe('SendToMessageLogsChannel', () => { - describe('Expect SendToChannel caleld with CHANNELS_LOGS_MESSAGE as parameter', () => { - process.env = { - EMBED_COLOUR: '0xd52803', - CHANNELS_LOGS_MESSAGE: 'message-logs', - CHANNELS_LOGS_MEMBER: 'member-logs', - CHANNELS_LOGS_MOD: 'mod-logs' - } - + test('Given setting is set, expect SendToChannel to be called with value', async () => { const sendToChannel = jest.fn(); + const getSetting = jest.fn().mockResolvedValue("message-logs"); - const guild = {} as unknown as Guild; - - const messageChannelSend = jest.fn(); + const guild = { + id: "guildId" + } as unknown as Guild; const message = { - channel: { - send: messageChannelSend - } - } as unknown as Message; + guild: guild + } as Message; const context: ICommandContext = { - name: 'command', + name: 'log', args: [], message: message }; - const errorEmbed = new LogEmbed(context, 'Event Message'); + SettingsHelper.GetSetting = getSetting; + + const logEmbed = new LogEmbed(context, 'Event Message'); - errorEmbed.SendToChannel = sendToChannel; + logEmbed.SendToChannel = sendToChannel; - errorEmbed.SendToMessageLogsChannel(); + await logEmbed.SendToMessageLogsChannel(); expect(sendToChannel).toBeCalledWith('message-logs'); + expect(getSetting).toBeCalledWith("channels.logs.message", "guildId"); + }); + + test('Given setting is not set, expect function to return', async () => { + const sendToChannel = jest.fn(); + const getSetting = jest.fn().mockResolvedValue(undefined); + + const guild = { + id: "guildId" + } as unknown as Guild; + + const message = { + guild: guild + } as Message; + + const context: ICommandContext = { + name: 'log', + args: [], + message: message + }; + + SettingsHelper.GetSetting = getSetting; + + const logEmbed = new LogEmbed(context, 'Event Message'); + + logEmbed.SendToChannel = sendToChannel; + + await logEmbed.SendToMessageLogsChannel(); + + expect(sendToChannel).not.toBeCalled(); + expect(getSetting).toBeCalledWith("channels.logs.message", "guildId"); }); }); describe('SendToMemberLogsChannel', () => { - describe('Expect SendToChannel caleld with CHANNELS_LOGS_MEMBER as parameter', () => { - process.env = { - EMBED_COLOUR: '0xd52803', - CHANNELS_LOGS_MESSAGE: 'message-logs', - CHANNELS_LOGS_MEMBER: 'member-logs', - CHANNELS_LOGS_MOD: 'mod-logs' - } - + test('Given setting is set, expect SendToChannel to be called with value', async () => { const sendToChannel = jest.fn(); + const getSetting = jest.fn().mockResolvedValue("member-logs"); - const guild = {} as unknown as Guild; - - const messageChannelSend = jest.fn(); + const guild = { + id: "guildId" + } as unknown as Guild; const message = { - channel: { - send: messageChannelSend - } - } as unknown as Message; + guild: guild + } as Message; const context: ICommandContext = { - name: 'command', + name: 'log', args: [], message: message }; - const errorEmbed = new LogEmbed(context, 'Event Message'); + SettingsHelper.GetSetting = getSetting; + + const logEmbed = new LogEmbed(context, 'Event Message'); - errorEmbed.SendToChannel = sendToChannel; + logEmbed.SendToChannel = sendToChannel; - errorEmbed.SendToMemberLogsChannel(); + await logEmbed.SendToMemberLogsChannel(); expect(sendToChannel).toBeCalledWith('member-logs'); + expect(getSetting).toBeCalledWith("channels.logs.member", "guildId"); + }); + + test('Given setting is not set, expect function to return', async () => { + const sendToChannel = jest.fn(); + const getSetting = jest.fn().mockResolvedValue(undefined); + + const guild = { + id: "guildId" + } as unknown as Guild; + + const message = { + guild: guild + } as Message; + + const context: ICommandContext = { + name: 'log', + args: [], + message: message + }; + + SettingsHelper.GetSetting = getSetting; + + const logEmbed = new LogEmbed(context, 'Event Message'); + + logEmbed.SendToChannel = sendToChannel; + + await logEmbed.SendToMemberLogsChannel(); + + expect(sendToChannel).not.toBeCalled(); + expect(getSetting).toBeCalledWith("channels.logs.member", "guildId"); }); }); describe('SendToModLogsChannel', () => { - describe('Expect SendToChannel caleld with CHANNELS_LOGS_MOD as parameter', () => { - process.env = { - EMBED_COLOUR: '0xd52803', - CHANNELS_LOGS_MESSAGE: 'message-logs', - CHANNELS_LOGS_MEMBER: 'member-logs', - CHANNELS_LOGS_MOD: 'mod-logs' - } - + test('Given setting is set, expect SendToChannel to be called with value', async () => { const sendToChannel = jest.fn(); + const getSetting = jest.fn().mockResolvedValue("mod-logs"); - const guild = {} as unknown as Guild; - - const messageChannelSend = jest.fn(); + const guild = { + id: "guildId" + } as unknown as Guild; const message = { - channel: { - send: messageChannelSend - } - } as unknown as Message; + guild: guild + } as Message; const context: ICommandContext = { - name: 'command', + name: 'log', args: [], message: message }; - const errorEmbed = new LogEmbed(context, 'Event Message'); + SettingsHelper.GetSetting = getSetting; + + const logEmbed = new LogEmbed(context, 'Event Message'); - errorEmbed.SendToChannel = sendToChannel; + logEmbed.SendToChannel = sendToChannel; - errorEmbed.SendToModLogsChannel(); + await logEmbed.SendToModLogsChannel(); expect(sendToChannel).toBeCalledWith('mod-logs'); + expect(getSetting).toBeCalledWith("channels.logs.mod", "guildId"); + }); + + test('Given setting is not set, expect function to return', async () => { + const sendToChannel = jest.fn(); + const getSetting = jest.fn().mockResolvedValue(undefined); + + const guild = { + id: "guildId" + } as unknown as Guild; + + const message = { + guild: guild + } as Message; + + const context: ICommandContext = { + name: 'log', + args: [], + message: message + }; + + SettingsHelper.GetSetting = getSetting; + + const logEmbed = new LogEmbed(context, 'Event Message'); + + logEmbed.SendToChannel = sendToChannel; + + await logEmbed.SendToModLogsChannel(); + + expect(sendToChannel).not.toBeCalled(); + expect(getSetting).toBeCalledWith("channels.logs.mod", "guildId"); }); }); \ No newline at end of file -- 2.43.4