From 2e3e0bbe0a06d259ef7b563952e45084f4b33536 Mon Sep 17 00:00:00 2001 From: Rauf Date: Sun, 19 Jan 2020 13:55:35 -0500 Subject: [PATCH] feat(bot): added eval and connected mongodb --- package-lock.json | 78 +++++++++++++++++++++++++++-- package.json | 4 +- src/PluginClient.ts | 11 ++++ src/assertions/userLevel.ts | 4 +- src/events/lifeguardCommandUsed.ts | 11 +++- src/events/message.ts | 14 +++++- src/index.ts | 5 ++ src/models/User.ts | 26 ++++++++++ src/plugins/Command.ts | 4 +- src/plugins/dev/eval.ts | 80 ++++++++++++++++++++++++++++++ src/util/Database.ts | 30 +++++++++++ 11 files changed, 256 insertions(+), 11 deletions(-) create mode 100644 src/models/User.ts create mode 100644 src/plugins/dev/eval.ts create mode 100644 src/util/Database.ts diff --git a/package-lock.json b/package-lock.json index 54e1099..c9e2511 100644 --- a/package-lock.json +++ b/package-lock.json @@ -96,12 +96,29 @@ "defer-to-connect": "^1.0.1" } }, + "@types/bson": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.1.tgz", + "integrity": "sha512-K6VAEdLVJFBxKp8m5cRTbUfeZpuSvOuLKJLrgw9ANIXo00RiyGzgH4BKWWR4F520gV4tWmxG7q9sKQRVDuzrBw==", + "requires": { + "@types/node": "*" + } + }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, + "@types/mongodb": { + "version": "3.3.14", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.3.14.tgz", + "integrity": "sha512-Ie0Fjoifm/TPY2rNOgixzhNSjDgxgR0dMKQk9XqUXHnkfuw26SpbMXjwECfxSnEdG1bH6bIlpLIK7HvGHQhzqg==", + "requires": { + "@types/bson": "*", + "@types/node": "*" + } + }, "@types/node": { "version": "10.17.13", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.13.tgz", @@ -111,6 +128,7 @@ "version": "6.0.4", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-6.0.4.tgz", "integrity": "sha512-PpPrX7SZW9re6+Ha8ojZG4Se8AZXgf0GK6zmfqEuCsY49LFDNXO3SByp44X3dFEqtB73lkCDAdUazhAjVPiNwg==", + "dev": true, "requires": { "@types/node": "*" } @@ -327,6 +345,11 @@ "concat-map": "0.0.1" } }, + "bson": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.3.tgz", + "integrity": "sha512-TdiJxMVnodVS7r0BdL42y/pqC9cL2iKynVwA0Ho3qbsQYr428veL3l7BQyuqiw+Q5SqqoT0m4srSY/BlZ9AxXg==" + }, "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", @@ -1147,6 +1170,12 @@ "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", "dev": true }, + "memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, "meow": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", @@ -1223,6 +1252,17 @@ "minimist": "0.0.8" } }, + "mongodb": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.4.1.tgz", + "integrity": "sha512-juqt5/Z42J4DcE7tG7UdVaTKmUC6zinF4yioPfpeOSNBieWSK6qCY+0tfGQcHLKrauWPDdMZVROHJOa8q2pWsA==", + "requires": { + "bson": "^1.1.1", + "require_optional": "^1.0.1", + "safe-buffer": "^5.1.2", + "saslprep": "^1.0.0" + } + }, "mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -1511,6 +1551,15 @@ "rc": "^1.2.8" } }, + "require_optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "requires": { + "resolve-from": "^2.0.0", + "semver": "^5.1.0" + } + }, "resolve": { "version": "1.14.2", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.14.2.tgz", @@ -1520,6 +1569,11 @@ "path-parse": "^1.0.6" } }, + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + }, "responselike": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", @@ -1569,8 +1623,7 @@ "safe-buffer": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", - "dev": true + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" }, "safer-buffer": { "version": "2.1.2", @@ -1578,11 +1631,19 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "saslprep": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", + "optional": true, + "requires": { + "sparse-bitfield": "^3.0.3" + } + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, "semver-diff": { "version": "2.1.0", @@ -1619,6 +1680,15 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, + "sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", + "optional": true, + "requires": { + "memory-pager": "^1.0.2" + } + }, "spdx-correct": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", diff --git a/package.json b/package.json index b599a01..d4119d1 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,8 @@ "posttest": "npm.cmd run check" }, "dependencies": { - "discord.js": "github:discordjs/discord.js" + "@types/mongodb": "^3.3.14", + "discord.js": "github:discordjs/discord.js", + "mongodb": "^3.4.1" } } diff --git a/src/PluginClient.ts b/src/PluginClient.ts index 18d7387..1fee445 100644 --- a/src/PluginClient.ts +++ b/src/PluginClient.ts @@ -1,9 +1,20 @@ import { Client, ClientOptions, Collection } from 'discord.js'; import { Plugin } from './plugins/Plugin'; +import { Database } from './util/Database'; +import { name, url } from './config/mongodb'; export class PluginClient extends Client { plugins!: Collection; + db: Database; constructor(options?: ClientOptions) { super(options); + this.db = new Database({ + name, + url, + MongoOptions: { + useNewUrlParser: true, + useUnifiedTopology: true, + }, + }); } } diff --git a/src/assertions/userLevel.ts b/src/assertions/userLevel.ts index b336b9e..90efd12 100644 --- a/src/assertions/userLevel.ts +++ b/src/assertions/userLevel.ts @@ -6,10 +6,10 @@ export function calcUserLevel(user: GuildMember, guild: Guild) { return 5; } if (user.id === guild.ownerID) { - return 4; + return 3; } if (user.permissions.has('ADMINISTRATOR', true)) { - return 3; + return 2; } return 0; } diff --git a/src/events/lifeguardCommandUsed.ts b/src/events/lifeguardCommandUsed.ts index 3aa3b89..ad2a420 100644 --- a/src/events/lifeguardCommandUsed.ts +++ b/src/events/lifeguardCommandUsed.ts @@ -3,6 +3,7 @@ import { Message } from 'discord.js'; import { prefix } from '../config/bot'; import { PluginClient } from '../PluginClient'; import { calcUserLevel } from '../assertions/userLevel'; +import { UserDoc } from '../models/User'; function parseContent(content: string) { const split = content.split(' '); @@ -19,7 +20,13 @@ function getCommandFromPlugin(lifeguard: PluginClient, cmdName: string) { export const event = new Event( 'lifeguardCommandUsed', - async (lifeguard, msg: Message) => { + async (lifeguard, msg: Message, dbUser: UserDoc) => { + if (msg.author.bot) { + return; + } + if (dbUser.blacklisted) { + return; + } const [cmdName, ...args] = parseContent(msg.content); const cmd = getCommandFromPlugin(lifeguard, cmdName); @@ -27,7 +34,7 @@ export const event = new Event( if (msg.member && msg.guild) { const userLevel = calcUserLevel(msg.member, msg.guild); if (userLevel >= cmd.options.level) { - cmd.func(lifeguard, msg, args); + cmd.func(lifeguard, msg, args, dbUser); } } } diff --git a/src/events/message.ts b/src/events/message.ts index de393a9..5cfc58c 100644 --- a/src/events/message.ts +++ b/src/events/message.ts @@ -1,9 +1,21 @@ import { Event } from './Event'; import { Message } from 'discord.js'; import { prefix } from '../config/bot'; +import { User, UserDoc } from '../models/User'; export const event = new Event('message', async (lifeguard, msg: Message) => { + let dbUser = (await lifeguard.db.users.findOne({ + id: msg.author.id, + })) as UserDoc; + if (!dbUser) { + await lifeguard.db.users.insertOne( + new User({ id: msg.author.id, infractions: [] }) + ); + dbUser = (await lifeguard.db.users.findOne({ + id: msg.author.id, + })) as UserDoc; + } if (msg.content.startsWith(prefix)) { - lifeguard.emit('lifeguardCommandUsed', msg); + lifeguard.emit('lifeguardCommandUsed', msg, dbUser); } }); diff --git a/src/index.ts b/src/index.ts index de52783..f77e02a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,11 @@ PluginLoader().then(plugins => { lifeguard.plugins = plugins; }); +lifeguard.db + .connect() + .then(() => console.log('Connected to MongoDB')) + .catch(err => console.error(err)); + lifeguard.login(token).then(() => { if (lifeguard.user) { console.log( diff --git a/src/models/User.ts b/src/models/User.ts new file mode 100644 index 0000000..fa98595 --- /dev/null +++ b/src/models/User.ts @@ -0,0 +1,26 @@ +interface UserInfraction { + action: 'Warn' | 'Mute' | 'Tempban' | 'Ban'; + active: boolean; + guild: string; + id: number; + moderator: string; + reason: string; + time: Date; +} + +export interface UserDoc { + blacklisted?: boolean; + id: string; + infractions: UserInfraction[]; +} + +export class User implements UserDoc { + blacklisted: boolean; + id: string; + infractions: UserInfraction[]; + constructor(data: UserDoc) { + this.blacklisted = data.blacklisted ?? false; + this.id = data.id; + this.infractions = data.infractions; + } +} diff --git a/src/plugins/Command.ts b/src/plugins/Command.ts index f61d46a..d9ce75b 100644 --- a/src/plugins/Command.ts +++ b/src/plugins/Command.ts @@ -1,10 +1,12 @@ import { Message, PermissionString } from 'discord.js'; import { PluginClient } from '../PluginClient'; +import { UserDoc } from '../models/User'; type CommandFunction = ( lifeguard: PluginClient, msg: Message, - args: string[] + args: string[], + dbUser: UserDoc ) => void; interface CommandOptions { diff --git a/src/plugins/dev/eval.ts b/src/plugins/dev/eval.ts new file mode 100644 index 0000000..73d8b7f --- /dev/null +++ b/src/plugins/dev/eval.ts @@ -0,0 +1,80 @@ +import { Command } from '../Command'; +import { MessageEmbed } from 'discord.js'; +import { inspect } from 'util'; +import { runInNewContext } from 'vm'; + +function parseBlock(script: string) { + const cbr = /^(([ \t]*`{3,4})([^\n]*)([\s\S]+?)(^[ \t]*\2))/gm; + const result = cbr.exec(script); + if (result) { + return result[4]; + } + return script; +} + +async function run( + script: string, + ctx: object, + opts: object +): Promise { + try { + const result = await runInNewContext( + `(async () => { ${script} })()`, + ctx, + opts + ); + if (typeof result !== 'string') { + return inspect(result); + } + return result; + } catch (err) { + return err; + } +} + +function makeCodeBlock(data: string, lang?: string) { + return `\`\`\`${lang}\n${data}\n\`\`\``; +} + +export const command = new Command( + 'eval', + async (lifeguard, msg, args, dbUser) => { + const start = Date.now(); + + const script = parseBlock(args.join(' ')); + const exec = await run( + script, + { + lifeguard, + msg, + MessageEmbed, + dbUser, + }, + { filename: msg.guild?.id.toString() } + ); + + const end = Date.now(); + + if (typeof exec === 'string') { + const embed = new MessageEmbed() + .addField('Input', makeCodeBlock(script, 'js')) + .addField('Output', makeCodeBlock(exec, 'js')) + .setFooter(`Script Executed in ${end - start}ms`) + .setTimestamp() + .setColor(0x7289da); + msg.channel.send(embed); + } else { + const embed = new MessageEmbed() + .addField('Input', makeCodeBlock(script, 'js')) + .addField('Output', makeCodeBlock(`${exec.name}: ${exec.message}`)) + .setFooter(`Script Executed in ${end - start}ms`) + .setTimestamp() + .setColor(0x7289da); + msg.channel.send(embed); + } + }, + { + level: 5, + usage: ['eval {code}'], + } +); diff --git a/src/util/Database.ts b/src/util/Database.ts new file mode 100644 index 0000000..1f57bc5 --- /dev/null +++ b/src/util/Database.ts @@ -0,0 +1,30 @@ +import { connect, Db, MongoClientOptions } from 'mongodb'; + +interface DatabaseConfig { + url: string; + name: string; + MongoOptions?: MongoClientOptions; +} + +export class Database { + db!: Db; + constructor(protected config: DatabaseConfig) {} + + async connect() { + const client = await connect( + this.config.url, + this.config.MongoOptions + ).catch(err => { + throw err; + }); + this.db = client.db(this.config.name); + } + + get guilds() { + return this.db.collection('guilds'); + } + + get users() { + return this.db.collection('users'); + } +}