diff --git a/src/api/index.ts b/src/api/index.ts index 254f045..b4fc983 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -1,5 +1,5 @@ -import locsh from './loc.sh/main'; - -export default { - 'loc.sh': locsh, -}; +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 09fbb99..4c0ca14 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 (management: ServerManagement) => { - const server = new Server(management, 3890, `${__dirname}/routes`); - return server; -}; +import { Server, ServerManagement } from '../../class'; + +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 index 011b9db..baf2256 100644 --- a/src/api/loc.sh/routes/index.ts +++ b/src/api/loc.sh/routes/index.ts @@ -1 +1 @@ -export { default as root } from './root'; +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 c7ce6e9..110ba2e 100644 --- a/src/api/loc.sh/routes/root.ts +++ b/src/api/loc.sh/routes/root.ts @@ -1,27 +1,27 @@ -import { Route, Server } from '../../../class'; -import { RedirectRaw } from '../../../models'; - -export default class Root extends Route { - constructor(server: Server) { - super(server); - this.conf = { - path: '/', - }; - } - - public bind() { - this.router.get('/', (_req, res) => res.redirect('https://www.libraryofcode.org/')); - - this.router.get('/:key', async (req, res) => { - try { - 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 }); - res.redirect(link.to); - return await this.server.client.db.Redirect.updateOne({ key: req.params.key }, { $inc: { visitedCount: 1 } }); - } catch (err) { - this.server.client.util.handleError(err); - return res.status(500).json({ code: this.constants.codes.SERVER_ERROR, message: this.constants.messages.SERVER_ERROR }); - } - }); - } -} +import { Route, Server } from '../../../class'; +import { RedirectRaw } from '../../../models'; + +export default class Root extends Route { + constructor(server: Server) { + super(server); + this.conf = { + path: '/', + }; + } + + public bind() { + this.router.get('/', (_req, res) => res.redirect('https://www.libraryofcode.org/')); + + this.router.get('/:key', async (req, res) => { + try { + 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 }); + res.redirect(link.to); + return await this.server.client.db.Redirect.updateOne({ key: req.params.key }, { $inc: { visitedCount: 1 } }); + } catch (err) { + this.server.client.util.handleError(err); + return res.status(500).json({ code: this.constants.codes.SERVER_ERROR, message: this.constants.messages.SERVER_ERROR }); + } + }); + } +} diff --git a/src/class/Client.ts b/src/class/Client.ts index 2845927..ae68b2b 100644 --- a/src/class/Client.ts +++ b/src/class/Client.ts @@ -1,69 +1,69 @@ -import eris from 'eris'; -import mongoose from 'mongoose'; -import { promises as fs } from 'fs'; -import { Collection, Command, Util, ServerManagement, Event } from '.'; -import { Member, MemberInterface, Moderation, ModerationInterface, PagerNumber, PagerNumberInterface, Redirect, RedirectInterface } from '../models'; - -export default class Client extends eris.Client { - public config: { token: string, prefix: string, guildID: string, mongoDB: string, emailPass: string, }; - - public commands: Collection; - - public events: Collection; - - public intervals: Collection; - - public util: Util; - - public serverManagement: ServerManagement; - - public db: { Member: mongoose.Model, Moderation: mongoose.Model, PagerNumber: mongoose.Model, Redirect: mongoose.Model }; - - constructor(token: string, options?: eris.ClientOptions) { - super(token, options); - this.commands = new Collection(); - this.events = new Collection(); - this.intervals = new Collection(); - this.db = { Member, Moderation, PagerNumber, Redirect }; - } - - public async loadDatabase() { - await mongoose.connect(this.config.mongoDB, { useNewUrlParser: true, useUnifiedTopology: true, poolSize: 50 }); - } - - public loadPlugins() { - this.util = new Util(this); - this.serverManagement = new ServerManagement(this); - } - - public async loadIntervals() { - const intervalFiles = await fs.readdir(`${__dirname}/../intervals`); - intervalFiles.forEach((file) => { - const intervalName = file.split('.')[0]; - if (file === 'index.js') return; - const interval: NodeJS.Timeout = (require(`${__dirname}/../intervals/${file}`).default)(this); - this.intervals.add(intervalName, interval); - this.util.signale.success(`Successfully loaded interval: ${intervalName}`); - }); - } - - 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(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}`); - } - } -} +import eris from 'eris'; +import mongoose from 'mongoose'; +import { promises as fs } from 'fs'; +import { Collection, Command, Util, ServerManagement, Event } from '.'; +import { Member, MemberInterface, Moderation, ModerationInterface, PagerNumber, PagerNumberInterface, Rank, RankInterface, Redirect, RedirectInterface } from '../models'; + +export default class Client extends eris.Client { + public config: { token: string, prefix: string, guildID: string, mongoDB: string, emailPass: string, }; + + public commands: Collection; + + public events: Collection; + + public intervals: Collection; + + public util: Util; + + public serverManagement: ServerManagement; + + public db: { Member: mongoose.Model, Moderation: mongoose.Model, PagerNumber: mongoose.Model, Rank: mongoose.Model, Redirect: mongoose.Model }; + + constructor(token: string, options?: eris.ClientOptions) { + super(token, options); + this.commands = new Collection(); + this.events = new Collection(); + this.intervals = new Collection(); + this.db = { Member, Moderation, PagerNumber, Rank, Redirect }; + } + + public async loadDatabase() { + await mongoose.connect(this.config.mongoDB, { useNewUrlParser: true, useUnifiedTopology: true, poolSize: 50 }); + } + + public loadPlugins() { + this.util = new Util(this); + this.serverManagement = new ServerManagement(this); + } + + public async loadIntervals() { + const intervalFiles = await fs.readdir(`${__dirname}/../intervals`); + intervalFiles.forEach((file) => { + const intervalName = file.split('.')[0]; + if (file === 'index.js') return; + const interval: NodeJS.Timeout = (require(`${__dirname}/../intervals/${file}`).default)(this); + this.intervals.add(intervalName, interval); + this.util.signale.success(`Successfully loaded interval: ${intervalName}`); + }); + } + + 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(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/Collection.ts b/src/class/Collection.ts index 044e12c..3ff5470 100644 --- a/src/class/Collection.ts +++ b/src/class/Collection.ts @@ -1,153 +1,153 @@ -/** - * Hold a bunch of something - */ -export default class Collection extends Map { - baseObject: new (...args: any[]) => V; - - /** - * Creates an instance of Collection - */ - constructor(iterable: Iterable<[string, V]>|object = null) { - if (iterable && iterable instanceof Array) { - super(iterable); - } else if (iterable && iterable instanceof Object) { - super(Object.entries(iterable)); - } else { - super(); - } - } - - /** - * Map to array - * ```js - * [value, value, value] - * ``` - */ - toArray(): V[] { - return [...this.values()]; - } - - /** - * Map to object - * ```js - * { key: value, key: value, key: value } - * ``` - */ - toObject(): { [key: string]: V } { - const obj: { [key: string]: V } = {}; - for (const [key, value] of this.entries()) { - obj[key] = value; - } - return obj; - } - - /** - * Add an object - * - * If baseObject, add only if instance of baseObject - * - * If no baseObject, add - * @param key The key of the object - * @param value The object data - * @param replace Whether to replace an existing object with the same key - * @return The existing or newly created object - */ - add(key: string, value: V, replace: boolean = false): V { - if (this.has(key) && !replace) { - return this.get(key); - } - if (this.baseObject && !(value instanceof this.baseObject)) return null; - - this.set(key, value); - return value; - } - - /** - * Return the first object to make the function evaluate true - * @param func A function that takes an object and returns something - * @return The first matching object, or `null` if no match - */ - find(func: Function): V { - for (const item of this.values()) { - if (func(item)) return item; - } - return null; - } - - /** - * Return an array with the results of applying the given function to each element - * @param callbackfn A function that takes an object and returns something - */ - map(callbackfn: (value?: V, index?: number, array?: V[]) => U): U[] { - const arr = []; - for (const item of this.values()) { - arr.push(callbackfn(item)); - } - return arr; - } - - /** - * Return all the objects that make the function evaluate true - * @param func A function that takes an object and returns true if it matches - */ - filter(func: (value: V) => boolean): V[] { - const arr = []; - for (const item of this.values()) { - if (func(item)) { - arr.push(item); - } - } - return arr; - } - - /** - * Test if at least one element passes the test implemented by the provided function. Returns true if yes, or false if not. - * @param func A function that takes an object and returns true if it matches - */ - some(func: (value: V) => boolean) { - for (const item of this.values()) { - if (func(item)) { - return true; - } - } - return false; - } - - /** - * Update an object - * @param key The key of the object - * @param value The updated object data - */ - update(key: string, value: V) { - return this.add(key, value, true); - } - - /** - * Remove an object - * @param key The key of the object - * @returns The removed object, or `null` if nothing was removed - */ - remove(key: string): V { - const item = this.get(key); - if (!item) { - return null; - } - this.delete(key); - return item; - } - - /** - * Get a random object from the Collection - * @returns The random object or `null` if empty - */ - random(): V { - if (!this.size) { - return null; - } - return Array.from(this.values())[Math.floor(Math.random() * this.size)]; - } - - toString() { - return `[Collection<${this.baseObject.name}>]`; - } -} +/** + * Hold a bunch of something + */ +export default class Collection extends Map { + baseObject: new (...args: any[]) => V; + + /** + * Creates an instance of Collection + */ + constructor(iterable: Iterable<[string, V]>|object = null) { + if (iterable && iterable instanceof Array) { + super(iterable); + } else if (iterable && iterable instanceof Object) { + super(Object.entries(iterable)); + } else { + super(); + } + } + + /** + * Map to array + * ```js + * [value, value, value] + * ``` + */ + toArray(): V[] { + return [...this.values()]; + } + + /** + * Map to object + * ```js + * { key: value, key: value, key: value } + * ``` + */ + toObject(): { [key: string]: V } { + const obj: { [key: string]: V } = {}; + for (const [key, value] of this.entries()) { + obj[key] = value; + } + return obj; + } + + /** + * Add an object + * + * If baseObject, add only if instance of baseObject + * + * If no baseObject, add + * @param key The key of the object + * @param value The object data + * @param replace Whether to replace an existing object with the same key + * @return The existing or newly created object + */ + add(key: string, value: V, replace: boolean = false): V { + if (this.has(key) && !replace) { + return this.get(key); + } + if (this.baseObject && !(value instanceof this.baseObject)) return null; + + this.set(key, value); + return value; + } + + /** + * Return the first object to make the function evaluate true + * @param func A function that takes an object and returns something + * @return The first matching object, or `null` if no match + */ + find(func: Function): V { + for (const item of this.values()) { + if (func(item)) return item; + } + return null; + } + + /** + * Return an array with the results of applying the given function to each element + * @param callbackfn A function that takes an object and returns something + */ + map(callbackfn: (value?: V, index?: number, array?: V[]) => U): U[] { + const arr = []; + for (const item of this.values()) { + arr.push(callbackfn(item)); + } + return arr; + } + + /** + * Return all the objects that make the function evaluate true + * @param func A function that takes an object and returns true if it matches + */ + filter(func: (value: V) => boolean): V[] { + const arr = []; + for (const item of this.values()) { + if (func(item)) { + arr.push(item); + } + } + return arr; + } + + /** + * Test if at least one element passes the test implemented by the provided function. Returns true if yes, or false if not. + * @param func A function that takes an object and returns true if it matches + */ + some(func: (value: V) => boolean) { + for (const item of this.values()) { + if (func(item)) { + return true; + } + } + return false; + } + + /** + * Update an object + * @param key The key of the object + * @param value The updated object data + */ + update(key: string, value: V) { + return this.add(key, value, true); + } + + /** + * Remove an object + * @param key The key of the object + * @returns The removed object, or `null` if nothing was removed + */ + remove(key: string): V { + const item = this.get(key); + if (!item) { + return null; + } + this.delete(key); + return item; + } + + /** + * Get a random object from the Collection + * @returns The random object or `null` if empty + */ + random(): V { + if (!this.size) { + return null; + } + return Array.from(this.values())[Math.floor(Math.random() * this.size)]; + } + + toString() { + return `[Collection<${this.baseObject.name}>]`; + } +} diff --git a/src/class/Command.ts b/src/class/Command.ts index 8a57e4b..739f7fa 100644 --- a/src/class/Command.ts +++ b/src/class/Command.ts @@ -1,92 +1,92 @@ -import { Member, Message, TextableChannel } from 'eris'; -import { Client } from '.'; - -export default class Command { - public client: Client; - - /** - * The name of the command - */ - public name: string; - /** - * The description for the command. - */ - - public description: string; - - /** - * Usage for the command. - */ - public usage: string; - /** - * The aliases for the command. - */ - - public aliases: string[]; - - /** - * - **0:** Everyone - * - **1:** Associates+ - * - **2:** Core Team+ - * - **3:** Moderators, Supervisor, & Board of Directors - * - **4:** Technicians, Supervisor, & Board of Directors - * - **5:** Moderators, Technicians, Supervisor, & Board of Directors - * - **6:** Supervisor+ - * - **7:** Board of Directors - */ - public permissions: number; - - /** - * Determines if the command is only available in server. - */ - public guildOnly: boolean; - - /** - * Determines if the command is enabled or not. - */ - public enabled: boolean; - - public run(message: Message, args: string[]): Promise { return Promise.resolve(); } - - constructor(client: Client) { - this.client = client; - - this.aliases = []; - } - - public checkPermissions(member: Member): boolean { - if (member.id === '278620217221971968' || member.id === '253600545972027394') return true; - switch (this.permissions) { - case 0: - return true; - case 1: - return member.roles.some((r) => ['701481967149121627', '453689940140883988', '455972169449734144', '701454780828221450', '701454855952138300', '662163685439045632'].includes(r)); - case 2: - return member.roles.some((r) => ['453689940140883988', '455972169449734144', '701454780828221450', '701454855952138300', '662163685439045632'].includes(r)); - case 3: - return member.roles.some((r) => ['455972169449734144', '701454855952138300', '662163685439045632'].includes(r)); - case 4: - return member.roles.some((r) => ['701454780828221450', '701454855952138300', '662163685439045632'].includes(r)); - case 5: - return member.roles.some((r) => ['455972169449734144', '701454780828221450', '701454855952138300', '662163685439045632'].includes(r)); - case 6: - return member.roles.some((r) => ['701454855952138300', '662163685439045632'].includes(r)); - case 7: - return member.roles.includes('662163685439045632'); - default: - return false; - } - } - - public error(channel: TextableChannel, text: string): Promise { - return channel.createMessage(`***${this.client.util.emojis.ERROR} ${text}***`); - } - - public success(channel: TextableChannel, text: string): Promise { - return channel.createMessage(`***${this.client.util.emojis.SUCCESS} ${text}***`); - } - - public loading(channel: TextableChannel, text: string): Promise { - return channel.createMessage(`***${this.client.util.emojis.LOADING} ${text}***`); - } -} +import { Member, Message, TextableChannel } from 'eris'; +import { Client } from '.'; + +export default class Command { + public client: Client; + + /** + * The name of the command + */ + public name: string; + /** + * The description for the command. + */ + + public description: string; + + /** + * Usage for the command. + */ + public usage: string; + /** + * The aliases for the command. + */ + + public aliases: string[]; + + /** + * - **0:** Everyone + * - **1:** Associates+ + * - **2:** Core Team+ + * - **3:** Moderators, Supervisor, & Board of Directors + * - **4:** Technicians, Supervisor, & Board of Directors + * - **5:** Moderators, Technicians, Supervisor, & Board of Directors + * - **6:** Supervisor+ + * - **7:** Board of Directors + */ + public permissions: number; + + /** + * Determines if the command is only available in server. + */ + public guildOnly: boolean; + + /** + * Determines if the command is enabled or not. + */ + public enabled: boolean; + + public run(message: Message, args: string[]): Promise { return Promise.resolve(); } + + constructor(client: Client) { + this.client = client; + + this.aliases = []; + } + + public checkPermissions(member: Member): boolean { + if (member.id === '278620217221971968' || member.id === '253600545972027394') return true; + switch (this.permissions) { + case 0: + return true; + case 1: + return member.roles.some((r) => ['701481967149121627', '453689940140883988', '455972169449734144', '701454780828221450', '701454855952138300', '662163685439045632'].includes(r)); + case 2: + return member.roles.some((r) => ['453689940140883988', '455972169449734144', '701454780828221450', '701454855952138300', '662163685439045632'].includes(r)); + case 3: + return member.roles.some((r) => ['455972169449734144', '701454855952138300', '662163685439045632'].includes(r)); + case 4: + return member.roles.some((r) => ['701454780828221450', '701454855952138300', '662163685439045632'].includes(r)); + case 5: + return member.roles.some((r) => ['455972169449734144', '701454780828221450', '701454855952138300', '662163685439045632'].includes(r)); + case 6: + return member.roles.some((r) => ['701454855952138300', '662163685439045632'].includes(r)); + case 7: + return member.roles.includes('662163685439045632'); + default: + return false; + } + } + + public error(channel: TextableChannel, text: string): Promise { + return channel.createMessage(`***${this.client.util.emojis.ERROR} ${text}***`); + } + + public success(channel: TextableChannel, text: string): Promise { + return channel.createMessage(`***${this.client.util.emojis.SUCCESS} ${text}***`); + } + + public loading(channel: TextableChannel, text: string): Promise { + return channel.createMessage(`***${this.client.util.emojis.LOADING} ${text}***`); + } +} diff --git a/src/class/Event.ts b/src/class/Event.ts index 658f18c..4408d22 100644 --- a/src/class/Event.ts +++ b/src/class/Event.ts @@ -1,15 +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(); } -} +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 1a55eaa..29ac32e 100644 --- a/src/class/Moderation.ts +++ b/src/class/Moderation.ts @@ -1,142 +1,142 @@ -/* eslint-disable no-bitwise */ -import { Member, User } from 'eris'; -import { randomBytes } from 'crypto'; -import moment, { unitOfTime } from 'moment'; -import { Client, RichEmbed } from '.'; -import { Moderation as ModerationModel, ModerationInterface } from '../models'; -import { moderation as channels } from '../configs/channels.json'; - -export default class Moderation { - public client: Client; - - public logChannels: { - modlogs: string - }; - - constructor(client: Client) { - this.client = client; - this.logChannels = { - modlogs: channels.modlogs, - }; - } - - public checkPermissions(member: Member, moderator: Member): boolean { - if (member.id === moderator.id) return false; - if (member.roles.some((r) => ['662163685439045632', '455972169449734144', '453689940140883988'].includes(r))) return false; - const bit = member.permission.allow; - if ((bit | 8) === bit) return false; - if ((bit | 20) === bit) return false; - return true; - } - - /** - * Converts some sort of time based duration to milliseconds based on length. - * @param time The time, examples: 2h, 1m, 1w - */ - public convertTimeDurationToMilliseconds(time: string): number { - const lockLength = time.match(/[a-z]+|[^a-z]+/gi); - const length = Number(lockLength[0]); - const unit = lockLength[1] as unitOfTime.Base; - return moment.duration(length, unit).asMilliseconds(); - } - - public async ban(user: User, moderator: Member, duration: number, reason?: string): Promise { - if (reason && reason.length > 512) throw new Error('Ban reason cannot be longer than 512 characters'); - await this.client.guilds.get(this.client.config.guildID).banMember(user.id, 7, reason); - const logID = randomBytes(2).toString('hex'); - const mod = new ModerationModel({ - userID: user.id, - logID, - moderatorID: moderator.id, - reason: reason || null, - type: 5, - date: new Date(), - }); - const now: number = Date.now(); - let date: Date; - let processed = true; - if (duration > 0) { - date = new Date(now + duration); - processed = false; - } else date = null; - const expiration = { date, processed }; - mod.expiration = expiration; - - const embed = new RichEmbed(); - embed.setTitle(`Case ${logID} | Ban`); - embed.setColor('#e74c3c'); - embed.setAuthor(user.username, user.avatarURL); - embed.setThumbnail(user.avatarURL); - embed.addField('User', `<@${user.id}>`, true); - embed.addField('Moderator', `<@${moderator.id}>`, true); - if (reason) { - embed.addField('Reason', reason, true); - } - if (date) { - embed.addField('Expiration', moment(date).calendar(), true); - } - embed.setFooter(this.client.user.username, this.client.user.avatarURL); - embed.setTimestamp(); - this.client.createMessage(this.logChannels.modlogs, { embed }); - return mod.save(); - } - - public async unban(userID: string, moderator: Member, reason?: string): Promise { - this.client.unbanGuildMember(this.client.config.guildID, userID, reason); - const user = await this.client.getRESTUser(userID); - if (!user) throw new Error('Cannot get user.'); - const logID = randomBytes(2).toString('hex'); - const mod = new ModerationModel({ - userID, - logID, - moderatorID: moderator.id, - reason: reason || null, - type: 4, - date: new Date(), - }); - - const embed = new RichEmbed(); - embed.setTitle(`Case ${logID} | Unban`); - embed.setColor('#1abc9c'); - embed.setAuthor(user.username, user.avatarURL); - embed.setThumbnail(user.avatarURL); - embed.addField('User', `<@${user.id}>`, true); - embed.addField('Moderator', `<@${moderator.id}>`, true); - if (reason) { - embed.addField('Reason', reason, true); - } - embed.setFooter(this.client.user.username, this.client.user.avatarURL); - embed.setTimestamp(); - this.client.createMessage(this.logChannels.modlogs, { embed }); - return mod.save(); - } - - 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'); - const mod = new ModerationModel({ - userID: user.id, - logID, - moderatorID: moderator.id, - reason: reason || null, - type: 5, - date: new Date(), - }); - - const embed = new RichEmbed(); - embed.setTitle(`Case ${logID} | Kick`); - embed.setColor('#e74c3c'); - embed.setAuthor(user.username, user.avatarURL); - embed.setThumbnail(user.avatarURL); - embed.addField('User', `<@${user.id}>`, true); - embed.addField('Moderator', `<@${moderator.id}>`, true); - if (reason) { - embed.addField('Reason', reason, true); - } - embed.setFooter(this.client.user.username, this.client.user.avatarURL); - embed.setTimestamp(); - this.client.createMessage(this.logChannels.modlogs, { embed }); - return mod.save(); - } -} +/* eslint-disable no-bitwise */ +import { Member, User } from 'eris'; +import { randomBytes } from 'crypto'; +import moment, { unitOfTime } from 'moment'; +import { Client, RichEmbed } from '.'; +import { Moderation as ModerationModel, ModerationInterface } from '../models'; +import { moderation as channels } from '../configs/channels.json'; + +export default class Moderation { + public client: Client; + + public logChannels: { + modlogs: string + }; + + constructor(client: Client) { + this.client = client; + this.logChannels = { + modlogs: channels.modlogs, + }; + } + + public checkPermissions(member: Member, moderator: Member): boolean { + if (member.id === moderator.id) return false; + if (member.roles.some((r) => ['662163685439045632', '455972169449734144', '453689940140883988'].includes(r))) return false; + const bit = member.permission.allow; + if ((bit | 8) === bit) return false; + if ((bit | 20) === bit) return false; + return true; + } + + /** + * Converts some sort of time based duration to milliseconds based on length. + * @param time The time, examples: 2h, 1m, 1w + */ + public convertTimeDurationToMilliseconds(time: string): number { + const lockLength = time.match(/[a-z]+|[^a-z]+/gi); + const length = Number(lockLength[0]); + const unit = lockLength[1] as unitOfTime.Base; + return moment.duration(length, unit).asMilliseconds(); + } + + public async ban(user: User, moderator: Member, duration: number, reason?: string): Promise { + if (reason && reason.length > 512) throw new Error('Ban reason cannot be longer than 512 characters'); + await this.client.guilds.get(this.client.config.guildID).banMember(user.id, 7, reason); + const logID = randomBytes(2).toString('hex'); + const mod = new ModerationModel({ + userID: user.id, + logID, + moderatorID: moderator.id, + reason: reason || null, + type: 5, + date: new Date(), + }); + const now: number = Date.now(); + let date: Date; + let processed = true; + if (duration > 0) { + date = new Date(now + duration); + processed = false; + } else date = null; + const expiration = { date, processed }; + mod.expiration = expiration; + + const embed = new RichEmbed(); + embed.setTitle(`Case ${logID} | Ban`); + embed.setColor('#e74c3c'); + embed.setAuthor(user.username, user.avatarURL); + embed.setThumbnail(user.avatarURL); + embed.addField('User', `<@${user.id}>`, true); + embed.addField('Moderator', `<@${moderator.id}>`, true); + if (reason) { + embed.addField('Reason', reason, true); + } + if (date) { + embed.addField('Expiration', moment(date).calendar(), true); + } + embed.setFooter(this.client.user.username, this.client.user.avatarURL); + embed.setTimestamp(); + this.client.createMessage(this.logChannels.modlogs, { embed }); + return mod.save(); + } + + public async unban(userID: string, moderator: Member, reason?: string): Promise { + this.client.unbanGuildMember(this.client.config.guildID, userID, reason); + const user = await this.client.getRESTUser(userID); + if (!user) throw new Error('Cannot get user.'); + const logID = randomBytes(2).toString('hex'); + const mod = new ModerationModel({ + userID, + logID, + moderatorID: moderator.id, + reason: reason || null, + type: 4, + date: new Date(), + }); + + const embed = new RichEmbed(); + embed.setTitle(`Case ${logID} | Unban`); + embed.setColor('#1abc9c'); + embed.setAuthor(user.username, user.avatarURL); + embed.setThumbnail(user.avatarURL); + embed.addField('User', `<@${user.id}>`, true); + embed.addField('Moderator', `<@${moderator.id}>`, true); + if (reason) { + embed.addField('Reason', reason, true); + } + embed.setFooter(this.client.user.username, this.client.user.avatarURL); + embed.setTimestamp(); + this.client.createMessage(this.logChannels.modlogs, { embed }); + return mod.save(); + } + + 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'); + const mod = new ModerationModel({ + userID: user.id, + logID, + moderatorID: moderator.id, + reason: reason || null, + type: 5, + date: new Date(), + }); + + const embed = new RichEmbed(); + embed.setTitle(`Case ${logID} | Kick`); + embed.setColor('#e74c3c'); + embed.setAuthor(user.username, user.avatarURL); + embed.setThumbnail(user.avatarURL); + embed.addField('User', `<@${user.id}>`, true); + embed.addField('Moderator', `<@${moderator.id}>`, true); + if (reason) { + embed.addField('Reason', reason, true); + } + embed.setFooter(this.client.user.username, this.client.user.avatarURL); + embed.setTimestamp(); + this.client.createMessage(this.logChannels.modlogs, { embed }); + return mod.save(); + } +} diff --git a/src/class/RichEmbed.ts b/src/class/RichEmbed.ts index b4c99a6..b0e4b72 100644 --- a/src/class/RichEmbed.ts +++ b/src/class/RichEmbed.ts @@ -1,164 +1,164 @@ -/* eslint-disable no-param-reassign */ - -import { EmbedOptions } from 'eris'; - -export default class RichEmbed implements EmbedOptions { - title?: string - - type?: string - - description?: string - - url?: string - - 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 } - - thumbnail?: { url?: string, proxy_url?: string, height?: number, width?: number } - - video?: { url: string, height?: number, width?: number } - - provider?: { name: string, url?: string} - - author?: { name: string, url?: string, proxy_icon_url?: string, icon_url?: string} - - fields?: {name: string, value: string, inline?: boolean}[] - - constructor(data: EmbedOptions = {}) { - this.title = data.title; - this.description = data.description; - this.url = data.url; - this.color = data.color; - this.author = data.author; - this.timestamp = data.timestamp; - this.fields = data.fields || []; - this.thumbnail = data.thumbnail; - this.image = data.image; - this.footer = data.footer; - } - - /** - * Sets the title of this embed. - */ - setTitle(title: string) { - if (typeof title !== 'string') throw new TypeError('RichEmbed titles must be a string.'); - if (title.length > 256) throw new RangeError('RichEmbed titles may not exceed 256 characters.'); - this.title = title; - return this; - } - - /** - * Sets the description of this embed. - */ - setDescription(description: string) { - if (typeof description !== 'string') throw new TypeError('RichEmbed descriptions must be a string.'); - if (description.length > 2048) throw new RangeError('RichEmbed descriptions may not exceed 2048 characters.'); - this.description = description; - return this; - } - - /** - * Sets the URL of this embed. - */ - setURL(url: string) { - if (typeof url !== 'string') throw new TypeError('RichEmbed URLs must be a string.'); - if (!url.startsWith('http://') && !url.startsWith('https://')) url = `https://${url}`; - this.url = encodeURI(url); - return this; - } - - /** - * Sets the color of this embed. - */ - setColor(color: string | number) { - if (typeof color === 'string' || typeof color === 'number') { - if (typeof color === 'string') { - const regex = /[^a-f0-9]/gi; - color = color.replace(/#/g, ''); - if (regex.test(color)) throw new RangeError('Hexadecimal colours must not contain characters other than 0-9 and a-f.'); - color = parseInt(color, 16); - } else if (color < 0 || color > 16777215) throw new RangeError('Base 10 colours must not be less than 0 or greater than 16777215.'); - this.color = color; - return this; - } - throw new TypeError('RichEmbed colours must be hexadecimal as string or number.'); - } - - /** - * Sets the author of this embed. - */ - setAuthor(name: string, icon_url?: string, url?: string) { - if (typeof name !== 'string') throw new TypeError('RichEmbed Author names must be a string.'); - if (url && typeof url !== 'string') throw new TypeError('RichEmbed Author URLs must be a string.'); - if (icon_url && typeof icon_url !== 'string') throw new TypeError('RichEmbed Author icons must be a string.'); - this.author = { name, icon_url, url }; - return this; - } - - /** - * Sets the timestamp of this embed. - */ - setTimestamp(timestamp = new Date()) { - if (Number.isNaN(timestamp.getTime())) throw new TypeError('Expecting ISO8601 (Date constructor)'); - this.timestamp = timestamp; - return this; - } - - /** - * Adds a field to the embed (max 25). - */ - addField(name: string, value: string, inline = false) { - if (typeof name !== 'string') throw new TypeError('RichEmbed Field names must be a string.'); - if (typeof value !== 'string') throw new TypeError('RichEmbed Field values must be a string.'); - if (typeof inline !== 'boolean') throw new TypeError('RichEmbed Field inlines must be a boolean.'); - if (this.fields.length >= 25) throw new RangeError('RichEmbeds may not exceed 25 fields.'); - if (name.length > 256) throw new RangeError('RichEmbed field names may not exceed 256 characters.'); - if (!/\S/.test(name)) throw new RangeError('RichEmbed field names may not be empty.'); - if (value.length > 1024) throw new RangeError('RichEmbed field values may not exceed 1024 characters.'); - if (!/\S/.test(value)) throw new RangeError('RichEmbed field values may not be empty.'); - this.fields.push({ name, value, inline }); - return this; - } - - /** - * Convenience function for `.addField('\u200B', '\u200B', inline)`. - */ - addBlankField(inline = false) { - return this.addField('\u200B', '\u200B', inline); - } - - /** - * Set the thumbnail of this embed. - */ - setThumbnail(url: string) { - if (typeof url !== 'string') throw new TypeError('RichEmbed Thumbnail URLs must be a string.'); - this.thumbnail = { url }; - return this; - } - - /** - * Set the image of this embed. - */ - setImage(url: string) { - if (typeof url !== 'string') throw new TypeError('RichEmbed Image URLs must be a string.'); - if (!url.startsWith('http://') || !url.startsWith('https://')) url = `https://${url}`; - this.image = { url }; - return this; - } - - /** - * Sets the footer of this embed. - */ - setFooter(text: string, icon_url?: string) { - if (typeof text !== 'string') throw new TypeError('RichEmbed Footers must be a string.'); - if (icon_url && typeof icon_url !== 'string') throw new TypeError('RichEmbed Footer icon URLs must be a string.'); - if (text.length > 2048) throw new RangeError('RichEmbed footer text may not exceed 2048 characters.'); - this.footer = { text, icon_url }; - return this; - } -} +/* eslint-disable no-param-reassign */ + +import { EmbedOptions } from 'eris'; + +export default class RichEmbed implements EmbedOptions { + title?: string + + type?: string + + description?: string + + url?: string + + 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 } + + thumbnail?: { url?: string, proxy_url?: string, height?: number, width?: number } + + video?: { url: string, height?: number, width?: number } + + provider?: { name: string, url?: string} + + author?: { name: string, url?: string, proxy_icon_url?: string, icon_url?: string} + + fields?: {name: string, value: string, inline?: boolean}[] + + constructor(data: EmbedOptions = {}) { + this.title = data.title; + this.description = data.description; + this.url = data.url; + this.color = data.color; + this.author = data.author; + this.timestamp = data.timestamp; + this.fields = data.fields || []; + this.thumbnail = data.thumbnail; + this.image = data.image; + this.footer = data.footer; + } + + /** + * Sets the title of this embed. + */ + setTitle(title: string) { + if (typeof title !== 'string') throw new TypeError('RichEmbed titles must be a string.'); + if (title.length > 256) throw new RangeError('RichEmbed titles may not exceed 256 characters.'); + this.title = title; + return this; + } + + /** + * Sets the description of this embed. + */ + setDescription(description: string) { + if (typeof description !== 'string') throw new TypeError('RichEmbed descriptions must be a string.'); + if (description.length > 2048) throw new RangeError('RichEmbed descriptions may not exceed 2048 characters.'); + this.description = description; + return this; + } + + /** + * Sets the URL of this embed. + */ + setURL(url: string) { + if (typeof url !== 'string') throw new TypeError('RichEmbed URLs must be a string.'); + if (!url.startsWith('http://') && !url.startsWith('https://')) url = `https://${url}`; + this.url = encodeURI(url); + return this; + } + + /** + * Sets the color of this embed. + */ + setColor(color: string | number) { + if (typeof color === 'string' || typeof color === 'number') { + if (typeof color === 'string') { + const regex = /[^a-f0-9]/gi; + color = color.replace(/#/g, ''); + if (regex.test(color)) throw new RangeError('Hexadecimal colours must not contain characters other than 0-9 and a-f.'); + color = parseInt(color, 16); + } else if (color < 0 || color > 16777215) throw new RangeError('Base 10 colours must not be less than 0 or greater than 16777215.'); + this.color = color; + return this; + } + throw new TypeError('RichEmbed colours must be hexadecimal as string or number.'); + } + + /** + * Sets the author of this embed. + */ + setAuthor(name: string, icon_url?: string, url?: string) { + if (typeof name !== 'string') throw new TypeError('RichEmbed Author names must be a string.'); + if (url && typeof url !== 'string') throw new TypeError('RichEmbed Author URLs must be a string.'); + if (icon_url && typeof icon_url !== 'string') throw new TypeError('RichEmbed Author icons must be a string.'); + this.author = { name, icon_url, url }; + return this; + } + + /** + * Sets the timestamp of this embed. + */ + setTimestamp(timestamp = new Date()) { + if (Number.isNaN(timestamp.getTime())) throw new TypeError('Expecting ISO8601 (Date constructor)'); + this.timestamp = timestamp; + return this; + } + + /** + * Adds a field to the embed (max 25). + */ + addField(name: string, value: string, inline = false) { + if (typeof name !== 'string') throw new TypeError('RichEmbed Field names must be a string.'); + if (typeof value !== 'string') throw new TypeError('RichEmbed Field values must be a string.'); + if (typeof inline !== 'boolean') throw new TypeError('RichEmbed Field inlines must be a boolean.'); + if (this.fields.length >= 25) throw new RangeError('RichEmbeds may not exceed 25 fields.'); + if (name.length > 256) throw new RangeError('RichEmbed field names may not exceed 256 characters.'); + if (!/\S/.test(name)) throw new RangeError('RichEmbed field names may not be empty.'); + if (value.length > 1024) throw new RangeError('RichEmbed field values may not exceed 1024 characters.'); + if (!/\S/.test(value)) throw new RangeError('RichEmbed field values may not be empty.'); + this.fields.push({ name, value, inline }); + return this; + } + + /** + * Convenience function for `.addField('\u200B', '\u200B', inline)`. + */ + addBlankField(inline = false) { + return this.addField('\u200B', '\u200B', inline); + } + + /** + * Set the thumbnail of this embed. + */ + setThumbnail(url: string) { + if (typeof url !== 'string') throw new TypeError('RichEmbed Thumbnail URLs must be a string.'); + this.thumbnail = { url }; + return this; + } + + /** + * Set the image of this embed. + */ + setImage(url: string) { + if (typeof url !== 'string') throw new TypeError('RichEmbed Image URLs must be a string.'); + if (!url.startsWith('http://') || !url.startsWith('https://')) url = `https://${url}`; + this.image = { url }; + return this; + } + + /** + * Sets the footer of this embed. + */ + setFooter(text: string, icon_url?: string) { + if (typeof text !== 'string') throw new TypeError('RichEmbed Footers must be a string.'); + if (icon_url && typeof icon_url !== 'string') throw new TypeError('RichEmbed Footer icon URLs must be a string.'); + if (text.length > 2048) throw new RangeError('RichEmbed footer text may not exceed 2048 characters.'); + this.footer = { text, icon_url }; + return this; + } +} diff --git a/src/class/Route.ts b/src/class/Route.ts index 1abdcda..29423e5 100644 --- a/src/class/Route.ts +++ b/src/class/Route.ts @@ -1,75 +1,75 @@ -/* eslint-disable consistent-return */ -import { Router, Request, Response } from 'express'; -import { Server } from '.'; - -export default class Route { - public server: Server; - - public conf: { path: string; deprecated?: boolean; maintenance?: boolean; }; - - public router: Router; - - constructor(server: Server) { - this.server = server; - this.conf = { path: '' }; - this.router = Router(); - } - - public bind() {} - - public init() { - this.router.all('*', (req, res, next) => { - this.server.client.util.signale.log(`'${req.method}' request from '${req.ip}' to '${req.hostname}${req.path}'.`); - if (this.conf.maintenance === true) res.status(503).json({ code: this.constants.codes.MAINTENANCE_OR_UNAVAILABLE, message: this.constants.messages.MAINTENANCE_OR_UNAVAILABLE }); - else if (this.conf.deprecated === true) res.status(501).json({ code: this.constants.codes.DEPRECATED, message: this.constants.messages.DEPRECATED }); - else next(); - }); - } - - public deprecated(): void { - this.router.all('*', (_req, res) => { - res.status(501).json({ code: this.constants.codes.DEPRECATED, message: this.constants.messages.DEPRECATED }); - }); - } - - public maintenance(): void { - this.router.all('*', (_req, res) => { - res.status(503).json({ code: this.constants.codes.MAINTENANCE_OR_UNAVAILABLE, message: this.constants.messages.MAINTENANCE_OR_UNAVAILABLE }); - }); - } - - public handleError(error: Error, res: Response) { - res.status(500).json({ code: this.constants.codes.SERVER_ERROR, message: this.constants.messages.SERVER_ERROR }); - this.server.parent.client.util.handleError(error); - } - - get constants() { - return { - codes: { - SUCCESS: 100, - UNAUTHORIZED: 101, - PERMISSION_DENIED: 104, - ENDPOINT_NOT_FOUND: 104, - NOT_FOUND: 1041, - ACCOUNT_NOT_FOUND: 1041, - CLIENT_ERROR: 1044, - SERVER_ERROR: 105, - DEPRECATED: 1051, - MAINTENANCE_OR_UNAVAILABLE: 1053, - }, - messages: { - UNAUTHORIZED: ['CREDENTIALS_INVALID', 'The credentials you supplied are invalid.'], - BEARER_TOKEN_INVALID: ['BEARER_TOKEN_INVALID', 'The Bearer token you supplied is invalid.'], - PERMISSION_DENIED: ['PERMISSION_DENIED', 'You do not have valid credentials to access this resource.'], - NOT_FOUND: ['NOT_FOUND', 'The resource you requested cannot be located.'], - ENDPOINT_NOT_FOUND: ['ENDPOINT_NOT_FOUND', 'The endpoint you requested does not exist or cannot be located.'], - SERVER_ERROR: ['INTERNAL_ERROR', 'An internal error has occurred, Engineers have been notified.'], - DEPRECATED: ['ENDPOINT_OR_RESOURCE_DEPRECATED', 'The endpoint or resource you\'re trying to access has been deprecated.'], - MAINTENANCE_OR_UNAVAILABLE: ['SERVICE_UNAVAILABLE', 'The endpoint or resource you\'re trying to access is either in maintenance or is not available.'], - }, - discord: { - SERVER_ID: '446067825673633794', - }, - }; - } -} +/* eslint-disable consistent-return */ +import { Router, Request, Response } from 'express'; +import { Server } from '.'; + +export default class Route { + public server: Server; + + public conf: { path: string; deprecated?: boolean; maintenance?: boolean; }; + + public router: Router; + + constructor(server: Server) { + this.server = server; + this.conf = { path: '' }; + this.router = Router(); + } + + public bind() {} + + public init() { + this.router.all('*', (req, res, next) => { + this.server.client.util.signale.log(`'${req.method}' request from '${req.ip}' to '${req.hostname}${req.path}'.`); + if (this.conf.maintenance === true) res.status(503).json({ code: this.constants.codes.MAINTENANCE_OR_UNAVAILABLE, message: this.constants.messages.MAINTENANCE_OR_UNAVAILABLE }); + else if (this.conf.deprecated === true) res.status(501).json({ code: this.constants.codes.DEPRECATED, message: this.constants.messages.DEPRECATED }); + else next(); + }); + } + + public deprecated(): void { + this.router.all('*', (_req, res) => { + res.status(501).json({ code: this.constants.codes.DEPRECATED, message: this.constants.messages.DEPRECATED }); + }); + } + + public maintenance(): void { + this.router.all('*', (_req, res) => { + res.status(503).json({ code: this.constants.codes.MAINTENANCE_OR_UNAVAILABLE, message: this.constants.messages.MAINTENANCE_OR_UNAVAILABLE }); + }); + } + + public handleError(error: Error, res: Response) { + res.status(500).json({ code: this.constants.codes.SERVER_ERROR, message: this.constants.messages.SERVER_ERROR }); + this.server.parent.client.util.handleError(error); + } + + get constants() { + return { + codes: { + SUCCESS: 100, + UNAUTHORIZED: 101, + PERMISSION_DENIED: 104, + ENDPOINT_NOT_FOUND: 104, + NOT_FOUND: 1041, + ACCOUNT_NOT_FOUND: 1041, + CLIENT_ERROR: 1044, + SERVER_ERROR: 105, + DEPRECATED: 1051, + MAINTENANCE_OR_UNAVAILABLE: 1053, + }, + messages: { + UNAUTHORIZED: ['CREDENTIALS_INVALID', 'The credentials you supplied are invalid.'], + BEARER_TOKEN_INVALID: ['BEARER_TOKEN_INVALID', 'The Bearer token you supplied is invalid.'], + PERMISSION_DENIED: ['PERMISSION_DENIED', 'You do not have valid credentials to access this resource.'], + NOT_FOUND: ['NOT_FOUND', 'The resource you requested cannot be located.'], + ENDPOINT_NOT_FOUND: ['ENDPOINT_NOT_FOUND', 'The endpoint you requested does not exist or cannot be located.'], + SERVER_ERROR: ['INTERNAL_ERROR', 'An internal error has occurred, Engineers have been notified.'], + DEPRECATED: ['ENDPOINT_OR_RESOURCE_DEPRECATED', 'The endpoint or resource you\'re trying to access has been deprecated.'], + MAINTENANCE_OR_UNAVAILABLE: ['SERVICE_UNAVAILABLE', 'The endpoint or resource you\'re trying to access is either in maintenance or is not available.'], + }, + discord: { + SERVER_ID: '446067825673633794', + }, + }; + } +} diff --git a/src/class/Server.ts b/src/class/Server.ts index b7cc5b7..34519aa 100644 --- a/src/class/Server.ts +++ b/src/class/Server.ts @@ -1,68 +1,68 @@ -import express from 'express'; -import helmet from 'helmet'; -import { promises as fs } from 'fs'; -import { Server as HTTPServer } from 'http'; -import { Collection, ServerManagement, Route } from '.'; - -export default class Server { - public app: express.Application; - - public routes: Collection; - - public parent: ServerManagement; - - public port: number; - - private root: string; - - constructor(parent: ServerManagement, port: number, routeRoot: string) { - this.parent = parent; - this.app = express(); - this.routes = new Collection(); - this.port = port; - this.root = routeRoot; - - this.loadRoutes(); - this.init(); - } - - get client() { - return this.parent.client; - } - - public async loadRoutes() { - 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) { - route.maintenance(); - } else { - route.init(); - route.bind(); - } - this.parent.client.util.signale.success(`Successfully loaded route 'http://localhost:${this.port}/${route.conf.path}'.`); - this.routes.add(route.conf.path, route); - this.app.use(route.conf.path, route.router); - } - this.app.listen(this.port); - } - - public init() { - this.app.set('trust proxy', 'loopback'); - this.app.use(helmet({ - hsts: false, - hidePoweredBy: false, - contentSecurityPolicy: { - directives: { - defaultSrc: ["'self'"], - }, - }, - })); - } - - public listen(port: number): HTTPServer { - return this.app.listen(port); - } -} +import express from 'express'; +import helmet from 'helmet'; +import { promises as fs } from 'fs'; +import { Server as HTTPServer } from 'http'; +import { Collection, ServerManagement, Route } from '.'; + +export default class Server { + public app: express.Application; + + public routes: Collection; + + public parent: ServerManagement; + + public port: number; + + private root: string; + + constructor(parent: ServerManagement, port: number, routeRoot: string) { + this.parent = parent; + this.app = express(); + this.routes = new Collection(); + this.port = port; + this.root = routeRoot; + + this.loadRoutes(); + this.init(); + } + + get client() { + return this.parent.client; + } + + public async loadRoutes() { + 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) { + route.maintenance(); + } else { + route.init(); + route.bind(); + } + this.parent.client.util.signale.success(`Successfully loaded route 'http://localhost:${this.port}/${route.conf.path}'.`); + this.routes.add(route.conf.path, route); + this.app.use(route.conf.path, route.router); + } + this.app.listen(this.port); + } + + public init() { + this.app.set('trust proxy', 'loopback'); + this.app.use(helmet({ + hsts: false, + hidePoweredBy: false, + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + }, + }, + })); + } + + public listen(port: number): HTTPServer { + return this.app.listen(port); + } +} diff --git a/src/class/ServerManagement.ts b/src/class/ServerManagement.ts index efb9d3e..cd1c412 100644 --- a/src/class/ServerManagement.ts +++ b/src/class/ServerManagement.ts @@ -1,22 +1,22 @@ -import { Client, Collection, Server } from '.'; -import serverSetup from '../api'; - -export default class ServerManagement { - public client: Client; - - public servers: Collection; - - constructor(client: Client) { - this.client = client; - this.servers = new Collection(); - this.loadServers(); - } - - public async loadServers() { - 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}'.`); - } - } -} +import { Client, Collection, Server } from '.'; +import serverSetup from '../api'; + +export default class ServerManagement { + public client: Client; + + public servers: Collection; + + constructor(client: Client) { + this.client = client; + this.servers = new Collection(); + this.loadServers(); + } + + public async loadServers() { + 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 0baf1d7..d1a096d 100644 --- a/src/class/Util.ts +++ b/src/class/Util.ts @@ -1,138 +1,138 @@ -/* eslint-disable no-bitwise */ -import nodemailer from 'nodemailer'; -import signale from 'signale'; -import { Member, Message, Guild, PrivateChannel, GroupChannel, Role, AnyGuildChannel } from 'eris'; -import { Client, Command, Moderation, RichEmbed } from '.'; -import { statusMessages as emotes } from '../configs/emotes.json'; - -export default class Util { - public client: Client; - - public moderation: Moderation; - - public signale: signale.Signale; - - public transporter: nodemailer.Transporter; - - constructor(client: Client) { - this.client = client; - this.moderation = new Moderation(this.client); - this.signale = signale; - this.signale.config({ - displayDate: true, - displayTimestamp: true, - displayFilename: true, - }); - this.transporter = nodemailer.createTransport({ - host: 'staff.libraryofcode.org', - port: 587, - auth: { user: 'internal', pass: this.client.config.emailPass }, - }); - } - - get emojis() { - return { - SUCCESS: emotes.success, - LOADING: emotes.loading, - ERROR: emotes.error, - }; - } - - /** - * Resolves a command - * @param query Command input - * @param message Only used to check for errors - */ - public resolveCommand(query: string | string[]): Promise<{cmd: Command, args: string[] }> { - try { - 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())); - - if (!resolvedCommand) return Promise.resolve(null); - query.shift(); - return Promise.resolve({ cmd: resolvedCommand, args: query }); - } catch (error) { - return Promise.reject(error); - } - } - - public resolveGuildChannel(query: string, { channels }: Guild, categories = false): AnyGuildChannel | undefined { - const ch: AnyGuildChannel[] = channels.filter((c) => (!categories ? c.type !== 4 : true)); - return ch.find((c) => c.id === query.replace(/[<#>]/g, '') || c.name === query) - || ch.find((c) => c.name.toLowerCase() === query.toLowerCase()) - || ch.find((c) => c.name.toLowerCase().startsWith(query.toLowerCase())); - } - - public resolveRole(query: string, { roles }: Guild): Role | undefined { - return roles.find((r) => r.id === query.replace(/[<@&>]/g, '') || r.name === query) - || roles.find((r) => r.name.toLowerCase() === query.toLowerCase()) - || roles.find((r) => r.name.toLowerCase().startsWith(query.toLowerCase())); - } - - public resolveMember(query: string, { members }: Guild): Member | undefined { - return members.find((m) => `${m.username}#${m.discriminator}` === query || m.username === query || m.id === query.replace(/[<@!>]/g, '') || m.nick === query) // Exact match for mention, username+discrim, username and user ID - || members.find((m) => `${m.username.toLowerCase()}#${m.discriminator}` === query.toLowerCase() || m.username.toLowerCase() === query.toLowerCase() || (m.nick && m.nick.toLowerCase() === query.toLowerCase())) // Case insensitive match for username+discrim, username - || members.find((m) => m.username.toLowerCase().startsWith(query.toLowerCase()) || (m.nick && m.nick.toLowerCase().startsWith(query.toLowerCase()))); - } - - public async handleError(error: Error, message?: Message, command?: Command, disable = true): Promise { - try { - this.signale.error(error); - const info = { content: `\`\`\`js\n${error.stack || error}\n\`\`\``, embed: null }; - if (message) { - const embed = new RichEmbed(); - embed.setColor('FF0000'); - embed.setAuthor(`Error caused by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL); - embed.setTitle('Message content'); - embed.setDescription(message.content); - embed.addField('User', `${message.author.mention} (\`${message.author.id}\`)`, true); - embed.addField('Channel', message.channel.mention, true); - let guild: string; - if (message.channel instanceof PrivateChannel || message.channel instanceof GroupChannel) guild = '@me'; - else guild = message.channel.guild.id; - embed.addField('Message link', `[Click here](https://discordapp.com/channels/${guild}/${message.channel.id}/${message.id})`, true); - embed.setTimestamp(new Date(message.timestamp)); - info.embed = embed; - } - await this.client.createMessage('595788220764127272', info); - const msg = message ? message.content.slice(this.client.config.prefix.length).trim().split(/ +/g) : []; - 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) { - this.signale.error(err); - } - } - - public splitString(string: string, length: number): string[] { - if (!string) return []; - if (Array.isArray(string)) string = string.join('\n'); - if (string.length <= length) return [string]; - const arrayString: string[] = []; - let str: string = ''; - let pos: number; - while (string.length > 0) { - pos = string.length > length ? string.lastIndexOf('\n', length) : string.length; - if (pos > length) pos = length; - str = string.substr(0, pos); - string = string.substr(pos); - arrayString.push(str); - } - return arrayString; - } - - public splitFields(fields: { name: string, value: string, inline?: boolean }[]): { name: string, value: string, inline?: boolean }[][] { - let index = 0; - const array: {name: string, value: string, inline?: boolean}[][] = [[]]; - while (fields.length) { - if (array[index].length >= 25) { index += 1; array[index] = []; } - array[index].push(fields[0]); fields.shift(); - } - return array; - } - - public decimalToHex(int: number): string { - const hex = int.toString(16); - return '#000000'.substring(0, 7 - hex.length) + hex; - } -} +/* eslint-disable no-bitwise */ +import nodemailer from 'nodemailer'; +import signale from 'signale'; +import { Member, Message, Guild, PrivateChannel, GroupChannel, Role, AnyGuildChannel } from 'eris'; +import { Client, Command, Moderation, RichEmbed } from '.'; +import { statusMessages as emotes } from '../configs/emotes.json'; + +export default class Util { + public client: Client; + + public moderation: Moderation; + + public signale: signale.Signale; + + public transporter: nodemailer.Transporter; + + constructor(client: Client) { + this.client = client; + this.moderation = new Moderation(this.client); + this.signale = signale; + this.signale.config({ + displayDate: true, + displayTimestamp: true, + displayFilename: true, + }); + this.transporter = nodemailer.createTransport({ + host: 'staff.libraryofcode.org', + port: 587, + auth: { user: 'internal', pass: this.client.config.emailPass }, + }); + } + + get emojis() { + return { + SUCCESS: emotes.success, + LOADING: emotes.loading, + ERROR: emotes.error, + }; + } + + /** + * Resolves a command + * @param query Command input + * @param message Only used to check for errors + */ + public resolveCommand(query: string | string[]): Promise<{cmd: Command, args: string[] }> { + try { + 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())); + + if (!resolvedCommand) return Promise.resolve(null); + query.shift(); + return Promise.resolve({ cmd: resolvedCommand, args: query }); + } catch (error) { + return Promise.reject(error); + } + } + + public resolveGuildChannel(query: string, { channels }: Guild, categories = false): AnyGuildChannel | undefined { + const ch: AnyGuildChannel[] = channels.filter((c) => (!categories ? c.type !== 4 : true)); + return ch.find((c) => c.id === query.replace(/[<#>]/g, '') || c.name === query) + || ch.find((c) => c.name.toLowerCase() === query.toLowerCase()) + || ch.find((c) => c.name.toLowerCase().startsWith(query.toLowerCase())); + } + + public resolveRole(query: string, { roles }: Guild): Role | undefined { + return roles.find((r) => r.id === query.replace(/[<@&>]/g, '') || r.name === query) + || roles.find((r) => r.name.toLowerCase() === query.toLowerCase()) + || roles.find((r) => r.name.toLowerCase().startsWith(query.toLowerCase())); + } + + public resolveMember(query: string, { members }: Guild): Member | undefined { + return members.find((m) => `${m.username}#${m.discriminator}` === query || m.username === query || m.id === query.replace(/[<@!>]/g, '') || m.nick === query) // Exact match for mention, username+discrim, username and user ID + || members.find((m) => `${m.username.toLowerCase()}#${m.discriminator}` === query.toLowerCase() || m.username.toLowerCase() === query.toLowerCase() || (m.nick && m.nick.toLowerCase() === query.toLowerCase())) // Case insensitive match for username+discrim, username + || members.find((m) => m.username.toLowerCase().startsWith(query.toLowerCase()) || (m.nick && m.nick.toLowerCase().startsWith(query.toLowerCase()))); + } + + public async handleError(error: Error, message?: Message, command?: Command, disable = true): Promise { + try { + this.signale.error(error); + const info = { content: `\`\`\`js\n${error.stack || error}\n\`\`\``, embed: null }; + if (message) { + const embed = new RichEmbed(); + embed.setColor('FF0000'); + embed.setAuthor(`Error caused by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL); + embed.setTitle('Message content'); + embed.setDescription(message.content); + embed.addField('User', `${message.author.mention} (\`${message.author.id}\`)`, true); + embed.addField('Channel', message.channel.mention, true); + let guild: string; + if (message.channel instanceof PrivateChannel || message.channel instanceof GroupChannel) guild = '@me'; + else guild = message.channel.guild.id; + embed.addField('Message link', `[Click here](https://discordapp.com/channels/${guild}/${message.channel.id}/${message.id})`, true); + embed.setTimestamp(new Date(message.timestamp)); + info.embed = embed; + } + await this.client.createMessage('595788220764127272', info); + const msg = message ? message.content.slice(this.client.config.prefix.length).trim().split(/ +/g) : []; + 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) { + this.signale.error(err); + } + } + + public splitString(string: string, length: number): string[] { + if (!string) return []; + if (Array.isArray(string)) string = string.join('\n'); + if (string.length <= length) return [string]; + const arrayString: string[] = []; + let str: string = ''; + let pos: number; + while (string.length > 0) { + pos = string.length > length ? string.lastIndexOf('\n', length) : string.length; + if (pos > length) pos = length; + str = string.substr(0, pos); + string = string.substr(pos); + arrayString.push(str); + } + return arrayString; + } + + public splitFields(fields: { name: string, value: string, inline?: boolean }[]): { name: string, value: string, inline?: boolean }[][] { + let index = 0; + const array: {name: string, value: string, inline?: boolean}[][] = [[]]; + while (fields.length) { + if (array[index].length >= 25) { index += 1; array[index] = []; } + array[index].push(fields[0]); fields.shift(); + } + return array; + } + + public decimalToHex(int: number): string { + const hex = int.toString(16); + return '#000000'.substring(0, 7 - hex.length) + hex; + } +} diff --git a/src/class/index.ts b/src/class/index.ts index b3ab3bb..b14fb21 100644 --- a/src/class/index.ts +++ b/src/class/index.ts @@ -1,10 +1,10 @@ -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'; -export { default as Server } from './Server'; -export { default as ServerManagement } from './ServerManagement'; -export { default as Util } from './Util'; +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'; +export { default as Server } from './Server'; +export { default as ServerManagement } from './ServerManagement'; +export { default as Util } from './Util'; diff --git a/src/commands/addrank.ts b/src/commands/addrank.ts new file mode 100644 index 0000000..dbdab64 --- /dev/null +++ b/src/commands/addrank.ts @@ -0,0 +1,45 @@ +import { Message } from 'eris'; +import { Client, Command } from '../class'; + +export default class AddRank extends Command { + constructor(client: Client) { + super(client); + this.name = 'addrank'; + this.description = 'Makes a role self-assignable.'; + this.usage = `${this.client.config.prefix}addrank `; + this.permissions = 6; + this.guildOnly = true; + this.enabled = true; + } + + public async run(message: Message, args: string[]) { + try { + if (!args[0]) return this.client.commands.get('help').run(message, [this.name]); + if (!args[1]) return this.error(message.channel, 'Permissions are required.'); + if (!args[2]) return this.error(message.channel, 'A description is required'); + const role = this.client.util.resolveRole(args[0], this.client.guilds.get(this.client.config.guildID)); + if (!role) return this.error(message.channel, 'The role you specified doesn\'t appear to exist.'); + + const check = await this.client.db.Rank.findOne({ roleID: role.id }); + if (check) return this.error(message.channel, 'This role is already self-assignable.'); + + let permissions: string[]; + if (args[1] === '0') { + permissions = ['0']; + } else { + permissions = args[1].split(':'); + } + + const entry = new this.client.db.Rank({ + name: role.name, + roleID: role.id, + permissions, + description: args.slice(2).join(' '), + }); + await entry.save(); + return this.success(message.channel, `Role ${role.name} is now self-assignable.`); + } catch (err) { + return this.client.util.handleError(err, message, this); + } + } +} diff --git a/src/commands/delrank.ts b/src/commands/delrank.ts new file mode 100644 index 0000000..64c068b --- /dev/null +++ b/src/commands/delrank.ts @@ -0,0 +1,30 @@ +import { Message } from 'eris'; +import { Client, Command } from '../class'; + +export default class DelRank extends Command { + constructor(client: Client) { + super(client); + this.name = 'delrank'; + this.description = 'Deletes an existing self-assignable role. This doesn\'t delete the role itself.'; + this.usage = `${this.client.config.prefix}delrank `; + this.permissions = 6; + this.guildOnly = true; + this.enabled = true; + } + + public async run(message: Message, args: string[]) { + try { + if (!args[0]) return this.client.commands.get('help').run(message, [this.name]); + const role = this.client.util.resolveRole(args[0], this.client.guilds.get(this.client.config.guildID)); + if (!role) return this.error(message.channel, 'The role you specified doesn\'t appear to exist.'); + + const check = await this.client.db.Rank.findOne({ roleID: role.id }); + if (!check) return this.error(message.channel, 'The entry doesn\'t appear to exist.'); + + await this.client.db.Rank.deleteOne({ roleID: role.id }); + return this.success(message.channel, `Role ${role.name} is no longer self-assignable.`); + } catch (err) { + return this.client.util.handleError(err, message, this); + } + } +} diff --git a/src/commands/index.ts b/src/commands/index.ts index 4ed5246..7e21286 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -1,7 +1,9 @@ export { default as additem } from './additem'; +export { default as addrank } from './addrank'; export { default as addredirect } from './addredirect'; export { default as ban } from './ban'; export { default as delitem } from './delitem'; +export { default as delrank } from './delrank'; export { default as delredirect } from './delredirect'; export { default as djs } from './djs'; export { default as eval } from './eval'; @@ -13,6 +15,7 @@ export { default as listredirects } from './listredirects'; export { default as npm } from './npm'; export { default as page } from './page'; export { default as ping } from './ping'; +export { default as rank } from './rank'; export { default as roleinfo } from './roleinfo'; export { default as unban } from './unban'; export { default as whois } from './whois'; diff --git a/src/commands/rank.ts b/src/commands/rank.ts new file mode 100644 index 0000000..5717ca7 --- /dev/null +++ b/src/commands/rank.ts @@ -0,0 +1,62 @@ +import { Message, Role } from 'eris'; +import { Client, Command, RichEmbed } from '../class'; + +export default class Rank extends Command { + constructor(client: Client) { + super(client); + this.name = 'rank'; + this.description = 'Joins/leaves a self-assignable rank. Run this command without arguments to get a list of self-assignable roles.'; + this.usage = `${this.client.config.prefix}rank `; + this.permissions = 0; + this.guildOnly = true; + this.enabled = true; + } + + public async run(message: Message, args: string[]) { + try { + if (!args[0]) { + const roles = await this.client.db.Rank.find(); + const embed = new RichEmbed(); + embed.setTitle('Ranks'); + embed.setDescription(`Use \`${this.client.config.prefix}rank to join/leave the rank.\``); + for (const rank of roles.sort((a, b) => a.name.localeCompare(b.name))) { + let perms: string; + if (rank.permissions.includes('0')) { + perms = 'Everyone'; + } else { + const rolesArray: Role[] = []; + rank.permissions.forEach((r) => { + rolesArray.push(this.client.guilds.get(this.client.config.guildID).roles.get(r)); + }); + perms = rolesArray.map((r) => message.guild.roles.get(r.id)).sort((a, b) => b.position - a.position).map((r) => `<@&${r.id}>`).join(', '); + } + embed.addField(rank.name, `__Description:__ ${rank.description}\n__Permissions:__ ${perms}`); + } + embed.setFooter(this.client.user.username, this.client.user.avatarURL); + embed.setTimestamp(); + return message.channel.createMessage({ embed }); + } + const role = this.client.util.resolveRole(args[0], this.client.guilds.get(this.client.config.guildID)); + if (!role) return this.error(message.channel, 'The role you specified doesn\'t exist.'); + const entry = await this.client.db.Rank.findOne({ roleID: role.id }).lean().exec(); + if (!entry) return this.error(message.channel, 'The rank you specified doesn\'t exist.'); + if (!message.member.roles.includes(entry.roleID)) { + let permCheck: boolean; + if (entry.permissions.includes('0')) { + permCheck = true; + } else { + permCheck = entry.permissions.some((item) => message.member.roles.includes(item)); + } + if (!permCheck) return this.error(message.channel, 'Permission denied.'); + await message.member.addRole(entry.roleID, `User ${message.author.username}#${message.author.discriminator} self-assigned this role.`); + this.success(message.channel, `You have self-assigned ${entry.name}.`); + } else if (message.member.roles.includes(entry.roleID)) { + await message.member.removeRole(entry.roleID, `User ${message.author.username}#${message.author.discriminator} has removed a self-assignable role.`); + this.success(message.channel, `You have removed ${entry.name}.`); + } + return null; + } catch (err) { + return this.client.util.handleError(err, message, this); + } + } +} diff --git a/src/configs/acknowledgements.json b/src/configs/acknowledgements.json index 225cb8c..2d1813f 100644 --- a/src/configs/acknowledgements.json +++ b/src/configs/acknowledgements.json @@ -1,142 +1,142 @@ -[ - { - "name": "Matthew", - "id": "278620217221971968", - "title": "Chief Director of Engineering", - "dept": "Board of Directors", - "emailAddress": "matthew@staff.libraryofcode.org", - "gitlab": "https://gitlab.libraryofcode.org/matthew", - "github": "https://github.com/matthew119427", - "bio": "so baby come light me up, and maybe ill let you on it. a little bit dangerous, but baby thats how i want it. a little less conversation and a little more touch my body. cuz im so into you... ~ Ariana Grande, Into You - Dangerous Woman", - "acknowledgements": ["Maintainer & Lead Developer"] - }, - { - "name": "Joe", - "id": "556969659531001858", - "dept": "Board of Directors", - "emailAddress": "joe@staff.libraryofcode.org", - "gitlab": "https://gitlab.libraryofcode.org/Joe", - "github": "https://github.com/sirdroolio", - "bio": "new Error(\"Proper Bio not found\");", - "acknowledgements": ["Developer"] - }, - { - "name": "Bsian", - "id": "253600545972027394", - "title": "Director of Engineering", - "dept": "Board of Directors", - "emailAddress": "bsian@staff.libraryofcode.org", - "bio": "I also like trains", - "acknowledgements": ["Maintainer & Assistant Lead Developer"] - }, - { - "name": "NightRaven", - "id": "239261547959025665", - "title": "Director of Information Security", - "dept": "Board of Directors", - "emailAddress": "nightraven@staff.libraryofcode.org", - "bio": "I like trains" - }, - { - "name": "Unknown", - "id": "143414786913206272", - "title": "Director of Operations", - "dept": "Board of Directors", - "emailAddress": "unknown@staff.libraryofcode.org", - "gitlab": "https://gitlab.libraryofcode.org/unknown", - "bio": "im not a proffesional developer or anything, i just enjoy it as a hobby." - }, - { - "name": "TheSkele27", - "id": "213632190557192192", - "title": "Director of Community Engagement", - "dept": "Board of Directors", - "emailAddress": "theskele27@staff.libraryofcode.org", - "gitlab": "https://gitlab.libraryofcode.org/TheSkele27", - "github": "https://github.com/TheSkele27", - "bio": "Is water wet?" - }, - { - "name": "Catbirby", - "id": "131953641371205632", - "dept": "Supervisor", - "emailAddress": "catbirby@staff.libraryofcode.org", - "github": "https://github.com/catbirby", - "bio": "Computer Tech/Networking Nerd/Sysadmin/Graphic Designer/Audiophile. I don't do much coding but know my way around most languages." - }, - { - "name": "D3XTER", - "id": "468009964263178262", - "dept": "Core Team", - "emailAddress": "dexter@staff.libraryofcode.org", - "gitlab": "https://gitlab.libraryofcode.org/D3XTER", - "bio": "Hi I'm D3XTER how are ya?" - }, - { - "name": "DedShotTM", - "id": "402154763363418142", - "dept": "Technician & Moderator", - "emailAddress": "dedshot@staff.libraryofcode.org", - "gitlab": "https://gitlab.libraryofcode.org/DedShotTM", - "github": "https://github.com/DedShotTM", - "bio": "Imagine having a bio", - "acknowledgements": ["Contributor"] - }, - { - "name": "EdgyBoi2414", - "id": "397432516010835970", - "dept": "Core Team", - "emailAddress": "edgyboi2414@gmail.com", - "gitlab": "https://gitlab.libraryofcode.org/EdgyBoi2414", - "github": "https://github.com/EdgyBoi2414", - "bio": "\"If teardrops could be bottled, there'd be swimming pools, built by models...\" - Some Philosopher" - }, - { - "name": "Hector", - "id": "377781496292835339", - "dept": "Core Team", - "emailAddress": "hector@staff.libraryofcode.org", - "gitlab": "https://gitlab.libraryofcode.org/Hector", - "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": "KhaaZ", - "id": "179908288337412096", - "dept": "Core Team", - "emailAddress": "khaaz@staff.libraryofcode.org", - "gitlab": "https://gitlab.libraryofcode.org/KhaaZ", - "github": "https://github.com/Khaazz", - "bio": "I baguette for a living and eat code for breakfast." - }, - { - "name": "Ryan", - "id": "186679073764802560", - "dept": "Associate", - "emailAddress": "wbdvryan@staff.libraryofcode.org", - "gitlab": "https://gitlab.libraryofcode.org/plainRyan", - "bio": "Experiment, learn, share, repeat.", - "acknowledgements": ["Contributor"] - }, - { - "name": "Zloth", - "id": "382368885267234816", - "dept": "Associate", - "emailAddress": "zloth@staff.libraryofcode.org", - "github": "https://github.com/gavintjhxx", - "bio": "Wake up. Eat. Code. Sleep. Loop()" - }, - { - "name": "PlayerVMachine", - "id": "273999507174195203", - "dept": "Instructor & Associate", - "emailAddress": "nicolas@staff.libraryofcode.org", - "bio": "I write C++ to pay off my student loans" - }, - { - "name": "Null", - "id": "323673862971588609", - "gitlab": "https://gitlab.libraryofcode.org/null", - "acknowledgements": ["Contributor"] - } -] +[ + { + "name": "Matthew", + "id": "278620217221971968", + "title": "Chief Director of Engineering", + "dept": "Board of Directors", + "emailAddress": "matthew@staff.libraryofcode.org", + "gitlab": "https://gitlab.libraryofcode.org/matthew", + "github": "https://github.com/matthew119427", + "bio": "so baby come light me up, and maybe ill let you on it. a little bit dangerous, but baby thats how i want it. a little less conversation and a little more touch my body. cuz im so into you... ~ Ariana Grande, Into You - Dangerous Woman", + "acknowledgements": ["Maintainer & Lead Developer"] + }, + { + "name": "Joe", + "id": "556969659531001858", + "dept": "Board of Directors", + "emailAddress": "joe@staff.libraryofcode.org", + "gitlab": "https://gitlab.libraryofcode.org/Joe", + "github": "https://github.com/sirdroolio", + "bio": "new Error(\"Proper Bio not found\");", + "acknowledgements": ["Developer"] + }, + { + "name": "Bsian", + "id": "253600545972027394", + "title": "Director of Engineering", + "dept": "Board of Directors", + "emailAddress": "bsian@staff.libraryofcode.org", + "bio": "I also like trains", + "acknowledgements": ["Maintainer & Assistant Lead Developer"] + }, + { + "name": "NightRaven", + "id": "239261547959025665", + "title": "Director of Information Security", + "dept": "Board of Directors", + "emailAddress": "nightraven@staff.libraryofcode.org", + "bio": "I like trains" + }, + { + "name": "Unknown", + "id": "143414786913206272", + "title": "Director of Operations", + "dept": "Board of Directors", + "emailAddress": "unknown@staff.libraryofcode.org", + "gitlab": "https://gitlab.libraryofcode.org/unknown", + "bio": "im not a proffesional developer or anything, i just enjoy it as a hobby." + }, + { + "name": "TheSkele27", + "id": "213632190557192192", + "title": "Director of Community Engagement", + "dept": "Board of Directors", + "emailAddress": "theskele27@staff.libraryofcode.org", + "gitlab": "https://gitlab.libraryofcode.org/TheSkele27", + "github": "https://github.com/TheSkele27", + "bio": "Is water wet?" + }, + { + "name": "Catbirby", + "id": "131953641371205632", + "dept": "Supervisor", + "emailAddress": "catbirby@staff.libraryofcode.org", + "github": "https://github.com/catbirby", + "bio": "Computer Tech/Networking Nerd/Sysadmin/Graphic Designer/Audiophile. I don't do much coding but know my way around most languages." + }, + { + "name": "D3XTER", + "id": "468009964263178262", + "dept": "Core Team", + "emailAddress": "dexter@staff.libraryofcode.org", + "gitlab": "https://gitlab.libraryofcode.org/D3XTER", + "bio": "Hi I'm D3XTER how are ya?" + }, + { + "name": "DedShotTM", + "id": "402154763363418142", + "dept": "Technician & Moderator", + "emailAddress": "dedshot@staff.libraryofcode.org", + "gitlab": "https://gitlab.libraryofcode.org/DedShotTM", + "github": "https://github.com/DedShotTM", + "bio": "Imagine having a bio", + "acknowledgements": ["Contributor"] + }, + { + "name": "EdgyBoi2414", + "id": "397432516010835970", + "dept": "Core Team", + "emailAddress": "edgyboi2414@gmail.com", + "gitlab": "https://gitlab.libraryofcode.org/EdgyBoi2414", + "github": "https://github.com/EdgyBoi2414", + "bio": "\"If teardrops could be bottled, there'd be swimming pools, built by models...\" - Some Philosopher" + }, + { + "name": "Hector", + "id": "377781496292835339", + "dept": "Core Team", + "emailAddress": "hector@staff.libraryofcode.org", + "gitlab": "https://gitlab.libraryofcode.org/Hector", + "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": "KhaaZ", + "id": "179908288337412096", + "dept": "Core Team", + "emailAddress": "khaaz@staff.libraryofcode.org", + "gitlab": "https://gitlab.libraryofcode.org/KhaaZ", + "github": "https://github.com/Khaazz", + "bio": "I baguette for a living and eat code for breakfast." + }, + { + "name": "Ryan", + "id": "186679073764802560", + "dept": "Associate", + "emailAddress": "wbdvryan@staff.libraryofcode.org", + "gitlab": "https://gitlab.libraryofcode.org/plainRyan", + "bio": "Experiment, learn, share, repeat.", + "acknowledgements": ["Contributor"] + }, + { + "name": "Zloth", + "id": "382368885267234816", + "dept": "Associate", + "emailAddress": "zloth@staff.libraryofcode.org", + "github": "https://github.com/gavintjhxx", + "bio": "Wake up. Eat. Code. Sleep. Loop()" + }, + { + "name": "PlayerVMachine", + "id": "273999507174195203", + "dept": "Instructor & Associate", + "emailAddress": "nicolas@staff.libraryofcode.org", + "bio": "I write C++ to pay off my student loans" + }, + { + "name": "Null", + "id": "323673862971588609", + "gitlab": "https://gitlab.libraryofcode.org/null", + "acknowledgements": ["Contributor"] + } +] diff --git a/src/configs/channels.json b/src/configs/channels.json index f7ac350..ae36ba6 100644 --- a/src/configs/channels.json +++ b/src/configs/channels.json @@ -1,6 +1,6 @@ -{ - "moderation": { - "modlogs": "446080867065135115", - "automod": "" - } -} +{ + "moderation": { + "modlogs": "446080867065135115", + "automod": "" + } +} diff --git a/src/configs/emotes.json b/src/configs/emotes.json index 3f0a0ab..1999f48 100644 --- a/src/configs/emotes.json +++ b/src/configs/emotes.json @@ -1,14 +1,14 @@ -{ - "whois": { - "titleAndDepartment": "<:loc:607695848612167700>", - "email": "<:email:699786452267040878>", - "gitlab": "<:gitlab:699788655748841492>", - "github": "<:github:699786469404835939>", - "bio": "<:bio:699786408193294416>" - }, - "statusMessages": { - "success": "<:modSuccess:578750988907970567>", - "loading": "", - "error": "<:modError:578750737920688128>" - } -} +{ + "whois": { + "titleAndDepartment": "<:loc:607695848612167700>", + "email": "<:email:699786452267040878>", + "gitlab": "<:gitlab:699788655748841492>", + "github": "<:github:699786469404835939>", + "bio": "<:bio:699786408193294416>" + }, + "statusMessages": { + "success": "<:modSuccess:578750988907970567>", + "loading": "", + "error": "<:modError:578750737920688128>" + } +} diff --git a/src/events/CommandHandler.ts b/src/events/CommandHandler.ts index a7d7733..2c7b792 100644 --- a/src/events/CommandHandler.ts +++ b/src/events/CommandHandler.ts @@ -1,32 +1,32 @@ -/* eslint-disable no-useless-return */ -import { Message, TextChannel, NewsChannel } from 'eris'; -import { Client, Event } from '../class'; - -export default class CommandHandler extends Event { - public client: Client; - - constructor(client: Client) { - super(client); - this.event = 'messageCreate'; - } - - public async run(message: Message) { - try { - if (message.author.bot) return; - if (message.content.indexOf(this.client.config.prefix) !== 0) return; - const noPrefix: string[] = message.content.slice(this.client.config.prefix.length).trim().split(/ +/g); - const resolved = await this.client.util.resolveCommand(noPrefix); - if (!resolved) return; - if (resolved.cmd.guildOnly && !(message.channel instanceof TextChannel || message.channel instanceof NewsChannel)) return; - if (!resolved.cmd.enabled) { message.channel.createMessage(`***${this.client.util.emojis.ERROR} This command has been disabled***`); return; } - if (!resolved.cmd.checkPermissions(message.member)) return; - if ((message.channel.type === 0) && !message.channel.guild.members.get(message.author.id)) { - message.channel.guild.members.add(await message.channel.guild.getRESTMember(message.author.id)); - } - this.client.util.signale.log(`User '${message.author.username}#${message.author.discriminator}' ran command '${resolved.cmd.name}' in '${message.channel.id}'.`); - await resolved.cmd.run(message, resolved.args); - } catch (err) { - this.client.util.handleError(err, message); - } - } -} +/* eslint-disable no-useless-return */ +import { Message, TextChannel, NewsChannel } from 'eris'; +import { Client, Event } from '../class'; + +export default class CommandHandler extends Event { + public client: Client; + + constructor(client: Client) { + super(client); + this.event = 'messageCreate'; + } + + public async run(message: Message) { + try { + if (message.author.bot) return; + if (message.content.indexOf(this.client.config.prefix) !== 0) return; + const noPrefix: string[] = message.content.slice(this.client.config.prefix.length).trim().split(/ +/g); + const resolved = await this.client.util.resolveCommand(noPrefix); + if (!resolved) return; + if (resolved.cmd.guildOnly && !(message.channel instanceof TextChannel || message.channel instanceof NewsChannel)) return; + if (!resolved.cmd.enabled) { message.channel.createMessage(`***${this.client.util.emojis.ERROR} This command has been disabled***`); return; } + if (!resolved.cmd.checkPermissions(message.member)) return; + if ((message.channel.type === 0) && !message.channel.guild.members.get(message.author.id)) { + message.channel.guild.members.add(await message.channel.guild.getRESTMember(message.author.id)); + } + this.client.util.signale.log(`User '${message.author.username}#${message.author.discriminator}' ran command '${resolved.cmd.name}' in '${message.channel.id}'.`); + await resolved.cmd.run(message, resolved.args); + } catch (err) { + this.client.util.handleError(err, message); + } + } +} diff --git a/src/events/index.ts b/src/events/index.ts index b57978e..4062731 100644 --- a/src/events/index.ts +++ b/src/events/index.ts @@ -1,2 +1,2 @@ -export { default as CommandHandler } from './CommandHandler'; -export { default as ready } from './ready'; +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 dd7f72b..10389ae 100644 --- a/src/events/ready.ts +++ b/src/events/ready.ts @@ -1,24 +1,24 @@ -import { Client, Event } from '../class'; - -export default class Ready extends Event { - public client: Client; - - constructor(client: Client) { - super(client); - this.event = 'ready'; - } - - public async run() { - this.client.util.signale.start(`${this.client.user.username} is now ready!\nServers: ${this.client.guilds.size}\nUsers: ${this.client.users.size}\n\nhttps://gitlab.libraryofcode.org/engineering/communityrelations`); - this.client.on('error', (err) => { - this.client.util.handleError(err); - }); - process.on('uncaughtException', (err) => { - this.client.util.handleError(err); - process.exit(1); - }); - process.on('unhandledRejection', (err: Error) => { - this.client.util.handleError(err); - }); - } -} +import { Client, Event } from '../class'; + +export default class Ready extends Event { + public client: Client; + + constructor(client: Client) { + super(client); + this.event = 'ready'; + } + + public async run() { + this.client.util.signale.start(`${this.client.user.username} is now ready!\nServers: ${this.client.guilds.size}\nUsers: ${this.client.users.size}\n\nhttps://gitlab.libraryofcode.org/engineering/communityrelations`); + this.client.on('error', (err) => { + this.client.util.handleError(err); + }); + process.on('uncaughtException', (err) => { + this.client.util.handleError(err); + process.exit(1); + }); + process.on('unhandledRejection', (err: Error) => { + this.client.util.handleError(err); + }); + } +} diff --git a/src/intervals/autoRelease.ts b/src/intervals/autoRelease.ts index 3d48e6c..3ca777c 100644 --- a/src/intervals/autoRelease.ts +++ b/src/intervals/autoRelease.ts @@ -1,24 +1,24 @@ -import { Client } from '../class'; - -let interval: NodeJS.Timeout; - -export default function checkLock(client: Client): NodeJS.Timeout { - interval = setInterval(async () => { - try { - const moderations = await client.db.Moderation.find(); - moderations.forEach(async (moderation) => { - if (!moderation.expiration) return; - if (moderation.expiration.processed) return; - if (new Date() > moderation.expiration.date) { - await moderation.updateOne({ 'expiration.processed': true }); - const moderator = client.guilds.get(client.config.guildID).members.get(moderation.moderatorID); - await client.util.moderation.unban(moderation.userID, moderator); - client.util.signale.complete(`Released member ${moderation.userID} | Queue date at ${moderation.expiration.date.toLocaleString('en-us')}`); - } - }); - } catch (error) { - await client.util.handleError(error); - } - }, 10000); - return interval; -} +import { Client } from '../class'; + +let interval: NodeJS.Timeout; + +export default function checkLock(client: Client): NodeJS.Timeout { + interval = setInterval(async () => { + try { + const moderations = await client.db.Moderation.find(); + moderations.forEach(async (moderation) => { + if (!moderation.expiration) return; + if (moderation.expiration.processed) return; + if (new Date() > moderation.expiration.date) { + await moderation.updateOne({ 'expiration.processed': true }); + const moderator = client.guilds.get(client.config.guildID).members.get(moderation.moderatorID); + await client.util.moderation.unban(moderation.userID, moderator); + client.util.signale.complete(`Released member ${moderation.userID} | Queue date at ${moderation.expiration.date.toLocaleString('en-us')}`); + } + }); + } catch (error) { + await client.util.handleError(error); + } + }, 10000); + return interval; +} diff --git a/src/intervals/departmentPager.ts b/src/intervals/departmentPager.ts index 9d0bc94..554cc4d 100644 --- a/src/intervals/departmentPager.ts +++ b/src/intervals/departmentPager.ts @@ -1,358 +1,358 @@ -/* eslint-disable no-continue */ -/* eslint-disable no-await-in-loop */ -import { Member, TextableChannel } from 'eris'; -import { Client } from '../class'; - -let interval: NodeJS.Timeout; - -async function setupDepartmentCodes(client: Client): Promise { - const directorPagers = await client.db.PagerNumber.findOne({ num: '00' }).lean().exec(); - const supervisorPagers = await client.db.PagerNumber.findOne({ num: '01' }).lean().exec(); - const technicianPagers = await client.db.PagerNumber.findOne({ num: '10' }).lean().exec(); - const moderatorPagers = await client.db.PagerNumber.findOne({ num: '20' }).lean().exec(); - const coreTeamPagers = await client.db.PagerNumber.findOne({ num: '21' }).lean().exec(); - const associatePagers = await client.db.PagerNumber.findOne({ num: '22' }).lean().exec(); - - if (!directorPagers) { - const setup = new client.db.PagerNumber({ - num: '00', - individualAssignID: '', - emailAddresses: [], - discordIDs: [], - }); - await setup.save(); - } - if (!supervisorPagers) { - const setup = new client.db.PagerNumber({ - num: '01', - individualAssignID: '', - emailAddresses: [], - discordIDs: [], - }); - await setup.save(); - } - if (!technicianPagers) { - const setup = new client.db.PagerNumber({ - num: '10', - individualAssignID: '', - emailAddresses: [], - discordIDs: [], - }); - setup.save(); - } - if (!moderatorPagers) { - const setup = new client.db.PagerNumber({ - num: '20', - individualAssignID: '', - emailAddresses: [], - discordIDs: [], - }); - await setup.save(); - } - if (!coreTeamPagers) { - const setup = new client.db.PagerNumber({ - num: '21', - individualAssignID: '', - emailAddresses: [], - discordIDs: [], - }); - await setup.save(); - } - if (!associatePagers) { - const setup = new client.db.PagerNumber({ - num: '22', - individualAssignID: '', - emailAddresses: [], - discordIDs: [], - }); - await setup.save(); - } -} - -function logNewPager(client: Client, num: string, member: Member): void { - const channel = client.guilds.get(client.config.guildID).channels.get('722636436716781619'); - channel.createMessage(`__**'${member.user.username}#${member.user.discriminator}' assigned to pager number '${num}'.**__`); -} - -export default function departmentPager(client: Client): NodeJS.Timeout { - setupDepartmentCodes(client); - interval = setInterval(async () => { - const acknowledgements = require(`${__dirname}/../configs/acknowledgements.json`); - function resolveStaffInformation(id: string) { - return acknowledgements.find((m) => m.id === id); - } - // await client.guilds.get(client.config.guildID).fetchAllMembers(); - const { members } = client.guilds.get(client.config.guildID); - - for (const member of members.values()) { - const pager = await client.db.PagerNumber.findOne({ individualAssignID: member.id }).lean().exec(); - if (!pager) continue; - if (pager.num.startsWith('00') && !member.roles.includes('662163685439045632')) { - client.db.PagerNumber.deleteOne({ num: pager.num }); - client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`); - } else if (pager.num.startsWith('01') && !member.roles.includes('701454855952138300')) { - client.db.PagerNumber.deleteOne({ num: pager.num }); - client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`); - } else if (pager.num.startsWith('10') && !member.roles.includes('701454780828221450')) { - client.db.PagerNumber.deleteOne({ num: pager.num }); - client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`); - } else if (pager.num.startsWith('20') && !member.roles.includes('455972169449734144')) { - client.db.PagerNumber.deleteOne({ num: pager.num }); - client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`); - } else if (pager.num.startsWith('21') && !member.roles.includes('453689940140883988')) { - client.db.PagerNumber.deleteOne({ num: pager.num }); - client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`); - } else if (pager.num.startsWith('22') && !member.roles.includes('701481967149121627')) { - client.db.PagerNumber.deleteOne({ num: pager.num }); - client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`); - } - } - - // const takenPagers = new Set(); - - for (const member of members.values()) { - let pager = await client.db.PagerNumber.findOne({ individualAssignID: member.id }).lean().exec(); - // Directors - if (!pager && member.roles.includes('662163685439045632')) { - let randomPagerNumber: string; - let status = true; - // eslint-disable-next-line no-constant-condition - while (status) { - randomPagerNumber = `00${String(Math.floor(Math.random() * 9) + 1)}`; - const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber }); - if (check) status = false; - if (check?.num !== randomPagerNumber) status = false; - } - const acknowledgement = resolveStaffInformation(member.id); - if (!acknowledgement || !acknowledgement.emailAddress) return; - const newNumber = new client.db.PagerNumber({ - num: randomPagerNumber, - individualAssignID: member.id, - emailAddresses: [acknowledgement.emailAddress], - discordIDs: [member.id], - }); - if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return; - pager = await newNumber.save(); - logNewPager(client, randomPagerNumber, member); - client.getDMChannel(member.id).then((chan) => { - chan.createMessage(`__**Pager Number Creation**__\nYour individual pager number has been automatically created. Your number (PN) is ${randomPagerNumber}.`); - }); - } else if (!pager && member.roles.includes('701454855952138300')) { - // Supervisors - let randomPagerNumber: string; - let status = true; - // eslint-disable-next-line no-constant-condition - while (status) { - randomPagerNumber = `01${String(Math.floor(Math.random() * 9) + 1)}`; - const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber }); - if (check) status = false; - if (check?.num !== randomPagerNumber) status = false; - } - const acknowledgement = resolveStaffInformation(member.id); - if (!acknowledgement || !acknowledgement.emailAddress) return; - const newNumber = new client.db.PagerNumber({ - num: randomPagerNumber, - individualAssignID: member.id, - emailAddresses: [acknowledgement.emailAddress], - discordIDs: [member.id], - }); - if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return; - logNewPager(client, randomPagerNumber, member); - pager = await newNumber.save(); - client.getDMChannel(member.id).then((chan) => { - chan.createMessage(`__**Pager Number Creation**__\nYour individual pager number has been automatically created. Your number (PN) is ${randomPagerNumber}.`); - }); - } else if (!pager && member.roles.includes('701454780828221450')) { - // Technicians - let randomPagerNumber: string; - let status = true; - // eslint-disable-next-line no-constant-condition - while (status) { - randomPagerNumber = `10${String(Math.floor(Math.random() * 99) + 1)}`; - if (randomPagerNumber.length === 3) randomPagerNumber = `${randomPagerNumber}0`; - const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber }); - if (check) status = false; - if (check?.num !== randomPagerNumber) status = false; - } - const acknowledgement = resolveStaffInformation(member.id); - if (!acknowledgement || !acknowledgement.emailAddress) return; - const newNumber = new client.db.PagerNumber({ - num: randomPagerNumber, - individualAssignID: member.id, - emailAddresses: [acknowledgement.emailAddress], - discordIDs: [member.id], - }); - if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return; - logNewPager(client, randomPagerNumber, member); - pager = await newNumber.save(); - client.getDMChannel(member.id).then((chan) => { - chan.createMessage(`__**Pager Number Creation**__\nYour individual pager number has been automatically created. Your number (PN) is ${randomPagerNumber}.`); - }); - } else if (!pager && member.roles.includes('455972169449734144')) { - // Moderators - let randomPagerNumber: string; - let status = true; - // eslint-disable-next-line no-constant-condition - while (status) { - randomPagerNumber = `20${String(Math.floor(Math.random() * 99) + 1)}`; - if (randomPagerNumber.length === 3) randomPagerNumber = `${randomPagerNumber}0`; - const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber }); - if (check) status = false; - if (check?.num !== randomPagerNumber) status = false; - } - const acknowledgement = resolveStaffInformation(member.id); - if (!acknowledgement || !acknowledgement.emailAddress) return; - const newNumber = new client.db.PagerNumber({ - num: randomPagerNumber, - individualAssignID: member.id, - emailAddresses: [acknowledgement.emailAddress], - discordIDs: [member.id], - }); - if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return; - logNewPager(client, randomPagerNumber, member); - pager = await newNumber.save(); - client.getDMChannel(member.id).then((chan) => { - chan.createMessage(`__**Pager Number Creation**__\nYour individual pager number has been automatically created. Your number (PN) is ${randomPagerNumber}.`); - }); - } else if (!pager && member.roles.includes('453689940140883988')) { - // Core Team - let randomPagerNumber: string; - let status = true; - // eslint-disable-next-line no-constant-condition - while (status) { - randomPagerNumber = `21${String(Math.floor(Math.random() * 999) + 1)}`; - if (randomPagerNumber.length === 4) randomPagerNumber = `${randomPagerNumber}0`; - const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber }); - if (check) status = false; - if (check?.num !== randomPagerNumber) status = false; - } - const acknowledgement = resolveStaffInformation(member.id); - if (!acknowledgement || !acknowledgement.emailAddress) return; - const newNumber = new client.db.PagerNumber({ - num: randomPagerNumber, - individualAssignID: member.id, - emailAddresses: [acknowledgement.emailAddress], - discordIDs: [member.id], - }); - if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return; - logNewPager(client, randomPagerNumber, member); - pager = await newNumber.save(); - client.getDMChannel(member.id).then((chan) => { - chan.createMessage(`__**Pager Number Creation**__\nYour individual pager number has been automatically created. Your number (PN) is ${randomPagerNumber}.`); - }); - } else if (!pager && member.roles.includes('701481967149121627')) { - // Associates - let randomPagerNumber: string; - let status = true; - // eslint-disable-next-line no-constant-condition - while (status) { - randomPagerNumber = `22${String(Math.floor(Math.random() * 999) + 1)}`; - if (randomPagerNumber.length === 4) randomPagerNumber = `${randomPagerNumber}0`; - const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber }); - if (check) status = false; - if (check?.num !== randomPagerNumber) status = false; - } - const acknowledgement = resolveStaffInformation(member.id); - if (!acknowledgement || !acknowledgement.emailAddress) return; - const newNumber = new client.db.PagerNumber({ - num: randomPagerNumber, - individualAssignID: member.id, - emailAddresses: [acknowledgement.emailAddress], - discordIDs: [member.id], - }); - if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return; - logNewPager(client, randomPagerNumber, member); - pager = await newNumber.save(); - client.getDMChannel(member.id).then((chan) => { - chan.createMessage(`__**Pager Number Creation**__\nYour individual pager number has been automatically created. Your number (PN) is ${randomPagerNumber}.`); - }); - } - } - - // Associates - const associatePagers = await client.db.PagerNumber.findOne({ num: '22' }); - for (const member of members.values()) { - if (member.roles.includes('701481967149121627') && !associatePagers.discordIDs.includes(member.id)) { - await associatePagers.updateOne({ $addToSet: { discordIDs: member.id } }); - const acknowledgement = resolveStaffInformation(member.id); - if (acknowledgement?.emailAddress) await associatePagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } }); - } - if (!member.roles.includes('701481967149121627') && associatePagers.discordIDs.includes(member.id)) { - await associatePagers.updateOne({ $pull: { discordIDs: member.id } }); - const acknowledgement = resolveStaffInformation(member.id); - if (acknowledgement?.emailAddress) await associatePagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } }); - } - } - // Core Team - const coreTeamPagers = await client.db.PagerNumber.findOne({ num: '21' }); - for (const member of members.values()) { - if (member.roles.includes('453689940140883988') && !coreTeamPagers.discordIDs.includes(member.id)) { - await coreTeamPagers.updateOne({ $addToSet: { discordIDs: member.id } }); - const acknowledgement = resolveStaffInformation(member.id); - if (acknowledgement?.emailAddress) await coreTeamPagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } }); - } - if (!member.roles.includes('453689940140883988') && coreTeamPagers.discordIDs.includes(member.id)) { - await coreTeamPagers.updateOne({ $pull: { discordIDs: member.id } }); - const acknowledgement = resolveStaffInformation(member.id); - if (acknowledgement?.emailAddress) await coreTeamPagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } }); - } - } - // Moderator - const moderatorPagers = await client.db.PagerNumber.findOne({ num: '20' }); - for (const member of members.values()) { - if (member.roles.includes('455972169449734144') && !moderatorPagers.discordIDs.includes(member.id)) { - await moderatorPagers.updateOne({ $addToSet: { discordIDs: member.id } }); - const acknowledgement = resolveStaffInformation(member.id); - if (acknowledgement?.emailAddress) await moderatorPagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } }); - } - if (!member.roles.includes('455972169449734144') && moderatorPagers.discordIDs.includes(member.id)) { - await moderatorPagers.updateOne({ $pull: { discordIDs: member.id } }); - const acknowledgement = resolveStaffInformation(member.id); - if (acknowledgement?.emailAddress) await moderatorPagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } }); - } - } - // Technician - const technicianPagers = await client.db.PagerNumber.findOne({ num: '10' }); - for (const member of members.values()) { - if (member.roles.includes('701454780828221450') && !technicianPagers.discordIDs.includes(member.id)) { - await technicianPagers.updateOne({ $addToSet: { discordIDs: member.id } }); - const acknowledgement = resolveStaffInformation(member.id); - if (acknowledgement?.emailAddress) await technicianPagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } }); - } - if (!member.roles.includes('701454780828221450') && technicianPagers.discordIDs.includes(member.id)) { - await technicianPagers.updateOne({ $pull: { discordIDs: member.id } }); - const acknowledgement = resolveStaffInformation(member.id); - if (acknowledgement?.emailAddress) await technicianPagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } }); - } - } - // Supervisor - const supervisorPagers = await client.db.PagerNumber.findOne({ num: '01' }); - for (const member of members.values()) { - if (member.roles.includes('701454855952138300') && !supervisorPagers.discordIDs.includes(member.id)) { - await supervisorPagers.updateOne({ $addToSet: { discordIDs: member.id } }); - const acknowledgement = resolveStaffInformation(member.id); - if (acknowledgement?.emailAddress) await supervisorPagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } }); - } - if (!member.roles.includes('701454855952138300') && supervisorPagers.discordIDs.includes(member.id)) { - await supervisorPagers.updateOne({ $pull: { discordIDs: member.id } }); - const acknowledgement = resolveStaffInformation(member.id); - if (acknowledgement?.emailAddress) await supervisorPagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } }); - } - } - // Board of Directors - const directorPagers = await client.db.PagerNumber.findOne({ num: '00' }); - for (const member of members.values()) { - if (member.roles.includes('662163685439045632') && !directorPagers.discordIDs.includes(member.id)) { - await directorPagers.updateOne({ $addToSet: { discordIDs: member.id } }); - const acknowledgement = resolveStaffInformation(member.id); - if (acknowledgement?.emailAddress) await directorPagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } }); - } - if (!member.roles.includes('662163685439045632') && directorPagers.discordIDs.includes(member.id)) { - await directorPagers.updateOne({ $pull: { discordIDs: member.id } }); - const acknowledgement = resolveStaffInformation(member.id); - if (acknowledgement?.emailAddress) await directorPagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } }); - } - } - }, 300000); - return interval; -} +/* eslint-disable no-continue */ +/* eslint-disable no-await-in-loop */ +import { Member, TextableChannel } from 'eris'; +import { Client } from '../class'; + +let interval: NodeJS.Timeout; + +async function setupDepartmentCodes(client: Client): Promise { + const directorPagers = await client.db.PagerNumber.findOne({ num: '00' }).lean().exec(); + const supervisorPagers = await client.db.PagerNumber.findOne({ num: '01' }).lean().exec(); + const technicianPagers = await client.db.PagerNumber.findOne({ num: '10' }).lean().exec(); + const moderatorPagers = await client.db.PagerNumber.findOne({ num: '20' }).lean().exec(); + const coreTeamPagers = await client.db.PagerNumber.findOne({ num: '21' }).lean().exec(); + const associatePagers = await client.db.PagerNumber.findOne({ num: '22' }).lean().exec(); + + if (!directorPagers) { + const setup = new client.db.PagerNumber({ + num: '00', + individualAssignID: '', + emailAddresses: [], + discordIDs: [], + }); + await setup.save(); + } + if (!supervisorPagers) { + const setup = new client.db.PagerNumber({ + num: '01', + individualAssignID: '', + emailAddresses: [], + discordIDs: [], + }); + await setup.save(); + } + if (!technicianPagers) { + const setup = new client.db.PagerNumber({ + num: '10', + individualAssignID: '', + emailAddresses: [], + discordIDs: [], + }); + setup.save(); + } + if (!moderatorPagers) { + const setup = new client.db.PagerNumber({ + num: '20', + individualAssignID: '', + emailAddresses: [], + discordIDs: [], + }); + await setup.save(); + } + if (!coreTeamPagers) { + const setup = new client.db.PagerNumber({ + num: '21', + individualAssignID: '', + emailAddresses: [], + discordIDs: [], + }); + await setup.save(); + } + if (!associatePagers) { + const setup = new client.db.PagerNumber({ + num: '22', + individualAssignID: '', + emailAddresses: [], + discordIDs: [], + }); + await setup.save(); + } +} + +function logNewPager(client: Client, num: string, member: Member): void { + const channel = client.guilds.get(client.config.guildID).channels.get('722636436716781619'); + channel.createMessage(`__**'${member.user.username}#${member.user.discriminator}' assigned to pager number '${num}'.**__`); +} + +export default function departmentPager(client: Client): NodeJS.Timeout { + setupDepartmentCodes(client); + interval = setInterval(async () => { + const acknowledgements = require(`${__dirname}/../configs/acknowledgements.json`); + function resolveStaffInformation(id: string) { + return acknowledgements.find((m) => m.id === id); + } + // await client.guilds.get(client.config.guildID).fetchAllMembers(); + const { members } = client.guilds.get(client.config.guildID); + + for (const member of members.values()) { + const pager = await client.db.PagerNumber.findOne({ individualAssignID: member.id }).lean().exec(); + if (!pager) continue; + if (pager.num.startsWith('00') && !member.roles.includes('662163685439045632')) { + client.db.PagerNumber.deleteOne({ num: pager.num }); + client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`); + } else if (pager.num.startsWith('01') && !member.roles.includes('701454855952138300')) { + client.db.PagerNumber.deleteOne({ num: pager.num }); + client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`); + } else if (pager.num.startsWith('10') && !member.roles.includes('701454780828221450')) { + client.db.PagerNumber.deleteOne({ num: pager.num }); + client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`); + } else if (pager.num.startsWith('20') && !member.roles.includes('455972169449734144')) { + client.db.PagerNumber.deleteOne({ num: pager.num }); + client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`); + } else if (pager.num.startsWith('21') && !member.roles.includes('453689940140883988')) { + client.db.PagerNumber.deleteOne({ num: pager.num }); + client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`); + } else if (pager.num.startsWith('22') && !member.roles.includes('701481967149121627')) { + client.db.PagerNumber.deleteOne({ num: pager.num }); + client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`); + } + } + + // const takenPagers = new Set(); + + for (const member of members.values()) { + let pager = await client.db.PagerNumber.findOne({ individualAssignID: member.id }).lean().exec(); + // Directors + if (!pager && member.roles.includes('662163685439045632')) { + let randomPagerNumber: string; + let status = true; + // eslint-disable-next-line no-constant-condition + while (status) { + randomPagerNumber = `00${String(Math.floor(Math.random() * 9) + 1)}`; + const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber }); + if (check) status = false; + if (check?.num !== randomPagerNumber) status = false; + } + const acknowledgement = resolveStaffInformation(member.id); + if (!acknowledgement || !acknowledgement.emailAddress) return; + const newNumber = new client.db.PagerNumber({ + num: randomPagerNumber, + individualAssignID: member.id, + emailAddresses: [acknowledgement.emailAddress], + discordIDs: [member.id], + }); + if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return; + pager = await newNumber.save(); + logNewPager(client, randomPagerNumber, member); + client.getDMChannel(member.id).then((chan) => { + chan.createMessage(`__**Pager Number Creation**__\nYour individual pager number has been automatically created. Your number (PN) is ${randomPagerNumber}.`); + }); + } else if (!pager && member.roles.includes('701454855952138300')) { + // Supervisors + let randomPagerNumber: string; + let status = true; + // eslint-disable-next-line no-constant-condition + while (status) { + randomPagerNumber = `01${String(Math.floor(Math.random() * 9) + 1)}`; + const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber }); + if (check) status = false; + if (check?.num !== randomPagerNumber) status = false; + } + const acknowledgement = resolveStaffInformation(member.id); + if (!acknowledgement || !acknowledgement.emailAddress) return; + const newNumber = new client.db.PagerNumber({ + num: randomPagerNumber, + individualAssignID: member.id, + emailAddresses: [acknowledgement.emailAddress], + discordIDs: [member.id], + }); + if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return; + logNewPager(client, randomPagerNumber, member); + pager = await newNumber.save(); + client.getDMChannel(member.id).then((chan) => { + chan.createMessage(`__**Pager Number Creation**__\nYour individual pager number has been automatically created. Your number (PN) is ${randomPagerNumber}.`); + }); + } else if (!pager && member.roles.includes('701454780828221450')) { + // Technicians + let randomPagerNumber: string; + let status = true; + // eslint-disable-next-line no-constant-condition + while (status) { + randomPagerNumber = `10${String(Math.floor(Math.random() * 99) + 1)}`; + if (randomPagerNumber.length === 3) randomPagerNumber = `${randomPagerNumber}0`; + const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber }); + if (check) status = false; + if (check?.num !== randomPagerNumber) status = false; + } + const acknowledgement = resolveStaffInformation(member.id); + if (!acknowledgement || !acknowledgement.emailAddress) return; + const newNumber = new client.db.PagerNumber({ + num: randomPagerNumber, + individualAssignID: member.id, + emailAddresses: [acknowledgement.emailAddress], + discordIDs: [member.id], + }); + if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return; + logNewPager(client, randomPagerNumber, member); + pager = await newNumber.save(); + client.getDMChannel(member.id).then((chan) => { + chan.createMessage(`__**Pager Number Creation**__\nYour individual pager number has been automatically created. Your number (PN) is ${randomPagerNumber}.`); + }); + } else if (!pager && member.roles.includes('455972169449734144')) { + // Moderators + let randomPagerNumber: string; + let status = true; + // eslint-disable-next-line no-constant-condition + while (status) { + randomPagerNumber = `20${String(Math.floor(Math.random() * 99) + 1)}`; + if (randomPagerNumber.length === 3) randomPagerNumber = `${randomPagerNumber}0`; + const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber }); + if (check) status = false; + if (check?.num !== randomPagerNumber) status = false; + } + const acknowledgement = resolveStaffInformation(member.id); + if (!acknowledgement || !acknowledgement.emailAddress) return; + const newNumber = new client.db.PagerNumber({ + num: randomPagerNumber, + individualAssignID: member.id, + emailAddresses: [acknowledgement.emailAddress], + discordIDs: [member.id], + }); + if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return; + logNewPager(client, randomPagerNumber, member); + pager = await newNumber.save(); + client.getDMChannel(member.id).then((chan) => { + chan.createMessage(`__**Pager Number Creation**__\nYour individual pager number has been automatically created. Your number (PN) is ${randomPagerNumber}.`); + }); + } else if (!pager && member.roles.includes('453689940140883988')) { + // Core Team + let randomPagerNumber: string; + let status = true; + // eslint-disable-next-line no-constant-condition + while (status) { + randomPagerNumber = `21${String(Math.floor(Math.random() * 999) + 1)}`; + if (randomPagerNumber.length === 4) randomPagerNumber = `${randomPagerNumber}0`; + const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber }); + if (check) status = false; + if (check?.num !== randomPagerNumber) status = false; + } + const acknowledgement = resolveStaffInformation(member.id); + if (!acknowledgement || !acknowledgement.emailAddress) return; + const newNumber = new client.db.PagerNumber({ + num: randomPagerNumber, + individualAssignID: member.id, + emailAddresses: [acknowledgement.emailAddress], + discordIDs: [member.id], + }); + if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return; + logNewPager(client, randomPagerNumber, member); + pager = await newNumber.save(); + client.getDMChannel(member.id).then((chan) => { + chan.createMessage(`__**Pager Number Creation**__\nYour individual pager number has been automatically created. Your number (PN) is ${randomPagerNumber}.`); + }); + } else if (!pager && member.roles.includes('701481967149121627')) { + // Associates + let randomPagerNumber: string; + let status = true; + // eslint-disable-next-line no-constant-condition + while (status) { + randomPagerNumber = `22${String(Math.floor(Math.random() * 999) + 1)}`; + if (randomPagerNumber.length === 4) randomPagerNumber = `${randomPagerNumber}0`; + const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber }); + if (check) status = false; + if (check?.num !== randomPagerNumber) status = false; + } + const acknowledgement = resolveStaffInformation(member.id); + if (!acknowledgement || !acknowledgement.emailAddress) return; + const newNumber = new client.db.PagerNumber({ + num: randomPagerNumber, + individualAssignID: member.id, + emailAddresses: [acknowledgement.emailAddress], + discordIDs: [member.id], + }); + if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return; + logNewPager(client, randomPagerNumber, member); + pager = await newNumber.save(); + client.getDMChannel(member.id).then((chan) => { + chan.createMessage(`__**Pager Number Creation**__\nYour individual pager number has been automatically created. Your number (PN) is ${randomPagerNumber}.`); + }); + } + } + + // Associates + const associatePagers = await client.db.PagerNumber.findOne({ num: '22' }); + for (const member of members.values()) { + if (member.roles.includes('701481967149121627') && !associatePagers.discordIDs.includes(member.id)) { + await associatePagers.updateOne({ $addToSet: { discordIDs: member.id } }); + const acknowledgement = resolveStaffInformation(member.id); + if (acknowledgement?.emailAddress) await associatePagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } }); + } + if (!member.roles.includes('701481967149121627') && associatePagers.discordIDs.includes(member.id)) { + await associatePagers.updateOne({ $pull: { discordIDs: member.id } }); + const acknowledgement = resolveStaffInformation(member.id); + if (acknowledgement?.emailAddress) await associatePagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } }); + } + } + // Core Team + const coreTeamPagers = await client.db.PagerNumber.findOne({ num: '21' }); + for (const member of members.values()) { + if (member.roles.includes('453689940140883988') && !coreTeamPagers.discordIDs.includes(member.id)) { + await coreTeamPagers.updateOne({ $addToSet: { discordIDs: member.id } }); + const acknowledgement = resolveStaffInformation(member.id); + if (acknowledgement?.emailAddress) await coreTeamPagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } }); + } + if (!member.roles.includes('453689940140883988') && coreTeamPagers.discordIDs.includes(member.id)) { + await coreTeamPagers.updateOne({ $pull: { discordIDs: member.id } }); + const acknowledgement = resolveStaffInformation(member.id); + if (acknowledgement?.emailAddress) await coreTeamPagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } }); + } + } + // Moderator + const moderatorPagers = await client.db.PagerNumber.findOne({ num: '20' }); + for (const member of members.values()) { + if (member.roles.includes('455972169449734144') && !moderatorPagers.discordIDs.includes(member.id)) { + await moderatorPagers.updateOne({ $addToSet: { discordIDs: member.id } }); + const acknowledgement = resolveStaffInformation(member.id); + if (acknowledgement?.emailAddress) await moderatorPagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } }); + } + if (!member.roles.includes('455972169449734144') && moderatorPagers.discordIDs.includes(member.id)) { + await moderatorPagers.updateOne({ $pull: { discordIDs: member.id } }); + const acknowledgement = resolveStaffInformation(member.id); + if (acknowledgement?.emailAddress) await moderatorPagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } }); + } + } + // Technician + const technicianPagers = await client.db.PagerNumber.findOne({ num: '10' }); + for (const member of members.values()) { + if (member.roles.includes('701454780828221450') && !technicianPagers.discordIDs.includes(member.id)) { + await technicianPagers.updateOne({ $addToSet: { discordIDs: member.id } }); + const acknowledgement = resolveStaffInformation(member.id); + if (acknowledgement?.emailAddress) await technicianPagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } }); + } + if (!member.roles.includes('701454780828221450') && technicianPagers.discordIDs.includes(member.id)) { + await technicianPagers.updateOne({ $pull: { discordIDs: member.id } }); + const acknowledgement = resolveStaffInformation(member.id); + if (acknowledgement?.emailAddress) await technicianPagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } }); + } + } + // Supervisor + const supervisorPagers = await client.db.PagerNumber.findOne({ num: '01' }); + for (const member of members.values()) { + if (member.roles.includes('701454855952138300') && !supervisorPagers.discordIDs.includes(member.id)) { + await supervisorPagers.updateOne({ $addToSet: { discordIDs: member.id } }); + const acknowledgement = resolveStaffInformation(member.id); + if (acknowledgement?.emailAddress) await supervisorPagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } }); + } + if (!member.roles.includes('701454855952138300') && supervisorPagers.discordIDs.includes(member.id)) { + await supervisorPagers.updateOne({ $pull: { discordIDs: member.id } }); + const acknowledgement = resolveStaffInformation(member.id); + if (acknowledgement?.emailAddress) await supervisorPagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } }); + } + } + // Board of Directors + const directorPagers = await client.db.PagerNumber.findOne({ num: '00' }); + for (const member of members.values()) { + if (member.roles.includes('662163685439045632') && !directorPagers.discordIDs.includes(member.id)) { + await directorPagers.updateOne({ $addToSet: { discordIDs: member.id } }); + const acknowledgement = resolveStaffInformation(member.id); + if (acknowledgement?.emailAddress) await directorPagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } }); + } + if (!member.roles.includes('662163685439045632') && directorPagers.discordIDs.includes(member.id)) { + await directorPagers.updateOne({ $pull: { discordIDs: member.id } }); + const acknowledgement = resolveStaffInformation(member.id); + if (acknowledgement?.emailAddress) await directorPagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } }); + } + } + }, 300000); + return interval; +} diff --git a/src/intervals/fetchMembers.ts b/src/intervals/fetchMembers.ts index 54268e5..fcc1862 100644 --- a/src/intervals/fetchMembers.ts +++ b/src/intervals/fetchMembers.ts @@ -1,11 +1,11 @@ -import { Client } from '../class'; - -let interval: NodeJS.Timeout; - -export default async function fetchMembers(client: Client): Promise { - await client.guilds.get(client.config.guildID)?.fetchAllMembers(); - interval = setInterval(async () => { - // await client.guilds.get(client.config.guildID).fetchAllMembers(); - }, 1800000); - return interval; -} +import { Client } from '../class'; + +let interval: NodeJS.Timeout; + +export default async function fetchMembers(client: Client): Promise { + await client.guilds.get(client.config.guildID)?.fetchAllMembers(); + interval = setInterval(async () => { + // await client.guilds.get(client.config.guildID).fetchAllMembers(); + }, 1800000); + return interval; +} diff --git a/src/main.ts b/src/main.ts index 55dd14a..4677d4b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,7 +3,6 @@ */ import { parse } from 'yaml'; import { promises as fs } from 'fs'; -import { TextChannel } from 'eris'; import { Client } from './class'; import * as eventFiles from './events'; import * as commandFiles from './commands'; diff --git a/src/models/Member.ts b/src/models/Member.ts index d69a7c8..e7023b5 100644 --- a/src/models/Member.ts +++ b/src/models/Member.ts @@ -1,19 +1,19 @@ -import { Document, Schema, model } from 'mongoose'; - -export interface MemberInterface extends Document { - userID: string - additional: { - langs: ['js', 'py', 'rb', 'ts', 'rs', 'go', 'cfam', 'csharp', 'swift', 'java', 'kt', 'asm'], - operatingSystems: ['arch', 'deb', 'cent', 'fedora', 'manjaro', 'mdarwin', 'redhat', 'ubuntu', 'win'], - }, -} - -const Member: Schema = new Schema({ - userID: String, - additional: { - langs: Array, - operatingSystems: Array, - }, -}); - -export default model('Member', Member); +import { Document, Schema, model } from 'mongoose'; + +export interface MemberInterface extends Document { + userID: string + additional: { + langs: ['js', 'py', 'rb', 'ts', 'rs', 'go', 'cfam', 'csharp', 'swift', 'java', 'kt', 'asm'], + operatingSystems: ['arch', 'deb', 'cent', 'fedora', 'manjaro', 'mdarwin', 'redhat', 'ubuntu', 'win'], + }, +} + +const Member: Schema = new Schema({ + userID: String, + additional: { + langs: Array, + operatingSystems: Array, + }, +}); + +export default model('Member', Member); diff --git a/src/models/Moderation.ts b/src/models/Moderation.ts index 990519f..1e12c8e 100644 --- a/src/models/Moderation.ts +++ b/src/models/Moderation.ts @@ -1,37 +1,37 @@ -import { Document, Schema, model } from 'mongoose'; - -export interface ModerationInterface extends Document { - userID: string, - logID: string, - moderatorID: string, - reason: string, - /** - * @field 0 - Warn - * @field 1 - Unmute - * @field 2 - Mute - * @field 3 - Kick - * @field 4 - Unban - * @field 5 - Ban - */ - type: 0 | 1 | 2 | 3 | 4 | 5 - date: Date, - expiration: { - date: Date, - processed: boolean - } -} - -const Moderation: Schema = new Schema({ - userID: String, - logID: String, - moderatorID: String, - reason: String, - type: Number, - date: Date, - expiration: { - date: Date, - processed: Boolean, - }, -}); - -export default model('Moderation', Moderation); +import { Document, Schema, model } from 'mongoose'; + +export interface ModerationInterface extends Document { + userID: string, + logID: string, + moderatorID: string, + reason: string, + /** + * @field 0 - Warn + * @field 1 - Unmute + * @field 2 - Mute + * @field 3 - Kick + * @field 4 - Unban + * @field 5 - Ban + */ + type: 0 | 1 | 2 | 3 | 4 | 5 + date: Date, + expiration: { + date: Date, + processed: boolean + } +} + +const Moderation: Schema = new Schema({ + userID: String, + logID: String, + moderatorID: String, + reason: String, + type: Number, + date: Date, + expiration: { + date: Date, + processed: Boolean, + }, +}); + +export default model('Moderation', Moderation); diff --git a/src/models/PagerNumber.ts b/src/models/PagerNumber.ts index e9ef603..f250c4d 100644 --- a/src/models/PagerNumber.ts +++ b/src/models/PagerNumber.ts @@ -1,26 +1,26 @@ -import { Document, Schema, model } from 'mongoose'; - -export interface PagerNumberRaw { - num: string, - // This field will be "" if the pager number doesn't belong to an individual user - individualAssignID: string, - emailAddresses: string[], - discordIDs: string[], -} - -export interface PagerNumberInterface extends Document { - num: string, - // This field will be "" if the pager number doesn't belong to an individual user - individualAssignID: string, - emailAddresses: string[], - discordIDs: string[], -} - -const PagerNumber: Schema = new Schema({ - num: String, - individualAssignID: String, - emailAddresses: Array, - discordIDs: Array, -}); - -export default model('PagerNumber', PagerNumber); +import { Document, Schema, model } from 'mongoose'; + +export interface PagerNumberRaw { + num: string, + // This field will be "" if the pager number doesn't belong to an individual user + individualAssignID: string, + emailAddresses: string[], + discordIDs: string[], +} + +export interface PagerNumberInterface extends Document { + num: string, + // This field will be "" if the pager number doesn't belong to an individual user + individualAssignID: string, + emailAddresses: string[], + discordIDs: string[], +} + +const PagerNumber: Schema = new Schema({ + num: String, + individualAssignID: String, + emailAddresses: Array, + discordIDs: Array, +}); + +export default model('PagerNumber', PagerNumber); diff --git a/src/models/Rank.ts b/src/models/Rank.ts new file mode 100644 index 0000000..f887528 --- /dev/null +++ b/src/models/Rank.ts @@ -0,0 +1,17 @@ +import { Document, Schema, model } from 'mongoose'; + +export interface RankInterface extends Document { + name: string, + roleID: string, + permissions: string[], + description: string, +} + +const Rank: Schema = new Schema({ + name: String, + roleID: String, + permissions: Array, + description: String, +}); + +export default model('Rank', Rank); diff --git a/src/models/Redirect.ts b/src/models/Redirect.ts index f17b77f..cc080d6 100644 --- a/src/models/Redirect.ts +++ b/src/models/Redirect.ts @@ -1,21 +1,21 @@ -import { Document, Schema, model } from 'mongoose'; - -export interface RedirectInterface extends Document { - key: string, - to: string, - visitedCount: number, -} - -export interface RedirectRaw { - key: string, - to: string, - visitedCount: number, -} - -const Redirect: Schema = new Schema({ - key: String, - to: String, - visitedCount: Number, -}); - -export default model('Redirect', Redirect); +import { Document, Schema, model } from 'mongoose'; + +export interface RedirectInterface extends Document { + key: string, + to: string, + visitedCount: number, +} + +export interface RedirectRaw { + key: string, + to: string, + visitedCount: number, +} + +const Redirect: Schema = new Schema({ + key: String, + to: String, + visitedCount: Number, +}); + +export default model('Redirect', Redirect); diff --git a/src/models/index.ts b/src/models/index.ts index 1b78410..c296c03 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -1,4 +1,5 @@ -export { default as Member, MemberInterface } from './Member'; -export { default as Moderation, ModerationInterface } from './Moderation'; -export { default as PagerNumber, PagerNumberInterface, PagerNumberRaw } from './PagerNumber'; -export { default as Redirect, RedirectInterface, RedirectRaw } from './Redirect'; +export { default as Member, MemberInterface } from './Member'; +export { default as Moderation, ModerationInterface } from './Moderation'; +export { default as PagerNumber, PagerNumberInterface, PagerNumberRaw } from './PagerNumber'; +export { default as Rank, RankInterface } from './Rank'; +export { default as Redirect, RedirectInterface, RedirectRaw } from './Redirect';