diff --git a/.eslintrc.json b/.eslintrc.json index cf9a1af..4d64bc1 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -39,6 +39,7 @@ "import/prefer-default-export": "off", "no-useless-constructor": "off", "@typescript-eslint/no-useless-constructor": 2, - "import/extensions": "off" + "import/extensions": "off", + "no-param-reassign": "off" } } \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..9a4871d --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,23 @@ +stages: + - lint + - build + +lint: + stage: lint + script: | + yarn install + yarn lint + only: + - pushes + - merge_requests + - web + +tsc: + stage: build + script: | + yarn install + tsc -p tsconfig.json -noEmit + only: + - pushes + - merge_requests + - web \ No newline at end of file diff --git a/Makefile b/Makefile index 6bb691c..ef34253 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ clean: @-rm -rf build build: - -tsc -p ./tsconfig.json + -npx tsc -p ./tsconfig.json run: cd build && node main diff --git a/src/api/index.ts b/src/api/index.ts new file mode 100644 index 0000000..254f045 --- /dev/null +++ b/src/api/index.ts @@ -0,0 +1,5 @@ +import locsh from './loc.sh/main'; + +export default { + 'loc.sh': locsh, +}; diff --git a/src/api/loc.sh/main.ts b/src/api/loc.sh/main.ts index 65f7092..09fbb99 100644 --- a/src/api/loc.sh/main.ts +++ b/src/api/loc.sh/main.ts @@ -1,6 +1,6 @@ import { Server, ServerManagement } from '../../class'; -export default function (management: ServerManagement) { +export default (management: ServerManagement) => { const server = new Server(management, 3890, `${__dirname}/routes`); return server; -} +}; diff --git a/src/api/loc.sh/routes/index.ts b/src/api/loc.sh/routes/index.ts new file mode 100644 index 0000000..011b9db --- /dev/null +++ b/src/api/loc.sh/routes/index.ts @@ -0,0 +1 @@ +export { default as root } from './root'; diff --git a/src/api/loc.sh/routes/root.ts b/src/api/loc.sh/routes/root.ts index 01f478b..406a0d6 100644 --- a/src/api/loc.sh/routes/root.ts +++ b/src/api/loc.sh/routes/root.ts @@ -1,16 +1,18 @@ import { Route, Server } from '../../../class'; -import { RedirectInterface } from '../../../models'; +import { RedirectRaw } from '../../../models'; export default class Root extends Route { constructor(server: Server) { - super(server, { path: '/' }); + super(server); + this.conf = { + path: '/', + }; } public bind() { this.router.get('/:key', async (req, res) => { try { - // @ts-ignore - const link: RedirectInterface = await this.server.client.db.redirect.findOne({ key: req.params.key }).lean().exec(); + const link: RedirectRaw = await this.server.client.db.Redirect.findOne({ key: req.params.key }).lean().exec(); if (!link) return res.status(404).json({ code: this.constants.codes.NOT_FOUND, message: this.constants.messages.NOT_FOUND }); return res.redirect(link.to); } catch (err) { diff --git a/src/api/server.ts b/src/api/server.ts deleted file mode 100644 index 8b13789..0000000 --- a/src/api/server.ts +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/class/Client.ts b/src/class/Client.ts index 04f5244..886c53c 100644 --- a/src/class/Client.ts +++ b/src/class/Client.ts @@ -1,7 +1,7 @@ import eris from 'eris'; import mongoose from 'mongoose'; import { promises as fs } from 'fs'; -import { Collection, Command, Util, ServerManagement } from '.'; +import { Collection, Command, Util, ServerManagement, Event } from '.'; import { Member, MemberInterface, Moderation, ModerationInterface, Redirect, RedirectInterface } from '../models'; export default class Client extends eris.Client { @@ -9,7 +9,7 @@ export default class Client extends eris.Client { public commands: Collection; - public events: Collection; + public events: Collection; public intervals: Collection; @@ -17,15 +17,14 @@ export default class Client extends eris.Client { public serverManagement: ServerManagement; - public db: { member: mongoose.Model, moderation: mongoose.Model, redirect: mongoose.Model }; + public db: { Member: mongoose.Model, Moderation: mongoose.Model, Redirect: mongoose.Model }; - // eslint-disable-next-line @typescript-eslint/no-useless-constructor constructor(token: string, options?: eris.ClientOptions) { super(token, options); this.commands = new Collection(); - this.events = new Collection(); + this.events = new Collection(); this.intervals = new Collection(); - this.db = { member: Member, moderation: Moderation, redirect: Redirect }; + this.db = { Member, Moderation, Redirect }; } public async loadDatabase() { @@ -48,27 +47,23 @@ export default class Client extends eris.Client { }); } - public async loadEvents() { - const evtFiles = await fs.readdir(`${__dirname}/../events`); - evtFiles.forEach((file) => { - const eventName = file.split('.')[0]; - if (file === 'index.js') return; - // eslint-disable-next-line - const event = new (require(`${__dirname}/../events/${file}`).default)(this); - this.events.add(eventName, event); - this.on(eventName, (...args) => event.run(...args)); - this.util.signale.success(`Successfully loaded event: ${eventName}`); - delete require.cache[require.resolve(`${__dirname}/../events/${file}`)]; - }); + public async loadEvents(eventFiles: { [s: string]: typeof Event; } | ArrayLike) { + const evtFiles = Object.entries(eventFiles); + for (const [name, Ev] of evtFiles) { + const event = new Ev(this); + this.events.add(event.event, event); + this.on(event.event, event.run); + this.util.signale.success(`Successfully loaded event: ${name}`); + delete require.cache[require.resolve(`${__dirname}/../events/${name}`)]; + } } - public async loadCommands() { - const commandFiles = await fs.readdir(`${__dirname}/../commands`); - commandFiles.forEach((file) => { - // eslint-disable-next-line new-cap - const command: Command = new (require(`${__dirname}/../commands/${file}`).default)(this); + public async loadCommands(commandFiles: { [s: string]: typeof Command; } | ArrayLike) { + const cmdFiles = Object.values(commandFiles); + for (const Cmd of cmdFiles) { + const command = new Cmd(this); this.commands.add(command.name, command); this.util.signale.success(`Successfully loaded command: ${command.name}`); - }); + } } } diff --git a/src/class/Event.ts b/src/class/Event.ts new file mode 100644 index 0000000..658f18c --- /dev/null +++ b/src/class/Event.ts @@ -0,0 +1,15 @@ +import { Client } from '.'; + +export default class Event { + public client: Client + + public event: string; + + constructor(client: Client) { + this.client = client; + this.event = ''; + this.run = this.run.bind(this); + } + + public async run(...args: any[]): Promise { return Promise.resolve(); } +} diff --git a/src/class/Moderation.ts b/src/class/Moderation.ts index 0f730d9..1a55eaa 100644 --- a/src/class/Moderation.ts +++ b/src/class/Moderation.ts @@ -111,7 +111,7 @@ export default class Moderation { return mod.save(); } - public async kick(user: User, moderator: Member, reason?: string): Promise { + public async kick(user: Member|User, moderator: Member, reason?: string): Promise { if (reason && reason.length > 512) throw new Error('Kick reason cannot be longer than 512 characters'); await this.client.guilds.get(this.client.config.guildID).kickMember(user.id, reason); const logID = randomBytes(2).toString('hex'); diff --git a/src/class/RichEmbed.ts b/src/class/RichEmbed.ts index fba5d52..38cf297 100644 --- a/src/class/RichEmbed.ts +++ b/src/class/RichEmbed.ts @@ -1,20 +1,8 @@ /* eslint-disable no-param-reassign */ -export interface EmbedData { - title?: string - description?: string - url?: string - timestamp?: Date - color?: number - footer?: { text: string, icon_url?: string, proxy_icon_url?: string} - image?: { url: string, proxy_url?: string, height?: number, width?: number } - thumbnail?: { url: string, proxy_url?: string, height?: number, width?: number } - video?: { url: string, height?: number, width?: number } - author?: { name: string, url?: string, proxy_icon_url?: string, icon_url?: string} - fields?: {name: string, value: string, inline?: boolean}[] -} +import { EmbedOptions } from 'eris'; -export default class RichEmbed implements EmbedData { +export default class RichEmbed implements EmbedOptions { title?: string type?: string @@ -23,15 +11,15 @@ export default class RichEmbed implements EmbedData { url?: string - timestamp?: Date + timestamp?: string | Date color?: number footer?: { text: string, icon_url?: string, proxy_icon_url?: string} - image?: { url: string, proxy_url?: string, height?: number, width?: number } + image?: { url?: string, proxy_url?: string, height?: number, width?: number } - thumbnail?: { url: string, proxy_url?: string, height?: number, width?: number } + thumbnail?: { url?: string, proxy_url?: string, height?: number, width?: number } video?: { url: string, height?: number, width?: number } @@ -41,7 +29,7 @@ export default class RichEmbed implements EmbedData { fields?: {name: string, value: string, inline?: boolean}[] - constructor(data: EmbedData = {}) { + constructor(data: EmbedOptions = {}) { /* let types: { title?: string, type?: string, description?: string, url?: string, timestamp?: Date, color?: number, fields?: {name: string, value: string, inline?: boolean}[] @@ -124,8 +112,7 @@ export default class RichEmbed implements EmbedData { * Sets the timestamp of this embed. */ setTimestamp(timestamp = new Date()) { - // eslint-disable-next-line no-restricted-globals - if (isNaN(timestamp.getTime())) throw new TypeError('Expecting ISO8601 (Date constructor)'); + if (Number.isNaN(timestamp.getTime())) throw new TypeError('Expecting ISO8601 (Date constructor)'); this.timestamp = timestamp; return this; } diff --git a/src/class/Route.ts b/src/class/Route.ts index 53a3852..1abdcda 100644 --- a/src/class/Route.ts +++ b/src/class/Route.ts @@ -9,9 +9,9 @@ export default class Route { public router: Router; - constructor(server: Server, options: { path: string, deprecated?: boolean, maintenance?: boolean }) { + constructor(server: Server) { this.server = server; - this.conf = options; + this.conf = { path: '' }; this.router = Router(); } diff --git a/src/class/Server.ts b/src/class/Server.ts index 26fbb91..b7cc5b7 100644 --- a/src/class/Server.ts +++ b/src/class/Server.ts @@ -31,10 +31,9 @@ export default class Server { } public async loadRoutes() { - const routes = await fs.readdir(`${this.root}`); - for (const routeFile of routes) { - // eslint-disable-next-line new-cap - const route: Route = new (require(`${this.root}/${routeFile}`).default)(this); + const routes = Object.values(require(this.root)); + for (const RouteFile of routes) { + const route = new RouteFile(this); if (route.conf.deprecated) { route.deprecated(); } else if (route.conf.maintenance) { diff --git a/src/class/ServerManagement.ts b/src/class/ServerManagement.ts index 9936a84..efb9d3e 100644 --- a/src/class/ServerManagement.ts +++ b/src/class/ServerManagement.ts @@ -1,7 +1,5 @@ -import express from 'express'; -import { promises as fs } from 'fs'; import { Client, Collection, Server } from '.'; -// import serverSetup from '../api/server'; +import serverSetup from '../api'; export default class ServerManagement { public client: Client; @@ -15,12 +13,9 @@ export default class ServerManagement { } public async loadServers() { - const apiRoot = await fs.readdir(`${__dirname}/../api`); - for (const api of apiRoot) { - // eslint-disable-next-line no-continue - if (api === 'server.js') continue; - const server: Server = require(`${__dirname}/../api/${api}/main.js`).default(this); - this.servers.add(api, server); + const apiRoot = Object.entries<(management: ServerManagement) => Server>(serverSetup); + for (const [api, server] of apiRoot) { + this.servers.add(api, server(this)); this.client.util.signale.success(`Successfully loaded server '${api}'.`); } } diff --git a/src/class/Util.ts b/src/class/Util.ts index 4083c10..fce0037 100644 --- a/src/class/Util.ts +++ b/src/class/Util.ts @@ -37,8 +37,6 @@ export default class Util { */ public resolveCommand(query: string | string[]): Promise<{cmd: Command, args: string[] }> { try { - // let resolvedCommand: Command; - // eslint-disable-next-line no-param-reassign if (typeof query === 'string') query = query.split(' '); const commands = this.client.commands.toArray(); const resolvedCommand = commands.find((c) => c.name === query[0].toLowerCase() || c.aliases.includes(query[0].toLowerCase())); @@ -91,7 +89,6 @@ export default class Util { } await this.client.createMessage('595788220764127272', info); const msg = message ? message.content.slice(this.client.config.prefix.length).trim().split(/ +/g) : []; - // eslint-disable-next-line no-param-reassign if (command && disable) this.resolveCommand(msg).then((c) => { c.cmd.enabled = false; }); if (message) message.channel.createMessage(`***${this.emojis.ERROR} An unexpected error has occured - please contact a Staff member.${command && disable ? ' This command has been disabled.' : ''}***`); } catch (err) { @@ -101,7 +98,6 @@ export default class Util { public splitString(string: string, length: number): string[] { if (!string) return []; - // eslint-disable-next-line no-param-reassign if (Array.isArray(string)) string = string.join('\n'); if (string.length <= length) return [string]; const arrayString: string[] = []; @@ -111,7 +107,6 @@ export default class Util { pos = string.length > length ? string.lastIndexOf('\n', length) : string.length; if (pos > length) pos = length; str = string.substr(0, pos); - // eslint-disable-next-line no-param-reassign string = string.substr(pos); arrayString.push(str); } diff --git a/src/class/index.ts b/src/class/index.ts index 105aa46..b3ab3bb 100644 --- a/src/class/index.ts +++ b/src/class/index.ts @@ -1,6 +1,7 @@ export { default as Client } from './Client'; export { default as Collection } from './Collection'; export { default as Command } from './Command'; +export { default as Event } from './Event'; export { default as Moderation } from './Moderation'; export { default as RichEmbed } from './RichEmbed'; export { default as Route } from './Route'; diff --git a/src/commands/additem.ts b/src/commands/additem.ts index 05073bf..d487a70 100644 --- a/src/commands/additem.ts +++ b/src/commands/additem.ts @@ -23,10 +23,9 @@ export default class AddItem extends Command { return message.channel.createMessage({ embed }); } if (args[0].split('-')[0] === 'os' && ['arch', 'deb', 'cent', 'fedora', 'manjaro', 'mdarwin', 'redhat', 'ubuntu', 'win'].includes(args[0].split('-')[1])) { - const account = await this.client.db.member.findOne({ userID: message.member.id }); + const account = await this.client.db.Member.findOne({ userID: message.member.id }); if (!account) { - // eslint-disable-next-line new-cap - const newAccount = new this.client.db.member({ + const newAccount = new this.client.db.Member({ userID: message.member.id, additional: { operatingSystems: [args[0].split('-')[1]], @@ -39,10 +38,9 @@ export default class AddItem extends Command { return message.channel.createMessage(`***${this.client.util.emojis.SUCCESS} Added OS code ${args[0]} to profile.***`); } if (args[0].split('-')[0] === 'lang' && ['js', 'py', 'rb', 'ts', 'rs', 'go', 'cfam', 'csharp', 'swift', 'java', 'kt', 'asm'].includes(args[0].split('-')[1])) { - const account = await this.client.db.member.findOne({ userID: message.member.id }); + const account = await this.client.db.Member.findOne({ userID: message.member.id }); if (!account) { - // eslint-disable-next-line new-cap - const newAccount = new this.client.db.member({ + const newAccount = new this.client.db.Member({ userID: message.member.id, additional: { langs: [args[0].split('-')[1]], diff --git a/src/commands/addredirect.ts b/src/commands/addredirect.ts index 92f6e0f..709389c 100644 --- a/src/commands/addredirect.ts +++ b/src/commands/addredirect.ts @@ -15,7 +15,7 @@ export default class AddRedirect extends Command { public async run(message: Message, args: string[]) { try { if (!args[0]) return this.client.commands.get('help').run(message, [this.name]); - const check = await this.client.db.redirect.findOne({ key: args[1].toLowerCase() }); + const check = await this.client.db.Redirect.findOne({ key: args[1].toLowerCase() }); if (check) return this.error(message.channel, `Redirect key ${args[1].toLowerCase()} already exists. Linked to: ${check.to}`); try { const test = new URL(args[0]); @@ -24,8 +24,7 @@ export default class AddRedirect extends Command { return this.error(message.channel, 'This doesn\'t appear to be a valid URL.'); } if ((/^[a-zA-Z0-9]+$/gi.test(args[1].toLowerCase().replace('-', '').trim()) === false) || args[1].toLowerCase().length > 15) return this.error(message.channel, 'Invalid key. The key must be alphanumeric and less than 16 characters.'); - // eslint-disable-next-line new-cap - const redirect = new this.client.db.redirect({ + const redirect = new this.client.db.Redirect({ key: args[1].toLowerCase(), to: args[0], }); diff --git a/src/commands/delitem.ts b/src/commands/delitem.ts index ade9cb3..164a3af 100644 --- a/src/commands/delitem.ts +++ b/src/commands/delitem.ts @@ -23,16 +23,16 @@ export default class DelItem extends Command { return message.channel.createMessage({ embed }); } if (args[0].split('-')[0] === 'os' && ['arch', 'deb', 'cent', 'fedora', 'manjaro', 'mdarwin', 'redhat', 'ubuntu', 'win'].includes(args[0].split('-')[1])) { - const account = await this.client.db.member.findOne({ userID: message.member.id }); - if (!account || !account?.additional.operatingSystems || account?.additional.operatingSystems.length < 1) { + const account = await this.client.db.Member.findOne({ userID: message.member.id }); + if (account?.additional.operatingSystems.length < 1) { return message.channel.createMessage(`***${this.client.util.emojis.ERROR} You don't have any operating systems to remove.***`); } await account.updateOne({ $pull: { 'additional.operatingSystems': args[0].split('-')[1] } }); return message.channel.createMessage(`***${this.client.util.emojis.SUCCESS} Removed OS code ${args[0]} from profile.***`); } if (args[0].split('-')[0] === 'lang' && ['js', 'py', 'rb', 'ts', 'rs', 'go', 'cfam', 'csharp', 'swift', 'java', 'kt', 'asm'].includes(args[0].split('-')[1])) { - const account = await this.client.db.member.findOne({ userID: message.member.id }); - if (!account || !account?.additional.langs || account?.additional.langs.length < 1) { + const account = await this.client.db.Member.findOne({ userID: message.member.id }); + if (account?.additional.langs.length < 1) { return message.channel.createMessage(`***${this.client.util.emojis.ERROR} You don't have any languages to remove.***`); } await account.updateOne({ $pull: { 'additional.langs': args[0].split('-')[1] } }); diff --git a/src/commands/delredirect.ts b/src/commands/delredirect.ts index 7c864f7..1dd8ae9 100644 --- a/src/commands/delredirect.ts +++ b/src/commands/delredirect.ts @@ -15,9 +15,9 @@ export default class DelRedirect extends Command { public async run(message: Message, args: string[]) { try { if (!args[0]) return this.client.commands.get('help').run(message, [this.name]); - const check = await this.client.db.redirect.findOne({ key: args[0].toLowerCase() }); + const check = await this.client.db.Redirect.findOne({ key: args[0].toLowerCase() }); if (!check) return this.error(message.channel, `Redirect key ${args[0].toLowerCase()} doesn't exist.`); - await this.client.db.redirect.deleteOne({ key: args[0].toLowerCase() }); + await this.client.db.Redirect.deleteOne({ key: args[0].toLowerCase() }); return this.success(message.channel, `Deleted redirect https://loc.sh/${args[0].toLowerCase()}.`); } catch (err) { return this.client.util.handleError(err, message, this); diff --git a/src/commands/djs.ts b/src/commands/djs.ts index 9060c82..224150a 100644 --- a/src/commands/djs.ts +++ b/src/commands/djs.ts @@ -1,5 +1,5 @@ -import { Message } from 'eris'; -import axios from 'axios'; +import { Message, EmbedOptions } from 'eris'; +import axios, { AxiosResponse } from 'axios'; import { Client, Command, RichEmbed } from '../class'; export default class DJS extends Command { @@ -17,34 +17,10 @@ export default class DJS extends Command { try { if (!args[0]) return this.client.commands.get('help').run(message, [this.name]); - let res; - try { - res = await axios.get(`https://djsdocs.sorta.moe/v2/embed?src=master&q=${args[0]}`); - } catch (err) { - this.error(message.channel, 'Please try again later, something unexpected happened.'); - return this.client.util.handleError(err, message, this); - } - const { data } = res; + const { data }: AxiosResponse = await axios.get(`https://djsdocs.sorta.moe/v2/embed?src=master&q=${args[0]}`); if (!data) return this.error(message.channel, 'Could not find information. Try something else.'); - const name: string = data.author?.name || ''; - const icon_url: string = data.author?.icon_url || ''; - const author_url: string = data.author?.url || ''; - const description: string = data.description || 'None'; - const title: string = data.title || ''; - - const embed = new RichEmbed(); - embed.setAuthor(name, icon_url, author_url); - embed.setColor(0x2296f3); - embed.setTitle(title); - embed.setDescription(description); - if (data.fields !== undefined && data.fields.length > 0) { - data.fields.forEach((field) => { - embed.addField(field.name, field.value); - }); - } - embed.setFooter(this.client.user.username, this.client.user.avatarURL); - embed.setTimestamp(); + const embed = new RichEmbed(data); return message.channel.createMessage({ embed }); } catch (err) { return this.client.util.handleError(err, message, this); diff --git a/src/commands/game.ts b/src/commands/game.ts index fded83d..40ecb6f 100644 --- a/src/commands/game.ts +++ b/src/commands/game.ts @@ -47,13 +47,9 @@ export default class Game extends Command { embed.setColor('#1ed760'); embed.addField('Song', mainStatus.details, true); embed.addField('Artist', mainStatus.state, true); - // @ts-ignore embed.addField('Album', mainStatus.assets.large_text); - // @ts-ignore embed.addField('Start', `${new Date(mainStatus.timestamps.start).toLocaleTimeString('en-us')} ET`, true); - // @ts-ignore embed.addField('End', `${new Date(mainStatus.timestamps.end).toLocaleTimeString('en-us')} ET`, true); - // @ts-ignore embed.setThumbnail(`https://i.scdn.co/image/${mainStatus.assets.large_image.split(':')[1]}`); embed.setFooter(`Listening to Spotify | ${this.client.user.username}`, 'https://media.discordapp.net/attachments/358674161566220288/496894273304920064/2000px-Spotify_logo_without_text.png'); embed.setTimestamp(); diff --git a/src/commands/help.ts b/src/commands/help.ts index ed362d5..bed8280 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -12,7 +12,6 @@ export default class Help extends Command { this.enabled = true; } - // eslint-disable-next-line consistent-return public async run(message: Message, args: string[]) { try { if (args.length > 0) { @@ -110,7 +109,7 @@ export default class Help extends Command { if (cmdPages.length === 1) return message.channel.createMessage({ embed: cmdPages[0] }); return createPaginationEmbed(message, cmdPages); } catch (err) { - this.client.util.handleError(err, message, this, false); + return this.client.util.handleError(err, message, this, false); } } } diff --git a/src/commands/index.ts b/src/commands/index.ts new file mode 100644 index 0000000..b319ac8 --- /dev/null +++ b/src/commands/index.ts @@ -0,0 +1,17 @@ +export { default as additem } from './additem'; +export { default as addredirect } from './addredirect'; +export { default as ban } from './ban'; +export { default as delitem } from './delitem'; +export { default as delredirect } from './delredirect'; +export { default as djs } from './djs'; +export { default as eval } from './eval'; +export { default as game } from './game'; +export { default as help } from './help'; +export { default as info } from './info'; +export { default as kick } from './kick'; +export { default as listredirects } from './listredirects'; +export { default as npm } from './npm'; +export { default as ping } from './ping'; +export { default as roleinfo } from './roleinfo'; +export { default as unban } from './unban'; +export { default as whois } from './whois'; diff --git a/src/commands/kick.ts b/src/commands/kick.ts index 45c3de4..2bf5c51 100644 --- a/src/commands/kick.ts +++ b/src/commands/kick.ts @@ -1,4 +1,4 @@ -import { Message, User } from 'eris'; +import { Message, User, Member } from 'eris'; import { Client, Command } from '../class'; export default class Kick extends Command { @@ -15,16 +15,15 @@ export default class Kick extends Command { public async run(message: Message, args: string[]) { try { if (!args[0]) return this.client.commands.get('help').run(message, [this.name]); - const member = this.client.util.resolveMember(args[0], this.client.guilds.get(this.client.config.guildID)); - let user: User; - if (!member) { + let user: Member = this.client.util.resolveMember(args[0], this.client.guilds.get(this.client.config.guildID)); + if (!user) { try { - user = await this.client.getRESTUser(args[0]); + user = await this.client.getRESTGuildMember(this.client.config.guildID, args[0]); } catch { return this.error(message.channel, 'Cannot find user.'); } } - if (member && !this.client.util.moderation.checkPermissions(member, message.member)) return this.error(message.channel, 'Permission Denied.'); + if (user && !this.client.util.moderation.checkPermissions(user, message.member)) return this.error(message.channel, 'Permission Denied.'); message.delete(); const reason: string = args[1]; diff --git a/src/commands/listredirects.ts b/src/commands/listredirects.ts index ed1d17e..9df7801 100644 --- a/src/commands/listredirects.ts +++ b/src/commands/listredirects.ts @@ -16,7 +16,7 @@ export default class DelRedirect extends Command { public async run(message: Message, args: string[]) { try { if (args[0]) { - const redirects = await this.client.db.redirect.find({ $or: [{ key: args[0].toLowerCase() }, { to: args[0].toLowerCase() }] }); + const redirects = await this.client.db.Redirect.find({ $or: [{ key: args[0].toLowerCase() }, { to: args[0].toLowerCase() }] }); if (redirects.length <= 0) return this.error(message.channel, 'Could not find an entry matching that query.'); const embed = new RichEmbed(); embed.setTitle('Redirect Information'); @@ -27,7 +27,7 @@ export default class DelRedirect extends Command { embed.setTimestamp(); return message.channel.createMessage({ embed }); } - const redirects = await this.client.db.redirect.find(); + const redirects = await this.client.db.Redirect.find(); if (!redirects) return this.error(message.channel, 'No redirect links found.'); const redirectArray: [{ name: string, value: string }?] = []; for (const redirect of redirects) { diff --git a/src/commands/npm.ts b/src/commands/npm.ts index 7cb8c74..5fec51d 100644 --- a/src/commands/npm.ts +++ b/src/commands/npm.ts @@ -34,8 +34,7 @@ export default class NPM extends Command { license = data.license; } let dependencies: string = 'None'; - // eslint-disable-next-line no-prototype-builtins - if (version !== 'Unknown' && data.versions[version].hasOwnProperty('dependencies') && Object.keys(data.versions[version].dependencies).length > 0) { + if (version !== 'Unknown' && data.versions[version].dependencies !== undefined && Object.keys(data.versions[version].dependencies).length > 0) { dependencies = Object.keys(data.versions[version].dependencies).join(', '); } const name: string = data.name || 'None'; diff --git a/src/commands/whois.ts b/src/commands/whois.ts index 1ed7329..f578bd3 100644 --- a/src/commands/whois.ts +++ b/src/commands/whois.ts @@ -41,22 +41,20 @@ export default class Whois extends Command { embed.setThumbnail(member.avatarURL); const ackResolve = this.resolveStaffInformation(member.id); let description = ''; - if (ackResolve) { - if (ackResolve?.title) { - description += `${emotes.titleAndDepartment} __**${ackResolve.title}**__\n\n`; - } - if (ackResolve?.emailAddress) { - description += `${emotes.email} ${ackResolve.emailAddress}\n`; - } - if (ackResolve?.gitlab) { - description += `${emotes.gitlab} ${ackResolve.gitlab}\n`; - } - if (ackResolve?.github) { - description += `${emotes.github} ${ackResolve.github}\n`; - } - if (ackResolve?.bio) { - description += `${emotes.bio} *${ackResolve.bio}*\n`; - } + if (ackResolve?.title) { + description += `${emotes.titleAndDepartment} __**${ackResolve.title}**__\n\n`; + } + if (ackResolve?.emailAddress) { + description += `${emotes.email} ${ackResolve.emailAddress}\n`; + } + if (ackResolve?.gitlab) { + description += `${emotes.gitlab} ${ackResolve.gitlab}\n`; + } + if (ackResolve?.github) { + description += `${emotes.github} ${ackResolve.github}\n`; + } + if (ackResolve?.bio) { + description += `${emotes.bio} *${ackResolve.bio}*\n`; } description += `\n<@${member.id}>`; embed.setDescription(description); @@ -95,7 +93,7 @@ export default class Whois extends Command { if ((bit | 1073741824) === bit) permissions.push('Manage Emojis'); if ((bit | 4) === bit) permissions.push('Ban Members'); if ((bit | 2) === bit) permissions.push('Kick Members'); - const account = await this.client.db.member.findOne({ userID: member.id }); + const account = await this.client.db.Member.findOne({ userID: member.id }); if (account?.additional?.langs.length > 0) { const langs: string[] = []; for (const lang of account.additional.langs.sort((a, b) => a.localeCompare(b))) { diff --git a/src/configs/acknowledgements.json b/src/configs/acknowledgements.json index 79a6b0e..1014e53 100644 --- a/src/configs/acknowledgements.json +++ b/src/configs/acknowledgements.json @@ -37,7 +37,7 @@ { "name": "Midori", "id": "109122112643440640", - "title": "Board of Directors", + "title": "Board of Directors [ON LEAVE]", "emailAddress": "midori@staff.libraryofcode.org" }, { @@ -101,13 +101,6 @@ "github": "https://github.com/Hector6704", "bio": "Hi there, I'm the developer of Delta, the Discord bot. I'm a free-time French JavaScript developer. I hope you'll enjoy LOC!" }, - { - "name": "Realitus", - "id": "156450671338586112", - "title": "Associate", - "github": "https://github.com/Realitus", - "bio": "A hobbyist software developer with some rather strange ideas, and even stranger implementations." - }, { "name": "KhaaZ", "id": "179908288337412096", @@ -133,6 +126,13 @@ "github": "https://github.com/gavintjhxx", "bio": "Wake up. Eat. Code. Sleep. Loop()" }, + { + "name": "PlayerVMachine", + "id": "273999507174195203", + "title": "Instructor & Associate", + "emailAddress": "nicolas@staff.libraryofcode.org", + "bio": "I write C++ to pay off my student loans" + }, { "name": "Null", "id": "323673862971588609", diff --git a/src/events/messageCreate.ts b/src/events/CommandHandler.ts similarity index 90% rename from src/events/messageCreate.ts rename to src/events/CommandHandler.ts index 7898561..a7d7733 100644 --- a/src/events/messageCreate.ts +++ b/src/events/CommandHandler.ts @@ -1,12 +1,13 @@ /* eslint-disable no-useless-return */ import { Message, TextChannel, NewsChannel } from 'eris'; -import { Client } from '../class'; +import { Client, Event } from '../class'; -export default class { +export default class CommandHandler extends Event { public client: Client; constructor(client: Client) { - this.client = client; + super(client); + this.event = 'messageCreate'; } public async run(message: Message) { diff --git a/src/events/index.ts b/src/events/index.ts new file mode 100644 index 0000000..b57978e --- /dev/null +++ b/src/events/index.ts @@ -0,0 +1,2 @@ +export { default as CommandHandler } from './CommandHandler'; +export { default as ready } from './ready'; diff --git a/src/events/ready.ts b/src/events/ready.ts index 908e189..dd7f72b 100644 --- a/src/events/ready.ts +++ b/src/events/ready.ts @@ -1,10 +1,11 @@ -import { Client } from '../class'; +import { Client, Event } from '../class'; -export default class { +export default class Ready extends Event { public client: Client; constructor(client: Client) { - this.client = client; + super(client); + this.event = 'ready'; } public async run() { diff --git a/src/intervals/autoRelease.ts b/src/intervals/autoRelease.ts index f54c874..3d48e6c 100644 --- a/src/intervals/autoRelease.ts +++ b/src/intervals/autoRelease.ts @@ -5,7 +5,7 @@ let interval: NodeJS.Timeout; export default function checkLock(client: Client): NodeJS.Timeout { interval = setInterval(async () => { try { - const moderations = await client.db.moderation.find(); + const moderations = await client.db.Moderation.find(); moderations.forEach(async (moderation) => { if (!moderation.expiration) return; if (moderation.expiration.processed) return; diff --git a/src/intervals/fetchMembers.ts b/src/intervals/fetchMembers.ts index 82a8f9d..fdfd1f1 100644 --- a/src/intervals/fetchMembers.ts +++ b/src/intervals/fetchMembers.ts @@ -3,7 +3,7 @@ import { Client } from '../class'; let interval: NodeJS.Timeout; export default async function fetchMembers(client: Client): Promise { - await client.guilds.get(client.config.guildID).fetchAllMembers(); + await client.guilds.get(client.config.guildID)?.fetchAllMembers(); interval = setInterval(async () => { await client.guilds.get(client.config.guildID).fetchAllMembers(); }, 1800000); diff --git a/src/main.ts b/src/main.ts index 5213da2..c3682ad 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,6 +4,8 @@ import { parse } from 'yaml'; import { promises as fs } from 'fs'; import { Client } from './class'; +import * as eventFiles from './events'; +import * as commandFiles from './commands'; async function main(): Promise { const read = await fs.readFile('../config.yaml', 'utf8'); @@ -13,8 +15,8 @@ async function main(): Promise { await client.loadDatabase(); client.loadPlugins(); await client.loadIntervals(); - await client.loadEvents(); - await client.loadCommands(); + await client.loadEvents(eventFiles); + await client.loadCommands(commandFiles); client.connect(); } diff --git a/src/models/Redirect.ts b/src/models/Redirect.ts index 18b8dd1..5376451 100644 --- a/src/models/Redirect.ts +++ b/src/models/Redirect.ts @@ -5,6 +5,11 @@ export interface RedirectInterface extends Document { to: string, } +export interface RedirectRaw { + key: string, + to: string, +} + const Redirect: Schema = new Schema({ key: String, to: String, diff --git a/src/models/index.ts b/src/models/index.ts index 77bf216..1351809 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -1,3 +1,3 @@ export { default as Member, MemberInterface } from './Member'; export { default as Moderation, ModerationInterface } from './Moderation'; -export { default as Redirect, RedirectInterface } from './Redirect'; +export { default as Redirect, RedirectInterface, RedirectRaw } from './Redirect';