add rank commands

merge-requests/15/head
Matthew 2020-07-06 03:15:06 -04:00
parent e39ebfd140
commit cd4805dbae
No known key found for this signature in database
GPG Key ID: 210AF32ADE3B5C4B
35 changed files with 1888 additions and 1730 deletions

View File

@ -1,5 +1,5 @@
import locsh from './loc.sh/main'; import locsh from './loc.sh/main';
export default { export default {
'loc.sh': locsh, 'loc.sh': locsh,
}; };

View File

@ -1,6 +1,6 @@
import { Server, ServerManagement } from '../../class'; import { Server, ServerManagement } from '../../class';
export default (management: ServerManagement) => { export default (management: ServerManagement) => {
const server = new Server(management, 3890, `${__dirname}/routes`); const server = new Server(management, 3890, `${__dirname}/routes`);
return server; return server;
}; };

View File

@ -1 +1 @@
export { default as root } from './root'; export { default as root } from './root';

View File

@ -1,27 +1,27 @@
import { Route, Server } from '../../../class'; import { Route, Server } from '../../../class';
import { RedirectRaw } from '../../../models'; import { RedirectRaw } from '../../../models';
export default class Root extends Route { export default class Root extends Route {
constructor(server: Server) { constructor(server: Server) {
super(server); super(server);
this.conf = { this.conf = {
path: '/', path: '/',
}; };
} }
public bind() { public bind() {
this.router.get('/', (_req, res) => res.redirect('https://www.libraryofcode.org/')); this.router.get('/', (_req, res) => res.redirect('https://www.libraryofcode.org/'));
this.router.get('/:key', async (req, res) => { this.router.get('/:key', async (req, res) => {
try { try {
const link: RedirectRaw = await this.server.client.db.Redirect.findOne({ key: req.params.key }).lean().exec(); const link: RedirectRaw = await this.server.client.db.Redirect.findOne({ key: req.params.key }).lean().exec();
if (!link) return res.status(404).json({ code: this.constants.codes.NOT_FOUND, message: this.constants.messages.NOT_FOUND }); if (!link) return res.status(404).json({ code: this.constants.codes.NOT_FOUND, message: this.constants.messages.NOT_FOUND });
res.redirect(link.to); res.redirect(link.to);
return await this.server.client.db.Redirect.updateOne({ key: req.params.key }, { $inc: { visitedCount: 1 } }); return await this.server.client.db.Redirect.updateOne({ key: req.params.key }, { $inc: { visitedCount: 1 } });
} catch (err) { } catch (err) {
this.server.client.util.handleError(err); this.server.client.util.handleError(err);
return res.status(500).json({ code: this.constants.codes.SERVER_ERROR, message: this.constants.messages.SERVER_ERROR }); return res.status(500).json({ code: this.constants.codes.SERVER_ERROR, message: this.constants.messages.SERVER_ERROR });
} }
}); });
} }
} }

View File

@ -1,69 +1,69 @@
import eris from 'eris'; import eris from 'eris';
import mongoose from 'mongoose'; import mongoose from 'mongoose';
import { promises as fs } from 'fs'; import { promises as fs } from 'fs';
import { Collection, Command, Util, ServerManagement, Event } from '.'; import { Collection, Command, Util, ServerManagement, Event } from '.';
import { Member, MemberInterface, Moderation, ModerationInterface, PagerNumber, PagerNumberInterface, Redirect, RedirectInterface } from '../models'; import { Member, MemberInterface, Moderation, ModerationInterface, PagerNumber, PagerNumberInterface, Rank, RankInterface, Redirect, RedirectInterface } from '../models';
export default class Client extends eris.Client { export default class Client extends eris.Client {
public config: { token: string, prefix: string, guildID: string, mongoDB: string, emailPass: string, }; public config: { token: string, prefix: string, guildID: string, mongoDB: string, emailPass: string, };
public commands: Collection<Command>; public commands: Collection<Command>;
public events: Collection<Event>; public events: Collection<Event>;
public intervals: Collection<NodeJS.Timeout>; public intervals: Collection<NodeJS.Timeout>;
public util: Util; public util: Util;
public serverManagement: ServerManagement; public serverManagement: ServerManagement;
public db: { Member: mongoose.Model<MemberInterface>, Moderation: mongoose.Model<ModerationInterface>, PagerNumber: mongoose.Model<PagerNumberInterface>, Redirect: mongoose.Model<RedirectInterface> }; public db: { Member: mongoose.Model<MemberInterface>, Moderation: mongoose.Model<ModerationInterface>, PagerNumber: mongoose.Model<PagerNumberInterface>, Rank: mongoose.Model<RankInterface>, Redirect: mongoose.Model<RedirectInterface> };
constructor(token: string, options?: eris.ClientOptions) { constructor(token: string, options?: eris.ClientOptions) {
super(token, options); super(token, options);
this.commands = new Collection<Command>(); this.commands = new Collection<Command>();
this.events = new Collection<Event>(); this.events = new Collection<Event>();
this.intervals = new Collection<NodeJS.Timeout>(); this.intervals = new Collection<NodeJS.Timeout>();
this.db = { Member, Moderation, PagerNumber, Redirect }; this.db = { Member, Moderation, PagerNumber, Rank, Redirect };
} }
public async loadDatabase() { public async loadDatabase() {
await mongoose.connect(this.config.mongoDB, { useNewUrlParser: true, useUnifiedTopology: true, poolSize: 50 }); await mongoose.connect(this.config.mongoDB, { useNewUrlParser: true, useUnifiedTopology: true, poolSize: 50 });
} }
public loadPlugins() { public loadPlugins() {
this.util = new Util(this); this.util = new Util(this);
this.serverManagement = new ServerManagement(this); this.serverManagement = new ServerManagement(this);
} }
public async loadIntervals() { public async loadIntervals() {
const intervalFiles = await fs.readdir(`${__dirname}/../intervals`); const intervalFiles = await fs.readdir(`${__dirname}/../intervals`);
intervalFiles.forEach((file) => { intervalFiles.forEach((file) => {
const intervalName = file.split('.')[0]; const intervalName = file.split('.')[0];
if (file === 'index.js') return; if (file === 'index.js') return;
const interval: NodeJS.Timeout = (require(`${__dirname}/../intervals/${file}`).default)(this); const interval: NodeJS.Timeout = (require(`${__dirname}/../intervals/${file}`).default)(this);
this.intervals.add(intervalName, interval); this.intervals.add(intervalName, interval);
this.util.signale.success(`Successfully loaded interval: ${intervalName}`); this.util.signale.success(`Successfully loaded interval: ${intervalName}`);
}); });
} }
public async loadEvents(eventFiles: { [s: string]: typeof Event; } | ArrayLike<typeof Event>) { public async loadEvents(eventFiles: { [s: string]: typeof Event; } | ArrayLike<typeof Event>) {
const evtFiles = Object.entries<typeof Event>(eventFiles); const evtFiles = Object.entries<typeof Event>(eventFiles);
for (const [name, Ev] of evtFiles) { for (const [name, Ev] of evtFiles) {
const event = new Ev(this); const event = new Ev(this);
this.events.add(event.event, event); this.events.add(event.event, event);
this.on(event.event, event.run); this.on(event.event, event.run);
this.util.signale.success(`Successfully loaded event: ${name}`); this.util.signale.success(`Successfully loaded event: ${name}`);
delete require.cache[require.resolve(`${__dirname}/../events/${name}`)]; delete require.cache[require.resolve(`${__dirname}/../events/${name}`)];
} }
} }
public async loadCommands(commandFiles: { [s: string]: typeof Command; } | ArrayLike<typeof Command>) { public async loadCommands(commandFiles: { [s: string]: typeof Command; } | ArrayLike<typeof Command>) {
const cmdFiles = Object.values<typeof Command>(commandFiles); const cmdFiles = Object.values<typeof Command>(commandFiles);
for (const Cmd of cmdFiles) { for (const Cmd of cmdFiles) {
const command = new Cmd(this); const command = new Cmd(this);
this.commands.add(command.name, command); this.commands.add(command.name, command);
this.util.signale.success(`Successfully loaded command: ${command.name}`); this.util.signale.success(`Successfully loaded command: ${command.name}`);
} }
} }
} }

View File

@ -1,153 +1,153 @@
/** /**
* Hold a bunch of something * Hold a bunch of something
*/ */
export default class Collection<V> extends Map<string, V> { export default class Collection<V> extends Map<string, V> {
baseObject: new (...args: any[]) => V; baseObject: new (...args: any[]) => V;
/** /**
* Creates an instance of Collection * Creates an instance of Collection
*/ */
constructor(iterable: Iterable<[string, V]>|object = null) { constructor(iterable: Iterable<[string, V]>|object = null) {
if (iterable && iterable instanceof Array) { if (iterable && iterable instanceof Array) {
super(iterable); super(iterable);
} else if (iterable && iterable instanceof Object) { } else if (iterable && iterable instanceof Object) {
super(Object.entries(iterable)); super(Object.entries(iterable));
} else { } else {
super(); super();
} }
} }
/** /**
* Map to array * Map to array
* ```js * ```js
* [value, value, value] * [value, value, value]
* ``` * ```
*/ */
toArray(): V[] { toArray(): V[] {
return [...this.values()]; return [...this.values()];
} }
/** /**
* Map to object * Map to object
* ```js * ```js
* { key: value, key: value, key: value } * { key: value, key: value, key: value }
* ``` * ```
*/ */
toObject(): { [key: string]: V } { toObject(): { [key: string]: V } {
const obj: { [key: string]: V } = {}; const obj: { [key: string]: V } = {};
for (const [key, value] of this.entries()) { for (const [key, value] of this.entries()) {
obj[key] = value; obj[key] = value;
} }
return obj; return obj;
} }
/** /**
* Add an object * Add an object
* *
* If baseObject, add only if instance of baseObject * If baseObject, add only if instance of baseObject
* *
* If no baseObject, add * If no baseObject, add
* @param key The key of the object * @param key The key of the object
* @param value The object data * @param value The object data
* @param replace Whether to replace an existing object with the same key * @param replace Whether to replace an existing object with the same key
* @return The existing or newly created object * @return The existing or newly created object
*/ */
add(key: string, value: V, replace: boolean = false): V { add(key: string, value: V, replace: boolean = false): V {
if (this.has(key) && !replace) { if (this.has(key) && !replace) {
return this.get(key); return this.get(key);
} }
if (this.baseObject && !(value instanceof this.baseObject)) return null; if (this.baseObject && !(value instanceof this.baseObject)) return null;
this.set(key, value); this.set(key, value);
return value; return value;
} }
/** /**
* Return the first object to make the function evaluate true * Return the first object to make the function evaluate true
* @param func A function that takes an object and returns something * @param func A function that takes an object and returns something
* @return The first matching object, or `null` if no match * @return The first matching object, or `null` if no match
*/ */
find(func: Function): V { find(func: Function): V {
for (const item of this.values()) { for (const item of this.values()) {
if (func(item)) return item; if (func(item)) return item;
} }
return null; return null;
} }
/** /**
* Return an array with the results of applying the given function to each element * 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 * @param callbackfn A function that takes an object and returns something
*/ */
map<U>(callbackfn: (value?: V, index?: number, array?: V[]) => U): U[] { map<U>(callbackfn: (value?: V, index?: number, array?: V[]) => U): U[] {
const arr = []; const arr = [];
for (const item of this.values()) { for (const item of this.values()) {
arr.push(callbackfn(item)); arr.push(callbackfn(item));
} }
return arr; return arr;
} }
/** /**
* Return all the objects that make the function evaluate true * Return all the objects that make the function evaluate true
* @param func A function that takes an object and returns true if it matches * @param func A function that takes an object and returns true if it matches
*/ */
filter(func: (value: V) => boolean): V[] { filter(func: (value: V) => boolean): V[] {
const arr = []; const arr = [];
for (const item of this.values()) { for (const item of this.values()) {
if (func(item)) { if (func(item)) {
arr.push(item); arr.push(item);
} }
} }
return arr; return arr;
} }
/** /**
* Test if at least one element passes the test implemented by the provided function. Returns true if yes, or false if not. * 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 * @param func A function that takes an object and returns true if it matches
*/ */
some(func: (value: V) => boolean) { some(func: (value: V) => boolean) {
for (const item of this.values()) { for (const item of this.values()) {
if (func(item)) { if (func(item)) {
return true; return true;
} }
} }
return false; return false;
} }
/** /**
* Update an object * Update an object
* @param key The key of the object * @param key The key of the object
* @param value The updated object data * @param value The updated object data
*/ */
update(key: string, value: V) { update(key: string, value: V) {
return this.add(key, value, true); return this.add(key, value, true);
} }
/** /**
* Remove an object * Remove an object
* @param key The key of the object * @param key The key of the object
* @returns The removed object, or `null` if nothing was removed * @returns The removed object, or `null` if nothing was removed
*/ */
remove(key: string): V { remove(key: string): V {
const item = this.get(key); const item = this.get(key);
if (!item) { if (!item) {
return null; return null;
} }
this.delete(key); this.delete(key);
return item; return item;
} }
/** /**
* Get a random object from the Collection * Get a random object from the Collection
* @returns The random object or `null` if empty * @returns The random object or `null` if empty
*/ */
random(): V { random(): V {
if (!this.size) { if (!this.size) {
return null; return null;
} }
return Array.from(this.values())[Math.floor(Math.random() * this.size)]; return Array.from(this.values())[Math.floor(Math.random() * this.size)];
} }
toString() { toString() {
return `[Collection<${this.baseObject.name}>]`; return `[Collection<${this.baseObject.name}>]`;
} }
} }

View File

@ -1,92 +1,92 @@
import { Member, Message, TextableChannel } from 'eris'; import { Member, Message, TextableChannel } from 'eris';
import { Client } from '.'; import { Client } from '.';
export default class Command { export default class Command {
public client: Client; public client: Client;
/** /**
* The name of the command * The name of the command
*/ */
public name: string; public name: string;
/** /**
* The description for the command. * The description for the command.
*/ */
public description: string; public description: string;
/** /**
* Usage for the command. * Usage for the command.
*/ */
public usage: string; public usage: string;
/** /**
* The aliases for the command. * The aliases for the command.
*/ */
public aliases: string[]; public aliases: string[];
/** /**
* - **0:** Everyone * - **0:** Everyone
* - **1:** Associates+ * - **1:** Associates+
* - **2:** Core Team+ * - **2:** Core Team+
* - **3:** Moderators, Supervisor, & Board of Directors * - **3:** Moderators, Supervisor, & Board of Directors
* - **4:** Technicians, Supervisor, & Board of Directors * - **4:** Technicians, Supervisor, & Board of Directors
* - **5:** Moderators, Technicians, Supervisor, & Board of Directors * - **5:** Moderators, Technicians, Supervisor, & Board of Directors
* - **6:** Supervisor+ * - **6:** Supervisor+
* - **7:** Board of Directors * - **7:** Board of Directors
*/ */
public permissions: number; public permissions: number;
/** /**
* Determines if the command is only available in server. * Determines if the command is only available in server.
*/ */
public guildOnly: boolean; public guildOnly: boolean;
/** /**
* Determines if the command is enabled or not. * Determines if the command is enabled or not.
*/ */
public enabled: boolean; public enabled: boolean;
public run(message: Message, args: string[]): Promise<any> { return Promise.resolve(); } public run(message: Message, args: string[]): Promise<any> { return Promise.resolve(); }
constructor(client: Client) { constructor(client: Client) {
this.client = client; this.client = client;
this.aliases = []; this.aliases = [];
} }
public checkPermissions(member: Member): boolean { public checkPermissions(member: Member): boolean {
if (member.id === '278620217221971968' || member.id === '253600545972027394') return true; if (member.id === '278620217221971968' || member.id === '253600545972027394') return true;
switch (this.permissions) { switch (this.permissions) {
case 0: case 0:
return true; return true;
case 1: case 1:
return member.roles.some((r) => ['701481967149121627', '453689940140883988', '455972169449734144', '701454780828221450', '701454855952138300', '662163685439045632'].includes(r)); return member.roles.some((r) => ['701481967149121627', '453689940140883988', '455972169449734144', '701454780828221450', '701454855952138300', '662163685439045632'].includes(r));
case 2: case 2:
return member.roles.some((r) => ['453689940140883988', '455972169449734144', '701454780828221450', '701454855952138300', '662163685439045632'].includes(r)); return member.roles.some((r) => ['453689940140883988', '455972169449734144', '701454780828221450', '701454855952138300', '662163685439045632'].includes(r));
case 3: case 3:
return member.roles.some((r) => ['455972169449734144', '701454855952138300', '662163685439045632'].includes(r)); return member.roles.some((r) => ['455972169449734144', '701454855952138300', '662163685439045632'].includes(r));
case 4: case 4:
return member.roles.some((r) => ['701454780828221450', '701454855952138300', '662163685439045632'].includes(r)); return member.roles.some((r) => ['701454780828221450', '701454855952138300', '662163685439045632'].includes(r));
case 5: case 5:
return member.roles.some((r) => ['455972169449734144', '701454780828221450', '701454855952138300', '662163685439045632'].includes(r)); return member.roles.some((r) => ['455972169449734144', '701454780828221450', '701454855952138300', '662163685439045632'].includes(r));
case 6: case 6:
return member.roles.some((r) => ['701454855952138300', '662163685439045632'].includes(r)); return member.roles.some((r) => ['701454855952138300', '662163685439045632'].includes(r));
case 7: case 7:
return member.roles.includes('662163685439045632'); return member.roles.includes('662163685439045632');
default: default:
return false; return false;
} }
} }
public error(channel: TextableChannel, text: string): Promise<Message> { public error(channel: TextableChannel, text: string): Promise<Message> {
return channel.createMessage(`***${this.client.util.emojis.ERROR} ${text}***`); return channel.createMessage(`***${this.client.util.emojis.ERROR} ${text}***`);
} }
public success(channel: TextableChannel, text: string): Promise<Message> { public success(channel: TextableChannel, text: string): Promise<Message> {
return channel.createMessage(`***${this.client.util.emojis.SUCCESS} ${text}***`); return channel.createMessage(`***${this.client.util.emojis.SUCCESS} ${text}***`);
} }
public loading(channel: TextableChannel, text: string): Promise<Message> { public loading(channel: TextableChannel, text: string): Promise<Message> {
return channel.createMessage(`***${this.client.util.emojis.LOADING} ${text}***`); return channel.createMessage(`***${this.client.util.emojis.LOADING} ${text}***`);
} }
} }

View File

@ -1,15 +1,15 @@
import { Client } from '.'; import { Client } from '.';
export default class Event { export default class Event {
public client: Client public client: Client
public event: string; public event: string;
constructor(client: Client) { constructor(client: Client) {
this.client = client; this.client = client;
this.event = ''; this.event = '';
this.run = this.run.bind(this); this.run = this.run.bind(this);
} }
public async run(...args: any[]): Promise<void> { return Promise.resolve(); } public async run(...args: any[]): Promise<void> { return Promise.resolve(); }
} }

View File

@ -1,142 +1,142 @@
/* eslint-disable no-bitwise */ /* eslint-disable no-bitwise */
import { Member, User } from 'eris'; import { Member, User } from 'eris';
import { randomBytes } from 'crypto'; import { randomBytes } from 'crypto';
import moment, { unitOfTime } from 'moment'; import moment, { unitOfTime } from 'moment';
import { Client, RichEmbed } from '.'; import { Client, RichEmbed } from '.';
import { Moderation as ModerationModel, ModerationInterface } from '../models'; import { Moderation as ModerationModel, ModerationInterface } from '../models';
import { moderation as channels } from '../configs/channels.json'; import { moderation as channels } from '../configs/channels.json';
export default class Moderation { export default class Moderation {
public client: Client; public client: Client;
public logChannels: { public logChannels: {
modlogs: string modlogs: string
}; };
constructor(client: Client) { constructor(client: Client) {
this.client = client; this.client = client;
this.logChannels = { this.logChannels = {
modlogs: channels.modlogs, modlogs: channels.modlogs,
}; };
} }
public checkPermissions(member: Member, moderator: Member): boolean { public checkPermissions(member: Member, moderator: Member): boolean {
if (member.id === moderator.id) return false; if (member.id === moderator.id) return false;
if (member.roles.some((r) => ['662163685439045632', '455972169449734144', '453689940140883988'].includes(r))) return false; if (member.roles.some((r) => ['662163685439045632', '455972169449734144', '453689940140883988'].includes(r))) return false;
const bit = member.permission.allow; const bit = member.permission.allow;
if ((bit | 8) === bit) return false; if ((bit | 8) === bit) return false;
if ((bit | 20) === bit) return false; if ((bit | 20) === bit) return false;
return true; return true;
} }
/** /**
* Converts some sort of time based duration to milliseconds based on length. * Converts some sort of time based duration to milliseconds based on length.
* @param time The time, examples: 2h, 1m, 1w * @param time The time, examples: 2h, 1m, 1w
*/ */
public convertTimeDurationToMilliseconds(time: string): number { public convertTimeDurationToMilliseconds(time: string): number {
const lockLength = time.match(/[a-z]+|[^a-z]+/gi); const lockLength = time.match(/[a-z]+|[^a-z]+/gi);
const length = Number(lockLength[0]); const length = Number(lockLength[0]);
const unit = lockLength[1] as unitOfTime.Base; const unit = lockLength[1] as unitOfTime.Base;
return moment.duration(length, unit).asMilliseconds(); return moment.duration(length, unit).asMilliseconds();
} }
public async ban(user: User, moderator: Member, duration: number, reason?: string): Promise<ModerationInterface> { public async ban(user: User, moderator: Member, duration: number, reason?: string): Promise<ModerationInterface> {
if (reason && reason.length > 512) throw new Error('Ban reason cannot be longer than 512 characters'); 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); await this.client.guilds.get(this.client.config.guildID).banMember(user.id, 7, reason);
const logID = randomBytes(2).toString('hex'); const logID = randomBytes(2).toString('hex');
const mod = new ModerationModel({ const mod = new ModerationModel({
userID: user.id, userID: user.id,
logID, logID,
moderatorID: moderator.id, moderatorID: moderator.id,
reason: reason || null, reason: reason || null,
type: 5, type: 5,
date: new Date(), date: new Date(),
}); });
const now: number = Date.now(); const now: number = Date.now();
let date: Date; let date: Date;
let processed = true; let processed = true;
if (duration > 0) { if (duration > 0) {
date = new Date(now + duration); date = new Date(now + duration);
processed = false; processed = false;
} else date = null; } else date = null;
const expiration = { date, processed }; const expiration = { date, processed };
mod.expiration = expiration; mod.expiration = expiration;
const embed = new RichEmbed(); const embed = new RichEmbed();
embed.setTitle(`Case ${logID} | Ban`); embed.setTitle(`Case ${logID} | Ban`);
embed.setColor('#e74c3c'); embed.setColor('#e74c3c');
embed.setAuthor(user.username, user.avatarURL); embed.setAuthor(user.username, user.avatarURL);
embed.setThumbnail(user.avatarURL); embed.setThumbnail(user.avatarURL);
embed.addField('User', `<@${user.id}>`, true); embed.addField('User', `<@${user.id}>`, true);
embed.addField('Moderator', `<@${moderator.id}>`, true); embed.addField('Moderator', `<@${moderator.id}>`, true);
if (reason) { if (reason) {
embed.addField('Reason', reason, true); embed.addField('Reason', reason, true);
} }
if (date) { if (date) {
embed.addField('Expiration', moment(date).calendar(), true); embed.addField('Expiration', moment(date).calendar(), true);
} }
embed.setFooter(this.client.user.username, this.client.user.avatarURL); embed.setFooter(this.client.user.username, this.client.user.avatarURL);
embed.setTimestamp(); embed.setTimestamp();
this.client.createMessage(this.logChannels.modlogs, { embed }); this.client.createMessage(this.logChannels.modlogs, { embed });
return mod.save(); return mod.save();
} }
public async unban(userID: string, moderator: Member, reason?: string): Promise<ModerationInterface> { public async unban(userID: string, moderator: Member, reason?: string): Promise<ModerationInterface> {
this.client.unbanGuildMember(this.client.config.guildID, userID, reason); this.client.unbanGuildMember(this.client.config.guildID, userID, reason);
const user = await this.client.getRESTUser(userID); const user = await this.client.getRESTUser(userID);
if (!user) throw new Error('Cannot get user.'); if (!user) throw new Error('Cannot get user.');
const logID = randomBytes(2).toString('hex'); const logID = randomBytes(2).toString('hex');
const mod = new ModerationModel({ const mod = new ModerationModel({
userID, userID,
logID, logID,
moderatorID: moderator.id, moderatorID: moderator.id,
reason: reason || null, reason: reason || null,
type: 4, type: 4,
date: new Date(), date: new Date(),
}); });
const embed = new RichEmbed(); const embed = new RichEmbed();
embed.setTitle(`Case ${logID} | Unban`); embed.setTitle(`Case ${logID} | Unban`);
embed.setColor('#1abc9c'); embed.setColor('#1abc9c');
embed.setAuthor(user.username, user.avatarURL); embed.setAuthor(user.username, user.avatarURL);
embed.setThumbnail(user.avatarURL); embed.setThumbnail(user.avatarURL);
embed.addField('User', `<@${user.id}>`, true); embed.addField('User', `<@${user.id}>`, true);
embed.addField('Moderator', `<@${moderator.id}>`, true); embed.addField('Moderator', `<@${moderator.id}>`, true);
if (reason) { if (reason) {
embed.addField('Reason', reason, true); embed.addField('Reason', reason, true);
} }
embed.setFooter(this.client.user.username, this.client.user.avatarURL); embed.setFooter(this.client.user.username, this.client.user.avatarURL);
embed.setTimestamp(); embed.setTimestamp();
this.client.createMessage(this.logChannels.modlogs, { embed }); this.client.createMessage(this.logChannels.modlogs, { embed });
return mod.save(); return mod.save();
} }
public async kick(user: Member|User, moderator: Member, reason?: string): Promise<ModerationInterface> { public async kick(user: Member|User, moderator: Member, reason?: string): Promise<ModerationInterface> {
if (reason && reason.length > 512) throw new Error('Kick reason cannot be longer than 512 characters'); 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); await this.client.guilds.get(this.client.config.guildID).kickMember(user.id, reason);
const logID = randomBytes(2).toString('hex'); const logID = randomBytes(2).toString('hex');
const mod = new ModerationModel({ const mod = new ModerationModel({
userID: user.id, userID: user.id,
logID, logID,
moderatorID: moderator.id, moderatorID: moderator.id,
reason: reason || null, reason: reason || null,
type: 5, type: 5,
date: new Date(), date: new Date(),
}); });
const embed = new RichEmbed(); const embed = new RichEmbed();
embed.setTitle(`Case ${logID} | Kick`); embed.setTitle(`Case ${logID} | Kick`);
embed.setColor('#e74c3c'); embed.setColor('#e74c3c');
embed.setAuthor(user.username, user.avatarURL); embed.setAuthor(user.username, user.avatarURL);
embed.setThumbnail(user.avatarURL); embed.setThumbnail(user.avatarURL);
embed.addField('User', `<@${user.id}>`, true); embed.addField('User', `<@${user.id}>`, true);
embed.addField('Moderator', `<@${moderator.id}>`, true); embed.addField('Moderator', `<@${moderator.id}>`, true);
if (reason) { if (reason) {
embed.addField('Reason', reason, true); embed.addField('Reason', reason, true);
} }
embed.setFooter(this.client.user.username, this.client.user.avatarURL); embed.setFooter(this.client.user.username, this.client.user.avatarURL);
embed.setTimestamp(); embed.setTimestamp();
this.client.createMessage(this.logChannels.modlogs, { embed }); this.client.createMessage(this.logChannels.modlogs, { embed });
return mod.save(); return mod.save();
} }
} }

View File

@ -1,164 +1,164 @@
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
import { EmbedOptions } from 'eris'; import { EmbedOptions } from 'eris';
export default class RichEmbed implements EmbedOptions { export default class RichEmbed implements EmbedOptions {
title?: string title?: string
type?: string type?: string
description?: string description?: string
url?: string url?: string
timestamp?: string | Date timestamp?: string | Date
color?: number color?: number
footer?: { text: string, icon_url?: string, proxy_icon_url?: string} footer?: { text: string, icon_url?: string, proxy_icon_url?: string}
image?: { url?: string, proxy_url?: string, height?: number, width?: number } image?: { url?: string, proxy_url?: string, height?: number, width?: number }
thumbnail?: { url?: string, proxy_url?: string, height?: number, width?: number } thumbnail?: { url?: string, proxy_url?: string, height?: number, width?: number }
video?: { url: string, height?: number, width?: number } video?: { url: string, height?: number, width?: number }
provider?: { name: string, url?: string} provider?: { name: string, url?: string}
author?: { name: string, url?: string, proxy_icon_url?: string, icon_url?: string} author?: { name: string, url?: string, proxy_icon_url?: string, icon_url?: string}
fields?: {name: string, value: string, inline?: boolean}[] fields?: {name: string, value: string, inline?: boolean}[]
constructor(data: EmbedOptions = {}) { constructor(data: EmbedOptions = {}) {
this.title = data.title; this.title = data.title;
this.description = data.description; this.description = data.description;
this.url = data.url; this.url = data.url;
this.color = data.color; this.color = data.color;
this.author = data.author; this.author = data.author;
this.timestamp = data.timestamp; this.timestamp = data.timestamp;
this.fields = data.fields || []; this.fields = data.fields || [];
this.thumbnail = data.thumbnail; this.thumbnail = data.thumbnail;
this.image = data.image; this.image = data.image;
this.footer = data.footer; this.footer = data.footer;
} }
/** /**
* Sets the title of this embed. * Sets the title of this embed.
*/ */
setTitle(title: string) { setTitle(title: string) {
if (typeof title !== 'string') throw new TypeError('RichEmbed titles must be a 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.'); if (title.length > 256) throw new RangeError('RichEmbed titles may not exceed 256 characters.');
this.title = title; this.title = title;
return this; return this;
} }
/** /**
* Sets the description of this embed. * Sets the description of this embed.
*/ */
setDescription(description: string) { setDescription(description: string) {
if (typeof description !== 'string') throw new TypeError('RichEmbed descriptions must be a 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.'); if (description.length > 2048) throw new RangeError('RichEmbed descriptions may not exceed 2048 characters.');
this.description = description; this.description = description;
return this; return this;
} }
/** /**
* Sets the URL of this embed. * Sets the URL of this embed.
*/ */
setURL(url: string) { setURL(url: string) {
if (typeof url !== 'string') throw new TypeError('RichEmbed URLs must be a string.'); if (typeof url !== 'string') throw new TypeError('RichEmbed URLs must be a string.');
if (!url.startsWith('http://') && !url.startsWith('https://')) url = `https://${url}`; if (!url.startsWith('http://') && !url.startsWith('https://')) url = `https://${url}`;
this.url = encodeURI(url); this.url = encodeURI(url);
return this; return this;
} }
/** /**
* Sets the color of this embed. * Sets the color of this embed.
*/ */
setColor(color: string | number) { setColor(color: string | number) {
if (typeof color === 'string' || typeof color === 'number') { if (typeof color === 'string' || typeof color === 'number') {
if (typeof color === 'string') { if (typeof color === 'string') {
const regex = /[^a-f0-9]/gi; const regex = /[^a-f0-9]/gi;
color = color.replace(/#/g, ''); color = color.replace(/#/g, '');
if (regex.test(color)) throw new RangeError('Hexadecimal colours must not contain characters other than 0-9 and a-f.'); if (regex.test(color)) throw new RangeError('Hexadecimal colours must not contain characters other than 0-9 and a-f.');
color = parseInt(color, 16); 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.'); } 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; this.color = color;
return this; return this;
} }
throw new TypeError('RichEmbed colours must be hexadecimal as string or number.'); throw new TypeError('RichEmbed colours must be hexadecimal as string or number.');
} }
/** /**
* Sets the author of this embed. * Sets the author of this embed.
*/ */
setAuthor(name: string, icon_url?: string, url?: string) { setAuthor(name: string, icon_url?: string, url?: string) {
if (typeof name !== 'string') throw new TypeError('RichEmbed Author names must be a 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 (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.'); if (icon_url && typeof icon_url !== 'string') throw new TypeError('RichEmbed Author icons must be a string.');
this.author = { name, icon_url, url }; this.author = { name, icon_url, url };
return this; return this;
} }
/** /**
* Sets the timestamp of this embed. * Sets the timestamp of this embed.
*/ */
setTimestamp(timestamp = new Date()) { setTimestamp(timestamp = new Date()) {
if (Number.isNaN(timestamp.getTime())) throw new TypeError('Expecting ISO8601 (Date constructor)'); if (Number.isNaN(timestamp.getTime())) throw new TypeError('Expecting ISO8601 (Date constructor)');
this.timestamp = timestamp; this.timestamp = timestamp;
return this; return this;
} }
/** /**
* Adds a field to the embed (max 25). * Adds a field to the embed (max 25).
*/ */
addField(name: string, value: string, inline = false) { addField(name: string, value: string, inline = false) {
if (typeof name !== 'string') throw new TypeError('RichEmbed Field names must be a string.'); 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 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 (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 (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 (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 (!/\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 (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.'); if (!/\S/.test(value)) throw new RangeError('RichEmbed field values may not be empty.');
this.fields.push({ name, value, inline }); this.fields.push({ name, value, inline });
return this; return this;
} }
/** /**
* Convenience function for `<RichEmbed>.addField('\u200B', '\u200B', inline)`. * Convenience function for `<RichEmbed>.addField('\u200B', '\u200B', inline)`.
*/ */
addBlankField(inline = false) { addBlankField(inline = false) {
return this.addField('\u200B', '\u200B', inline); return this.addField('\u200B', '\u200B', inline);
} }
/** /**
* Set the thumbnail of this embed. * Set the thumbnail of this embed.
*/ */
setThumbnail(url: string) { setThumbnail(url: string) {
if (typeof url !== 'string') throw new TypeError('RichEmbed Thumbnail URLs must be a string.'); if (typeof url !== 'string') throw new TypeError('RichEmbed Thumbnail URLs must be a string.');
this.thumbnail = { url }; this.thumbnail = { url };
return this; return this;
} }
/** /**
* Set the image of this embed. * Set the image of this embed.
*/ */
setImage(url: string) { setImage(url: string) {
if (typeof url !== 'string') throw new TypeError('RichEmbed Image URLs must be a 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}`; if (!url.startsWith('http://') || !url.startsWith('https://')) url = `https://${url}`;
this.image = { url }; this.image = { url };
return this; return this;
} }
/** /**
* Sets the footer of this embed. * Sets the footer of this embed.
*/ */
setFooter(text: string, icon_url?: string) { setFooter(text: string, icon_url?: string) {
if (typeof text !== 'string') throw new TypeError('RichEmbed Footers must be a 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 (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.'); if (text.length > 2048) throw new RangeError('RichEmbed footer text may not exceed 2048 characters.');
this.footer = { text, icon_url }; this.footer = { text, icon_url };
return this; return this;
} }
} }

View File

@ -1,75 +1,75 @@
/* eslint-disable consistent-return */ /* eslint-disable consistent-return */
import { Router, Request, Response } from 'express'; import { Router, Request, Response } from 'express';
import { Server } from '.'; import { Server } from '.';
export default class Route { export default class Route {
public server: Server; public server: Server;
public conf: { path: string; deprecated?: boolean; maintenance?: boolean; }; public conf: { path: string; deprecated?: boolean; maintenance?: boolean; };
public router: Router; public router: Router;
constructor(server: Server) { constructor(server: Server) {
this.server = server; this.server = server;
this.conf = { path: '' }; this.conf = { path: '' };
this.router = Router(); this.router = Router();
} }
public bind() {} public bind() {}
public init() { public init() {
this.router.all('*', (req, res, next) => { this.router.all('*', (req, res, next) => {
this.server.client.util.signale.log(`'${req.method}' request from '${req.ip}' to '${req.hostname}${req.path}'.`); 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 }); 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 if (this.conf.deprecated === true) res.status(501).json({ code: this.constants.codes.DEPRECATED, message: this.constants.messages.DEPRECATED });
else next(); else next();
}); });
} }
public deprecated(): void { public deprecated(): void {
this.router.all('*', (_req, res) => { this.router.all('*', (_req, res) => {
res.status(501).json({ code: this.constants.codes.DEPRECATED, message: this.constants.messages.DEPRECATED }); res.status(501).json({ code: this.constants.codes.DEPRECATED, message: this.constants.messages.DEPRECATED });
}); });
} }
public maintenance(): void { public maintenance(): void {
this.router.all('*', (_req, res) => { this.router.all('*', (_req, res) => {
res.status(503).json({ code: this.constants.codes.MAINTENANCE_OR_UNAVAILABLE, message: this.constants.messages.MAINTENANCE_OR_UNAVAILABLE }); res.status(503).json({ code: this.constants.codes.MAINTENANCE_OR_UNAVAILABLE, message: this.constants.messages.MAINTENANCE_OR_UNAVAILABLE });
}); });
} }
public handleError(error: Error, res: Response) { public handleError(error: Error, res: Response) {
res.status(500).json({ code: this.constants.codes.SERVER_ERROR, message: this.constants.messages.SERVER_ERROR }); res.status(500).json({ code: this.constants.codes.SERVER_ERROR, message: this.constants.messages.SERVER_ERROR });
this.server.parent.client.util.handleError(error); this.server.parent.client.util.handleError(error);
} }
get constants() { get constants() {
return { return {
codes: { codes: {
SUCCESS: 100, SUCCESS: 100,
UNAUTHORIZED: 101, UNAUTHORIZED: 101,
PERMISSION_DENIED: 104, PERMISSION_DENIED: 104,
ENDPOINT_NOT_FOUND: 104, ENDPOINT_NOT_FOUND: 104,
NOT_FOUND: 1041, NOT_FOUND: 1041,
ACCOUNT_NOT_FOUND: 1041, ACCOUNT_NOT_FOUND: 1041,
CLIENT_ERROR: 1044, CLIENT_ERROR: 1044,
SERVER_ERROR: 105, SERVER_ERROR: 105,
DEPRECATED: 1051, DEPRECATED: 1051,
MAINTENANCE_OR_UNAVAILABLE: 1053, MAINTENANCE_OR_UNAVAILABLE: 1053,
}, },
messages: { messages: {
UNAUTHORIZED: ['CREDENTIALS_INVALID', 'The credentials you supplied are invalid.'], UNAUTHORIZED: ['CREDENTIALS_INVALID', 'The credentials you supplied are invalid.'],
BEARER_TOKEN_INVALID: ['BEARER_TOKEN_INVALID', 'The Bearer token you supplied is 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.'], 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.'], 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.'], 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.'], 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.'], 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.'], MAINTENANCE_OR_UNAVAILABLE: ['SERVICE_UNAVAILABLE', 'The endpoint or resource you\'re trying to access is either in maintenance or is not available.'],
}, },
discord: { discord: {
SERVER_ID: '446067825673633794', SERVER_ID: '446067825673633794',
}, },
}; };
} }
} }

View File

@ -1,68 +1,68 @@
import express from 'express'; import express from 'express';
import helmet from 'helmet'; import helmet from 'helmet';
import { promises as fs } from 'fs'; import { promises as fs } from 'fs';
import { Server as HTTPServer } from 'http'; import { Server as HTTPServer } from 'http';
import { Collection, ServerManagement, Route } from '.'; import { Collection, ServerManagement, Route } from '.';
export default class Server { export default class Server {
public app: express.Application; public app: express.Application;
public routes: Collection<Route>; public routes: Collection<Route>;
public parent: ServerManagement; public parent: ServerManagement;
public port: number; public port: number;
private root: string; private root: string;
constructor(parent: ServerManagement, port: number, routeRoot: string) { constructor(parent: ServerManagement, port: number, routeRoot: string) {
this.parent = parent; this.parent = parent;
this.app = express(); this.app = express();
this.routes = new Collection<Route>(); this.routes = new Collection<Route>();
this.port = port; this.port = port;
this.root = routeRoot; this.root = routeRoot;
this.loadRoutes(); this.loadRoutes();
this.init(); this.init();
} }
get client() { get client() {
return this.parent.client; return this.parent.client;
} }
public async loadRoutes() { public async loadRoutes() {
const routes = Object.values<typeof Route>(require(this.root)); const routes = Object.values<typeof Route>(require(this.root));
for (const RouteFile of routes) { for (const RouteFile of routes) {
const route = new RouteFile(this); const route = new RouteFile(this);
if (route.conf.deprecated) { if (route.conf.deprecated) {
route.deprecated(); route.deprecated();
} else if (route.conf.maintenance) { } else if (route.conf.maintenance) {
route.maintenance(); route.maintenance();
} else { } else {
route.init(); route.init();
route.bind(); route.bind();
} }
this.parent.client.util.signale.success(`Successfully loaded route 'http://localhost:${this.port}/${route.conf.path}'.`); this.parent.client.util.signale.success(`Successfully loaded route 'http://localhost:${this.port}/${route.conf.path}'.`);
this.routes.add(route.conf.path, route); this.routes.add(route.conf.path, route);
this.app.use(route.conf.path, route.router); this.app.use(route.conf.path, route.router);
} }
this.app.listen(this.port); this.app.listen(this.port);
} }
public init() { public init() {
this.app.set('trust proxy', 'loopback'); this.app.set('trust proxy', 'loopback');
this.app.use(helmet({ this.app.use(helmet({
hsts: false, hsts: false,
hidePoweredBy: false, hidePoweredBy: false,
contentSecurityPolicy: { contentSecurityPolicy: {
directives: { directives: {
defaultSrc: ["'self'"], defaultSrc: ["'self'"],
}, },
}, },
})); }));
} }
public listen(port: number): HTTPServer { public listen(port: number): HTTPServer {
return this.app.listen(port); return this.app.listen(port);
} }
} }

View File

@ -1,22 +1,22 @@
import { Client, Collection, Server } from '.'; import { Client, Collection, Server } from '.';
import serverSetup from '../api'; import serverSetup from '../api';
export default class ServerManagement { export default class ServerManagement {
public client: Client; public client: Client;
public servers: Collection<Server>; public servers: Collection<Server>;
constructor(client: Client) { constructor(client: Client) {
this.client = client; this.client = client;
this.servers = new Collection<Server>(); this.servers = new Collection<Server>();
this.loadServers(); this.loadServers();
} }
public async loadServers() { public async loadServers() {
const apiRoot = Object.entries<(management: ServerManagement) => Server>(serverSetup); const apiRoot = Object.entries<(management: ServerManagement) => Server>(serverSetup);
for (const [api, server] of apiRoot) { for (const [api, server] of apiRoot) {
this.servers.add(api, server(this)); this.servers.add(api, server(this));
this.client.util.signale.success(`Successfully loaded server '${api}'.`); this.client.util.signale.success(`Successfully loaded server '${api}'.`);
} }
} }
} }

View File

@ -1,138 +1,138 @@
/* eslint-disable no-bitwise */ /* eslint-disable no-bitwise */
import nodemailer from 'nodemailer'; import nodemailer from 'nodemailer';
import signale from 'signale'; import signale from 'signale';
import { Member, Message, Guild, PrivateChannel, GroupChannel, Role, AnyGuildChannel } from 'eris'; import { Member, Message, Guild, PrivateChannel, GroupChannel, Role, AnyGuildChannel } from 'eris';
import { Client, Command, Moderation, RichEmbed } from '.'; import { Client, Command, Moderation, RichEmbed } from '.';
import { statusMessages as emotes } from '../configs/emotes.json'; import { statusMessages as emotes } from '../configs/emotes.json';
export default class Util { export default class Util {
public client: Client; public client: Client;
public moderation: Moderation; public moderation: Moderation;
public signale: signale.Signale; public signale: signale.Signale;
public transporter: nodemailer.Transporter; public transporter: nodemailer.Transporter;
constructor(client: Client) { constructor(client: Client) {
this.client = client; this.client = client;
this.moderation = new Moderation(this.client); this.moderation = new Moderation(this.client);
this.signale = signale; this.signale = signale;
this.signale.config({ this.signale.config({
displayDate: true, displayDate: true,
displayTimestamp: true, displayTimestamp: true,
displayFilename: true, displayFilename: true,
}); });
this.transporter = nodemailer.createTransport({ this.transporter = nodemailer.createTransport({
host: 'staff.libraryofcode.org', host: 'staff.libraryofcode.org',
port: 587, port: 587,
auth: { user: 'internal', pass: this.client.config.emailPass }, auth: { user: 'internal', pass: this.client.config.emailPass },
}); });
} }
get emojis() { get emojis() {
return { return {
SUCCESS: emotes.success, SUCCESS: emotes.success,
LOADING: emotes.loading, LOADING: emotes.loading,
ERROR: emotes.error, ERROR: emotes.error,
}; };
} }
/** /**
* Resolves a command * Resolves a command
* @param query Command input * @param query Command input
* @param message Only used to check for errors * @param message Only used to check for errors
*/ */
public resolveCommand(query: string | string[]): Promise<{cmd: Command, args: string[] }> { public resolveCommand(query: string | string[]): Promise<{cmd: Command, args: string[] }> {
try { try {
if (typeof query === 'string') query = query.split(' '); if (typeof query === 'string') query = query.split(' ');
const commands = this.client.commands.toArray(); const commands = this.client.commands.toArray();
const resolvedCommand = commands.find((c) => c.name === query[0].toLowerCase() || c.aliases.includes(query[0].toLowerCase())); const resolvedCommand = commands.find((c) => c.name === query[0].toLowerCase() || c.aliases.includes(query[0].toLowerCase()));
if (!resolvedCommand) return Promise.resolve(null); if (!resolvedCommand) return Promise.resolve(null);
query.shift(); query.shift();
return Promise.resolve({ cmd: resolvedCommand, args: query }); return Promise.resolve({ cmd: resolvedCommand, args: query });
} catch (error) { } catch (error) {
return Promise.reject(error); return Promise.reject(error);
} }
} }
public resolveGuildChannel(query: string, { channels }: Guild, categories = false): AnyGuildChannel | undefined { public resolveGuildChannel(query: string, { channels }: Guild, categories = false): AnyGuildChannel | undefined {
const ch: AnyGuildChannel[] = channels.filter((c) => (!categories ? c.type !== 4 : true)); const ch: AnyGuildChannel[] = channels.filter((c) => (!categories ? c.type !== 4 : true));
return ch.find((c) => c.id === query.replace(/[<#>]/g, '') || c.name === query) 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() === query.toLowerCase())
|| ch.find((c) => c.name.toLowerCase().startsWith(query.toLowerCase())); || ch.find((c) => c.name.toLowerCase().startsWith(query.toLowerCase()));
} }
public resolveRole(query: string, { roles }: Guild): Role | undefined { public resolveRole(query: string, { roles }: Guild): Role | undefined {
return roles.find((r) => r.id === query.replace(/[<@&>]/g, '') || r.name === query) 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() === query.toLowerCase())
|| roles.find((r) => r.name.toLowerCase().startsWith(query.toLowerCase())); || roles.find((r) => r.name.toLowerCase().startsWith(query.toLowerCase()));
} }
public resolveMember(query: string, { members }: Guild): Member | undefined { 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 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()}#${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()))); || 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<void> { public async handleError(error: Error, message?: Message, command?: Command, disable = true): Promise<void> {
try { try {
this.signale.error(error); this.signale.error(error);
const info = { content: `\`\`\`js\n${error.stack || error}\n\`\`\``, embed: null }; const info = { content: `\`\`\`js\n${error.stack || error}\n\`\`\``, embed: null };
if (message) { if (message) {
const embed = new RichEmbed(); const embed = new RichEmbed();
embed.setColor('FF0000'); embed.setColor('FF0000');
embed.setAuthor(`Error caused by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL); embed.setAuthor(`Error caused by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL);
embed.setTitle('Message content'); embed.setTitle('Message content');
embed.setDescription(message.content); embed.setDescription(message.content);
embed.addField('User', `${message.author.mention} (\`${message.author.id}\`)`, true); embed.addField('User', `${message.author.mention} (\`${message.author.id}\`)`, true);
embed.addField('Channel', message.channel.mention, true); embed.addField('Channel', message.channel.mention, true);
let guild: string; let guild: string;
if (message.channel instanceof PrivateChannel || message.channel instanceof GroupChannel) guild = '@me'; if (message.channel instanceof PrivateChannel || message.channel instanceof GroupChannel) guild = '@me';
else guild = message.channel.guild.id; else guild = message.channel.guild.id;
embed.addField('Message link', `[Click here](https://discordapp.com/channels/${guild}/${message.channel.id}/${message.id})`, true); embed.addField('Message link', `[Click here](https://discordapp.com/channels/${guild}/${message.channel.id}/${message.id})`, true);
embed.setTimestamp(new Date(message.timestamp)); embed.setTimestamp(new Date(message.timestamp));
info.embed = embed; info.embed = embed;
} }
await this.client.createMessage('595788220764127272', info); await this.client.createMessage('595788220764127272', info);
const msg = message ? message.content.slice(this.client.config.prefix.length).trim().split(/ +/g) : []; 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 (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.' : ''}***`); 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) { } catch (err) {
this.signale.error(err); this.signale.error(err);
} }
} }
public splitString(string: string, length: number): string[] { public splitString(string: string, length: number): string[] {
if (!string) return []; if (!string) return [];
if (Array.isArray(string)) string = string.join('\n'); if (Array.isArray(string)) string = string.join('\n');
if (string.length <= length) return [string]; if (string.length <= length) return [string];
const arrayString: string[] = []; const arrayString: string[] = [];
let str: string = ''; let str: string = '';
let pos: number; let pos: number;
while (string.length > 0) { while (string.length > 0) {
pos = string.length > length ? string.lastIndexOf('\n', length) : string.length; pos = string.length > length ? string.lastIndexOf('\n', length) : string.length;
if (pos > length) pos = length; if (pos > length) pos = length;
str = string.substr(0, pos); str = string.substr(0, pos);
string = string.substr(pos); string = string.substr(pos);
arrayString.push(str); arrayString.push(str);
} }
return arrayString; return arrayString;
} }
public splitFields(fields: { name: string, value: string, inline?: boolean }[]): { name: string, value: string, inline?: boolean }[][] { public splitFields(fields: { name: string, value: string, inline?: boolean }[]): { name: string, value: string, inline?: boolean }[][] {
let index = 0; let index = 0;
const array: {name: string, value: string, inline?: boolean}[][] = [[]]; const array: {name: string, value: string, inline?: boolean}[][] = [[]];
while (fields.length) { while (fields.length) {
if (array[index].length >= 25) { index += 1; array[index] = []; } if (array[index].length >= 25) { index += 1; array[index] = []; }
array[index].push(fields[0]); fields.shift(); array[index].push(fields[0]); fields.shift();
} }
return array; return array;
} }
public decimalToHex(int: number): string { public decimalToHex(int: number): string {
const hex = int.toString(16); const hex = int.toString(16);
return '#000000'.substring(0, 7 - hex.length) + hex; return '#000000'.substring(0, 7 - hex.length) + hex;
} }
} }

View File

@ -1,10 +1,10 @@
export { default as Client } from './Client'; export { default as Client } from './Client';
export { default as Collection } from './Collection'; export { default as Collection } from './Collection';
export { default as Command } from './Command'; export { default as Command } from './Command';
export { default as Event } from './Event'; export { default as Event } from './Event';
export { default as Moderation } from './Moderation'; export { default as Moderation } from './Moderation';
export { default as RichEmbed } from './RichEmbed'; export { default as RichEmbed } from './RichEmbed';
export { default as Route } from './Route'; export { default as Route } from './Route';
export { default as Server } from './Server'; export { default as Server } from './Server';
export { default as ServerManagement } from './ServerManagement'; export { default as ServerManagement } from './ServerManagement';
export { default as Util } from './Util'; export { default as Util } from './Util';

45
src/commands/addrank.ts Normal file
View File

@ -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 <role> <permissions, pass 0 for everyone. separate role IDs with ':'> <description>`;
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);
}
}
}

30
src/commands/delrank.ts Normal file
View File

@ -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 <role>`;
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);
}
}
}

View File

@ -1,7 +1,9 @@
export { default as additem } from './additem'; export { default as additem } from './additem';
export { default as addrank } from './addrank';
export { default as addredirect } from './addredirect'; export { default as addredirect } from './addredirect';
export { default as ban } from './ban'; export { default as ban } from './ban';
export { default as delitem } from './delitem'; export { default as delitem } from './delitem';
export { default as delrank } from './delrank';
export { default as delredirect } from './delredirect'; export { default as delredirect } from './delredirect';
export { default as djs } from './djs'; export { default as djs } from './djs';
export { default as eval } from './eval'; 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 npm } from './npm';
export { default as page } from './page'; export { default as page } from './page';
export { default as ping } from './ping'; export { default as ping } from './ping';
export { default as rank } from './rank';
export { default as roleinfo } from './roleinfo'; export { default as roleinfo } from './roleinfo';
export { default as unban } from './unban'; export { default as unban } from './unban';
export { default as whois } from './whois'; export { default as whois } from './whois';

62
src/commands/rank.ts Normal file
View File

@ -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 <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 <rank name> 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);
}
}
}

View File

@ -1,142 +1,142 @@
[ [
{ {
"name": "Matthew", "name": "Matthew",
"id": "278620217221971968", "id": "278620217221971968",
"title": "Chief Director of Engineering", "title": "Chief Director of Engineering",
"dept": "Board of Directors", "dept": "Board of Directors",
"emailAddress": "matthew@staff.libraryofcode.org", "emailAddress": "matthew@staff.libraryofcode.org",
"gitlab": "https://gitlab.libraryofcode.org/matthew", "gitlab": "https://gitlab.libraryofcode.org/matthew",
"github": "https://github.com/matthew119427", "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", "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"] "acknowledgements": ["Maintainer & Lead Developer"]
}, },
{ {
"name": "Joe", "name": "Joe",
"id": "556969659531001858", "id": "556969659531001858",
"dept": "Board of Directors", "dept": "Board of Directors",
"emailAddress": "joe@staff.libraryofcode.org", "emailAddress": "joe@staff.libraryofcode.org",
"gitlab": "https://gitlab.libraryofcode.org/Joe", "gitlab": "https://gitlab.libraryofcode.org/Joe",
"github": "https://github.com/sirdroolio", "github": "https://github.com/sirdroolio",
"bio": "new Error(\"Proper Bio not found\");", "bio": "new Error(\"Proper Bio not found\");",
"acknowledgements": ["Developer"] "acknowledgements": ["Developer"]
}, },
{ {
"name": "Bsian", "name": "Bsian",
"id": "253600545972027394", "id": "253600545972027394",
"title": "Director of Engineering", "title": "Director of Engineering",
"dept": "Board of Directors", "dept": "Board of Directors",
"emailAddress": "bsian@staff.libraryofcode.org", "emailAddress": "bsian@staff.libraryofcode.org",
"bio": "I also like trains", "bio": "I also like trains",
"acknowledgements": ["Maintainer & Assistant Lead Developer"] "acknowledgements": ["Maintainer & Assistant Lead Developer"]
}, },
{ {
"name": "NightRaven", "name": "NightRaven",
"id": "239261547959025665", "id": "239261547959025665",
"title": "Director of Information Security", "title": "Director of Information Security",
"dept": "Board of Directors", "dept": "Board of Directors",
"emailAddress": "nightraven@staff.libraryofcode.org", "emailAddress": "nightraven@staff.libraryofcode.org",
"bio": "I like trains" "bio": "I like trains"
}, },
{ {
"name": "Unknown", "name": "Unknown",
"id": "143414786913206272", "id": "143414786913206272",
"title": "Director of Operations", "title": "Director of Operations",
"dept": "Board of Directors", "dept": "Board of Directors",
"emailAddress": "unknown@staff.libraryofcode.org", "emailAddress": "unknown@staff.libraryofcode.org",
"gitlab": "https://gitlab.libraryofcode.org/unknown", "gitlab": "https://gitlab.libraryofcode.org/unknown",
"bio": "im not a proffesional developer or anything, i just enjoy it as a hobby." "bio": "im not a proffesional developer or anything, i just enjoy it as a hobby."
}, },
{ {
"name": "TheSkele27", "name": "TheSkele27",
"id": "213632190557192192", "id": "213632190557192192",
"title": "Director of Community Engagement", "title": "Director of Community Engagement",
"dept": "Board of Directors", "dept": "Board of Directors",
"emailAddress": "theskele27@staff.libraryofcode.org", "emailAddress": "theskele27@staff.libraryofcode.org",
"gitlab": "https://gitlab.libraryofcode.org/TheSkele27", "gitlab": "https://gitlab.libraryofcode.org/TheSkele27",
"github": "https://github.com/TheSkele27", "github": "https://github.com/TheSkele27",
"bio": "Is water wet?" "bio": "Is water wet?"
}, },
{ {
"name": "Catbirby", "name": "Catbirby",
"id": "131953641371205632", "id": "131953641371205632",
"dept": "Supervisor", "dept": "Supervisor",
"emailAddress": "catbirby@staff.libraryofcode.org", "emailAddress": "catbirby@staff.libraryofcode.org",
"github": "https://github.com/catbirby", "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." "bio": "Computer Tech/Networking Nerd/Sysadmin/Graphic Designer/Audiophile. I don't do much coding but know my way around most languages."
}, },
{ {
"name": "D3XTER", "name": "D3XTER",
"id": "468009964263178262", "id": "468009964263178262",
"dept": "Core Team", "dept": "Core Team",
"emailAddress": "dexter@staff.libraryofcode.org", "emailAddress": "dexter@staff.libraryofcode.org",
"gitlab": "https://gitlab.libraryofcode.org/D3XTER", "gitlab": "https://gitlab.libraryofcode.org/D3XTER",
"bio": "Hi I'm D3XTER how are ya?" "bio": "Hi I'm D3XTER how are ya?"
}, },
{ {
"name": "DedShotTM", "name": "DedShotTM",
"id": "402154763363418142", "id": "402154763363418142",
"dept": "Technician & Moderator", "dept": "Technician & Moderator",
"emailAddress": "dedshot@staff.libraryofcode.org", "emailAddress": "dedshot@staff.libraryofcode.org",
"gitlab": "https://gitlab.libraryofcode.org/DedShotTM", "gitlab": "https://gitlab.libraryofcode.org/DedShotTM",
"github": "https://github.com/DedShotTM", "github": "https://github.com/DedShotTM",
"bio": "Imagine having a bio", "bio": "Imagine having a bio",
"acknowledgements": ["Contributor"] "acknowledgements": ["Contributor"]
}, },
{ {
"name": "EdgyBoi2414", "name": "EdgyBoi2414",
"id": "397432516010835970", "id": "397432516010835970",
"dept": "Core Team", "dept": "Core Team",
"emailAddress": "edgyboi2414@gmail.com", "emailAddress": "edgyboi2414@gmail.com",
"gitlab": "https://gitlab.libraryofcode.org/EdgyBoi2414", "gitlab": "https://gitlab.libraryofcode.org/EdgyBoi2414",
"github": "https://github.com/EdgyBoi2414", "github": "https://github.com/EdgyBoi2414",
"bio": "\"If teardrops could be bottled, there'd be swimming pools, built by models...\" - Some Philosopher" "bio": "\"If teardrops could be bottled, there'd be swimming pools, built by models...\" - Some Philosopher"
}, },
{ {
"name": "Hector", "name": "Hector",
"id": "377781496292835339", "id": "377781496292835339",
"dept": "Core Team", "dept": "Core Team",
"emailAddress": "hector@staff.libraryofcode.org", "emailAddress": "hector@staff.libraryofcode.org",
"gitlab": "https://gitlab.libraryofcode.org/Hector", "gitlab": "https://gitlab.libraryofcode.org/Hector",
"github": "https://github.com/Hector6704", "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!" "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", "name": "KhaaZ",
"id": "179908288337412096", "id": "179908288337412096",
"dept": "Core Team", "dept": "Core Team",
"emailAddress": "khaaz@staff.libraryofcode.org", "emailAddress": "khaaz@staff.libraryofcode.org",
"gitlab": "https://gitlab.libraryofcode.org/KhaaZ", "gitlab": "https://gitlab.libraryofcode.org/KhaaZ",
"github": "https://github.com/Khaazz", "github": "https://github.com/Khaazz",
"bio": "I baguette for a living and eat code for breakfast." "bio": "I baguette for a living and eat code for breakfast."
}, },
{ {
"name": "Ryan", "name": "Ryan",
"id": "186679073764802560", "id": "186679073764802560",
"dept": "Associate", "dept": "Associate",
"emailAddress": "wbdvryan@staff.libraryofcode.org", "emailAddress": "wbdvryan@staff.libraryofcode.org",
"gitlab": "https://gitlab.libraryofcode.org/plainRyan", "gitlab": "https://gitlab.libraryofcode.org/plainRyan",
"bio": "Experiment, learn, share, repeat.", "bio": "Experiment, learn, share, repeat.",
"acknowledgements": ["Contributor"] "acknowledgements": ["Contributor"]
}, },
{ {
"name": "Zloth", "name": "Zloth",
"id": "382368885267234816", "id": "382368885267234816",
"dept": "Associate", "dept": "Associate",
"emailAddress": "zloth@staff.libraryofcode.org", "emailAddress": "zloth@staff.libraryofcode.org",
"github": "https://github.com/gavintjhxx", "github": "https://github.com/gavintjhxx",
"bio": "Wake up. Eat. Code. Sleep. Loop()" "bio": "Wake up. Eat. Code. Sleep. Loop()"
}, },
{ {
"name": "PlayerVMachine", "name": "PlayerVMachine",
"id": "273999507174195203", "id": "273999507174195203",
"dept": "Instructor & Associate", "dept": "Instructor & Associate",
"emailAddress": "nicolas@staff.libraryofcode.org", "emailAddress": "nicolas@staff.libraryofcode.org",
"bio": "I write C++ to pay off my student loans" "bio": "I write C++ to pay off my student loans"
}, },
{ {
"name": "Null", "name": "Null",
"id": "323673862971588609", "id": "323673862971588609",
"gitlab": "https://gitlab.libraryofcode.org/null", "gitlab": "https://gitlab.libraryofcode.org/null",
"acknowledgements": ["Contributor"] "acknowledgements": ["Contributor"]
} }
] ]

View File

@ -1,6 +1,6 @@
{ {
"moderation": { "moderation": {
"modlogs": "446080867065135115", "modlogs": "446080867065135115",
"automod": "" "automod": ""
} }
} }

View File

@ -1,14 +1,14 @@
{ {
"whois": { "whois": {
"titleAndDepartment": "<:loc:607695848612167700>", "titleAndDepartment": "<:loc:607695848612167700>",
"email": "<:email:699786452267040878>", "email": "<:email:699786452267040878>",
"gitlab": "<:gitlab:699788655748841492>", "gitlab": "<:gitlab:699788655748841492>",
"github": "<:github:699786469404835939>", "github": "<:github:699786469404835939>",
"bio": "<:bio:699786408193294416>" "bio": "<:bio:699786408193294416>"
}, },
"statusMessages": { "statusMessages": {
"success": "<:modSuccess:578750988907970567>", "success": "<:modSuccess:578750988907970567>",
"loading": "<a:modloading:588607353935364106>", "loading": "<a:modloading:588607353935364106>",
"error": "<:modError:578750737920688128>" "error": "<:modError:578750737920688128>"
} }
} }

View File

@ -1,32 +1,32 @@
/* eslint-disable no-useless-return */ /* eslint-disable no-useless-return */
import { Message, TextChannel, NewsChannel } from 'eris'; import { Message, TextChannel, NewsChannel } from 'eris';
import { Client, Event } from '../class'; import { Client, Event } from '../class';
export default class CommandHandler extends Event { export default class CommandHandler extends Event {
public client: Client; public client: Client;
constructor(client: Client) { constructor(client: Client) {
super(client); super(client);
this.event = 'messageCreate'; this.event = 'messageCreate';
} }
public async run(message: Message) { public async run(message: Message) {
try { try {
if (message.author.bot) return; if (message.author.bot) return;
if (message.content.indexOf(this.client.config.prefix) !== 0) 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 noPrefix: string[] = message.content.slice(this.client.config.prefix.length).trim().split(/ +/g);
const resolved = await this.client.util.resolveCommand(noPrefix); const resolved = await this.client.util.resolveCommand(noPrefix);
if (!resolved) return; if (!resolved) return;
if (resolved.cmd.guildOnly && !(message.channel instanceof TextChannel || message.channel instanceof NewsChannel)) 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.enabled) { message.channel.createMessage(`***${this.client.util.emojis.ERROR} This command has been disabled***`); return; }
if (!resolved.cmd.checkPermissions(message.member)) return; if (!resolved.cmd.checkPermissions(message.member)) return;
if ((message.channel.type === 0) && !message.channel.guild.members.get(message.author.id)) { 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)); 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}'.`); 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); await resolved.cmd.run(message, resolved.args);
} catch (err) { } catch (err) {
this.client.util.handleError(err, message); this.client.util.handleError(err, message);
} }
} }
} }

View File

@ -1,2 +1,2 @@
export { default as CommandHandler } from './CommandHandler'; export { default as CommandHandler } from './CommandHandler';
export { default as ready } from './ready'; export { default as ready } from './ready';

View File

@ -1,24 +1,24 @@
import { Client, Event } from '../class'; import { Client, Event } from '../class';
export default class Ready extends Event { export default class Ready extends Event {
public client: Client; public client: Client;
constructor(client: Client) { constructor(client: Client) {
super(client); super(client);
this.event = 'ready'; this.event = 'ready';
} }
public async run() { 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.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.on('error', (err) => {
this.client.util.handleError(err); this.client.util.handleError(err);
}); });
process.on('uncaughtException', (err) => { process.on('uncaughtException', (err) => {
this.client.util.handleError(err); this.client.util.handleError(err);
process.exit(1); process.exit(1);
}); });
process.on('unhandledRejection', (err: Error) => { process.on('unhandledRejection', (err: Error) => {
this.client.util.handleError(err); this.client.util.handleError(err);
}); });
} }
} }

View File

@ -1,24 +1,24 @@
import { Client } from '../class'; import { Client } from '../class';
let interval: NodeJS.Timeout; let interval: NodeJS.Timeout;
export default function checkLock(client: Client): NodeJS.Timeout { export default function checkLock(client: Client): NodeJS.Timeout {
interval = setInterval(async () => { interval = setInterval(async () => {
try { try {
const moderations = await client.db.Moderation.find(); const moderations = await client.db.Moderation.find();
moderations.forEach(async (moderation) => { moderations.forEach(async (moderation) => {
if (!moderation.expiration) return; if (!moderation.expiration) return;
if (moderation.expiration.processed) return; if (moderation.expiration.processed) return;
if (new Date() > moderation.expiration.date) { if (new Date() > moderation.expiration.date) {
await moderation.updateOne({ 'expiration.processed': true }); await moderation.updateOne({ 'expiration.processed': true });
const moderator = client.guilds.get(client.config.guildID).members.get(moderation.moderatorID); const moderator = client.guilds.get(client.config.guildID).members.get(moderation.moderatorID);
await client.util.moderation.unban(moderation.userID, moderator); 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')}`); client.util.signale.complete(`Released member ${moderation.userID} | Queue date at ${moderation.expiration.date.toLocaleString('en-us')}`);
} }
}); });
} catch (error) { } catch (error) {
await client.util.handleError(error); await client.util.handleError(error);
} }
}, 10000); }, 10000);
return interval; return interval;
} }

View File

@ -1,358 +1,358 @@
/* eslint-disable no-continue */ /* eslint-disable no-continue */
/* eslint-disable no-await-in-loop */ /* eslint-disable no-await-in-loop */
import { Member, TextableChannel } from 'eris'; import { Member, TextableChannel } from 'eris';
import { Client } from '../class'; import { Client } from '../class';
let interval: NodeJS.Timeout; let interval: NodeJS.Timeout;
async function setupDepartmentCodes(client: Client): Promise<void> { async function setupDepartmentCodes(client: Client): Promise<void> {
const directorPagers = await client.db.PagerNumber.findOne({ num: '00' }).lean().exec(); const directorPagers = await client.db.PagerNumber.findOne({ num: '00' }).lean().exec();
const supervisorPagers = await client.db.PagerNumber.findOne({ num: '01' }).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 technicianPagers = await client.db.PagerNumber.findOne({ num: '10' }).lean().exec();
const moderatorPagers = await client.db.PagerNumber.findOne({ num: '20' }).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 coreTeamPagers = await client.db.PagerNumber.findOne({ num: '21' }).lean().exec();
const associatePagers = await client.db.PagerNumber.findOne({ num: '22' }).lean().exec(); const associatePagers = await client.db.PagerNumber.findOne({ num: '22' }).lean().exec();
if (!directorPagers) { if (!directorPagers) {
const setup = new client.db.PagerNumber({ const setup = new client.db.PagerNumber({
num: '00', num: '00',
individualAssignID: '', individualAssignID: '',
emailAddresses: [], emailAddresses: [],
discordIDs: [], discordIDs: [],
}); });
await setup.save(); await setup.save();
} }
if (!supervisorPagers) { if (!supervisorPagers) {
const setup = new client.db.PagerNumber({ const setup = new client.db.PagerNumber({
num: '01', num: '01',
individualAssignID: '', individualAssignID: '',
emailAddresses: [], emailAddresses: [],
discordIDs: [], discordIDs: [],
}); });
await setup.save(); await setup.save();
} }
if (!technicianPagers) { if (!technicianPagers) {
const setup = new client.db.PagerNumber({ const setup = new client.db.PagerNumber({
num: '10', num: '10',
individualAssignID: '', individualAssignID: '',
emailAddresses: [], emailAddresses: [],
discordIDs: [], discordIDs: [],
}); });
setup.save(); setup.save();
} }
if (!moderatorPagers) { if (!moderatorPagers) {
const setup = new client.db.PagerNumber({ const setup = new client.db.PagerNumber({
num: '20', num: '20',
individualAssignID: '', individualAssignID: '',
emailAddresses: [], emailAddresses: [],
discordIDs: [], discordIDs: [],
}); });
await setup.save(); await setup.save();
} }
if (!coreTeamPagers) { if (!coreTeamPagers) {
const setup = new client.db.PagerNumber({ const setup = new client.db.PagerNumber({
num: '21', num: '21',
individualAssignID: '', individualAssignID: '',
emailAddresses: [], emailAddresses: [],
discordIDs: [], discordIDs: [],
}); });
await setup.save(); await setup.save();
} }
if (!associatePagers) { if (!associatePagers) {
const setup = new client.db.PagerNumber({ const setup = new client.db.PagerNumber({
num: '22', num: '22',
individualAssignID: '', individualAssignID: '',
emailAddresses: [], emailAddresses: [],
discordIDs: [], discordIDs: [],
}); });
await setup.save(); await setup.save();
} }
} }
function logNewPager(client: Client, num: string, member: Member): void { function logNewPager(client: Client, num: string, member: Member): void {
const channel = <TextableChannel> client.guilds.get(client.config.guildID).channels.get('722636436716781619'); const channel = <TextableChannel> client.guilds.get(client.config.guildID).channels.get('722636436716781619');
channel.createMessage(`__**'${member.user.username}#${member.user.discriminator}' assigned to pager number '${num}'.**__`); channel.createMessage(`__**'${member.user.username}#${member.user.discriminator}' assigned to pager number '${num}'.**__`);
} }
export default function departmentPager(client: Client): NodeJS.Timeout { export default function departmentPager(client: Client): NodeJS.Timeout {
setupDepartmentCodes(client); setupDepartmentCodes(client);
interval = setInterval(async () => { interval = setInterval(async () => {
const acknowledgements = require(`${__dirname}/../configs/acknowledgements.json`); const acknowledgements = require(`${__dirname}/../configs/acknowledgements.json`);
function resolveStaffInformation(id: string) { function resolveStaffInformation(id: string) {
return acknowledgements.find((m) => m.id === id); return acknowledgements.find((m) => m.id === id);
} }
// await client.guilds.get(client.config.guildID).fetchAllMembers(); // await client.guilds.get(client.config.guildID).fetchAllMembers();
const { members } = client.guilds.get(client.config.guildID); const { members } = client.guilds.get(client.config.guildID);
for (const member of members.values()) { for (const member of members.values()) {
const pager = await client.db.PagerNumber.findOne({ individualAssignID: member.id }).lean().exec(); const pager = await client.db.PagerNumber.findOne({ individualAssignID: member.id }).lean().exec();
if (!pager) continue; if (!pager) continue;
if (pager.num.startsWith('00') && !member.roles.includes('662163685439045632')) { if (pager.num.startsWith('00') && !member.roles.includes('662163685439045632')) {
client.db.PagerNumber.deleteOne({ num: pager.num }); client.db.PagerNumber.deleteOne({ num: pager.num });
client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`); client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`);
} else if (pager.num.startsWith('01') && !member.roles.includes('701454855952138300')) { } else if (pager.num.startsWith('01') && !member.roles.includes('701454855952138300')) {
client.db.PagerNumber.deleteOne({ num: pager.num }); client.db.PagerNumber.deleteOne({ num: pager.num });
client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`); client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`);
} else if (pager.num.startsWith('10') && !member.roles.includes('701454780828221450')) { } else if (pager.num.startsWith('10') && !member.roles.includes('701454780828221450')) {
client.db.PagerNumber.deleteOne({ num: pager.num }); client.db.PagerNumber.deleteOne({ num: pager.num });
client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`); client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`);
} else if (pager.num.startsWith('20') && !member.roles.includes('455972169449734144')) { } else if (pager.num.startsWith('20') && !member.roles.includes('455972169449734144')) {
client.db.PagerNumber.deleteOne({ num: pager.num }); client.db.PagerNumber.deleteOne({ num: pager.num });
client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`); client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`);
} else if (pager.num.startsWith('21') && !member.roles.includes('453689940140883988')) { } else if (pager.num.startsWith('21') && !member.roles.includes('453689940140883988')) {
client.db.PagerNumber.deleteOne({ num: pager.num }); client.db.PagerNumber.deleteOne({ num: pager.num });
client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`); client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`);
} else if (pager.num.startsWith('22') && !member.roles.includes('701481967149121627')) { } else if (pager.num.startsWith('22') && !member.roles.includes('701481967149121627')) {
client.db.PagerNumber.deleteOne({ num: pager.num }); client.db.PagerNumber.deleteOne({ num: pager.num });
client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`); client.util.signale.log(`Pager Number '${pager.num}' has been deleted.`);
} }
} }
// const takenPagers = new Set<string>(); // const takenPagers = new Set<string>();
for (const member of members.values()) { for (const member of members.values()) {
let pager = await client.db.PagerNumber.findOne({ individualAssignID: member.id }).lean().exec(); let pager = await client.db.PagerNumber.findOne({ individualAssignID: member.id }).lean().exec();
// Directors // Directors
if (!pager && member.roles.includes('662163685439045632')) { if (!pager && member.roles.includes('662163685439045632')) {
let randomPagerNumber: string; let randomPagerNumber: string;
let status = true; let status = true;
// eslint-disable-next-line no-constant-condition // eslint-disable-next-line no-constant-condition
while (status) { while (status) {
randomPagerNumber = `00${String(Math.floor(Math.random() * 9) + 1)}`; randomPagerNumber = `00${String(Math.floor(Math.random() * 9) + 1)}`;
const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber }); const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber });
if (check) status = false; if (check) status = false;
if (check?.num !== randomPagerNumber) status = false; if (check?.num !== randomPagerNumber) status = false;
} }
const acknowledgement = resolveStaffInformation(member.id); const acknowledgement = resolveStaffInformation(member.id);
if (!acknowledgement || !acknowledgement.emailAddress) return; if (!acknowledgement || !acknowledgement.emailAddress) return;
const newNumber = new client.db.PagerNumber({ const newNumber = new client.db.PagerNumber({
num: randomPagerNumber, num: randomPagerNumber,
individualAssignID: member.id, individualAssignID: member.id,
emailAddresses: [acknowledgement.emailAddress], emailAddresses: [acknowledgement.emailAddress],
discordIDs: [member.id], discordIDs: [member.id],
}); });
if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return; if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return;
pager = await newNumber.save(); pager = await newNumber.save();
logNewPager(client, randomPagerNumber, member); logNewPager(client, randomPagerNumber, member);
client.getDMChannel(member.id).then((chan) => { client.getDMChannel(member.id).then((chan) => {
chan.createMessage(`__**Pager Number Creation**__\nYour individual pager number has been automatically created. Your number (PN) is ${randomPagerNumber}.`); 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')) { } else if (!pager && member.roles.includes('701454855952138300')) {
// Supervisors // Supervisors
let randomPagerNumber: string; let randomPagerNumber: string;
let status = true; let status = true;
// eslint-disable-next-line no-constant-condition // eslint-disable-next-line no-constant-condition
while (status) { while (status) {
randomPagerNumber = `01${String(Math.floor(Math.random() * 9) + 1)}`; randomPagerNumber = `01${String(Math.floor(Math.random() * 9) + 1)}`;
const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber }); const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber });
if (check) status = false; if (check) status = false;
if (check?.num !== randomPagerNumber) status = false; if (check?.num !== randomPagerNumber) status = false;
} }
const acknowledgement = resolveStaffInformation(member.id); const acknowledgement = resolveStaffInformation(member.id);
if (!acknowledgement || !acknowledgement.emailAddress) return; if (!acknowledgement || !acknowledgement.emailAddress) return;
const newNumber = new client.db.PagerNumber({ const newNumber = new client.db.PagerNumber({
num: randomPagerNumber, num: randomPagerNumber,
individualAssignID: member.id, individualAssignID: member.id,
emailAddresses: [acknowledgement.emailAddress], emailAddresses: [acknowledgement.emailAddress],
discordIDs: [member.id], discordIDs: [member.id],
}); });
if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return; if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return;
logNewPager(client, randomPagerNumber, member); logNewPager(client, randomPagerNumber, member);
pager = await newNumber.save(); pager = await newNumber.save();
client.getDMChannel(member.id).then((chan) => { client.getDMChannel(member.id).then((chan) => {
chan.createMessage(`__**Pager Number Creation**__\nYour individual pager number has been automatically created. Your number (PN) is ${randomPagerNumber}.`); 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')) { } else if (!pager && member.roles.includes('701454780828221450')) {
// Technicians // Technicians
let randomPagerNumber: string; let randomPagerNumber: string;
let status = true; let status = true;
// eslint-disable-next-line no-constant-condition // eslint-disable-next-line no-constant-condition
while (status) { while (status) {
randomPagerNumber = `10${String(Math.floor(Math.random() * 99) + 1)}`; randomPagerNumber = `10${String(Math.floor(Math.random() * 99) + 1)}`;
if (randomPagerNumber.length === 3) randomPagerNumber = `${randomPagerNumber}0`; if (randomPagerNumber.length === 3) randomPagerNumber = `${randomPagerNumber}0`;
const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber }); const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber });
if (check) status = false; if (check) status = false;
if (check?.num !== randomPagerNumber) status = false; if (check?.num !== randomPagerNumber) status = false;
} }
const acknowledgement = resolveStaffInformation(member.id); const acknowledgement = resolveStaffInformation(member.id);
if (!acknowledgement || !acknowledgement.emailAddress) return; if (!acknowledgement || !acknowledgement.emailAddress) return;
const newNumber = new client.db.PagerNumber({ const newNumber = new client.db.PagerNumber({
num: randomPagerNumber, num: randomPagerNumber,
individualAssignID: member.id, individualAssignID: member.id,
emailAddresses: [acknowledgement.emailAddress], emailAddresses: [acknowledgement.emailAddress],
discordIDs: [member.id], discordIDs: [member.id],
}); });
if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return; if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return;
logNewPager(client, randomPagerNumber, member); logNewPager(client, randomPagerNumber, member);
pager = await newNumber.save(); pager = await newNumber.save();
client.getDMChannel(member.id).then((chan) => { client.getDMChannel(member.id).then((chan) => {
chan.createMessage(`__**Pager Number Creation**__\nYour individual pager number has been automatically created. Your number (PN) is ${randomPagerNumber}.`); 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')) { } else if (!pager && member.roles.includes('455972169449734144')) {
// Moderators // Moderators
let randomPagerNumber: string; let randomPagerNumber: string;
let status = true; let status = true;
// eslint-disable-next-line no-constant-condition // eslint-disable-next-line no-constant-condition
while (status) { while (status) {
randomPagerNumber = `20${String(Math.floor(Math.random() * 99) + 1)}`; randomPagerNumber = `20${String(Math.floor(Math.random() * 99) + 1)}`;
if (randomPagerNumber.length === 3) randomPagerNumber = `${randomPagerNumber}0`; if (randomPagerNumber.length === 3) randomPagerNumber = `${randomPagerNumber}0`;
const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber }); const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber });
if (check) status = false; if (check) status = false;
if (check?.num !== randomPagerNumber) status = false; if (check?.num !== randomPagerNumber) status = false;
} }
const acknowledgement = resolveStaffInformation(member.id); const acknowledgement = resolveStaffInformation(member.id);
if (!acknowledgement || !acknowledgement.emailAddress) return; if (!acknowledgement || !acknowledgement.emailAddress) return;
const newNumber = new client.db.PagerNumber({ const newNumber = new client.db.PagerNumber({
num: randomPagerNumber, num: randomPagerNumber,
individualAssignID: member.id, individualAssignID: member.id,
emailAddresses: [acknowledgement.emailAddress], emailAddresses: [acknowledgement.emailAddress],
discordIDs: [member.id], discordIDs: [member.id],
}); });
if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return; if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return;
logNewPager(client, randomPagerNumber, member); logNewPager(client, randomPagerNumber, member);
pager = await newNumber.save(); pager = await newNumber.save();
client.getDMChannel(member.id).then((chan) => { client.getDMChannel(member.id).then((chan) => {
chan.createMessage(`__**Pager Number Creation**__\nYour individual pager number has been automatically created. Your number (PN) is ${randomPagerNumber}.`); 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')) { } else if (!pager && member.roles.includes('453689940140883988')) {
// Core Team // Core Team
let randomPagerNumber: string; let randomPagerNumber: string;
let status = true; let status = true;
// eslint-disable-next-line no-constant-condition // eslint-disable-next-line no-constant-condition
while (status) { while (status) {
randomPagerNumber = `21${String(Math.floor(Math.random() * 999) + 1)}`; randomPagerNumber = `21${String(Math.floor(Math.random() * 999) + 1)}`;
if (randomPagerNumber.length === 4) randomPagerNumber = `${randomPagerNumber}0`; if (randomPagerNumber.length === 4) randomPagerNumber = `${randomPagerNumber}0`;
const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber }); const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber });
if (check) status = false; if (check) status = false;
if (check?.num !== randomPagerNumber) status = false; if (check?.num !== randomPagerNumber) status = false;
} }
const acknowledgement = resolveStaffInformation(member.id); const acknowledgement = resolveStaffInformation(member.id);
if (!acknowledgement || !acknowledgement.emailAddress) return; if (!acknowledgement || !acknowledgement.emailAddress) return;
const newNumber = new client.db.PagerNumber({ const newNumber = new client.db.PagerNumber({
num: randomPagerNumber, num: randomPagerNumber,
individualAssignID: member.id, individualAssignID: member.id,
emailAddresses: [acknowledgement.emailAddress], emailAddresses: [acknowledgement.emailAddress],
discordIDs: [member.id], discordIDs: [member.id],
}); });
if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return; if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return;
logNewPager(client, randomPagerNumber, member); logNewPager(client, randomPagerNumber, member);
pager = await newNumber.save(); pager = await newNumber.save();
client.getDMChannel(member.id).then((chan) => { client.getDMChannel(member.id).then((chan) => {
chan.createMessage(`__**Pager Number Creation**__\nYour individual pager number has been automatically created. Your number (PN) is ${randomPagerNumber}.`); 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')) { } else if (!pager && member.roles.includes('701481967149121627')) {
// Associates // Associates
let randomPagerNumber: string; let randomPagerNumber: string;
let status = true; let status = true;
// eslint-disable-next-line no-constant-condition // eslint-disable-next-line no-constant-condition
while (status) { while (status) {
randomPagerNumber = `22${String(Math.floor(Math.random() * 999) + 1)}`; randomPagerNumber = `22${String(Math.floor(Math.random() * 999) + 1)}`;
if (randomPagerNumber.length === 4) randomPagerNumber = `${randomPagerNumber}0`; if (randomPagerNumber.length === 4) randomPagerNumber = `${randomPagerNumber}0`;
const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber }); const check = await client.db.PagerNumber.findOne({ num: randomPagerNumber });
if (check) status = false; if (check) status = false;
if (check?.num !== randomPagerNumber) status = false; if (check?.num !== randomPagerNumber) status = false;
} }
const acknowledgement = resolveStaffInformation(member.id); const acknowledgement = resolveStaffInformation(member.id);
if (!acknowledgement || !acknowledgement.emailAddress) return; if (!acknowledgement || !acknowledgement.emailAddress) return;
const newNumber = new client.db.PagerNumber({ const newNumber = new client.db.PagerNumber({
num: randomPagerNumber, num: randomPagerNumber,
individualAssignID: member.id, individualAssignID: member.id,
emailAddresses: [acknowledgement.emailAddress], emailAddresses: [acknowledgement.emailAddress],
discordIDs: [member.id], discordIDs: [member.id],
}); });
if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return; if (await client.db.PagerNumber.findOne({ num: randomPagerNumber })) return;
logNewPager(client, randomPagerNumber, member); logNewPager(client, randomPagerNumber, member);
pager = await newNumber.save(); pager = await newNumber.save();
client.getDMChannel(member.id).then((chan) => { client.getDMChannel(member.id).then((chan) => {
chan.createMessage(`__**Pager Number Creation**__\nYour individual pager number has been automatically created. Your number (PN) is ${randomPagerNumber}.`); chan.createMessage(`__**Pager Number Creation**__\nYour individual pager number has been automatically created. Your number (PN) is ${randomPagerNumber}.`);
}); });
} }
} }
// Associates // Associates
const associatePagers = await client.db.PagerNumber.findOne({ num: '22' }); const associatePagers = await client.db.PagerNumber.findOne({ num: '22' });
for (const member of members.values()) { for (const member of members.values()) {
if (member.roles.includes('701481967149121627') && !associatePagers.discordIDs.includes(member.id)) { if (member.roles.includes('701481967149121627') && !associatePagers.discordIDs.includes(member.id)) {
await associatePagers.updateOne({ $addToSet: { discordIDs: member.id } }); await associatePagers.updateOne({ $addToSet: { discordIDs: member.id } });
const acknowledgement = resolveStaffInformation(member.id); const acknowledgement = resolveStaffInformation(member.id);
if (acknowledgement?.emailAddress) await associatePagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } }); if (acknowledgement?.emailAddress) await associatePagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } });
} }
if (!member.roles.includes('701481967149121627') && associatePagers.discordIDs.includes(member.id)) { if (!member.roles.includes('701481967149121627') && associatePagers.discordIDs.includes(member.id)) {
await associatePagers.updateOne({ $pull: { discordIDs: member.id } }); await associatePagers.updateOne({ $pull: { discordIDs: member.id } });
const acknowledgement = resolveStaffInformation(member.id); const acknowledgement = resolveStaffInformation(member.id);
if (acknowledgement?.emailAddress) await associatePagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } }); if (acknowledgement?.emailAddress) await associatePagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } });
} }
} }
// Core Team // Core Team
const coreTeamPagers = await client.db.PagerNumber.findOne({ num: '21' }); const coreTeamPagers = await client.db.PagerNumber.findOne({ num: '21' });
for (const member of members.values()) { for (const member of members.values()) {
if (member.roles.includes('453689940140883988') && !coreTeamPagers.discordIDs.includes(member.id)) { if (member.roles.includes('453689940140883988') && !coreTeamPagers.discordIDs.includes(member.id)) {
await coreTeamPagers.updateOne({ $addToSet: { discordIDs: member.id } }); await coreTeamPagers.updateOne({ $addToSet: { discordIDs: member.id } });
const acknowledgement = resolveStaffInformation(member.id); const acknowledgement = resolveStaffInformation(member.id);
if (acknowledgement?.emailAddress) await coreTeamPagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } }); if (acknowledgement?.emailAddress) await coreTeamPagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } });
} }
if (!member.roles.includes('453689940140883988') && coreTeamPagers.discordIDs.includes(member.id)) { if (!member.roles.includes('453689940140883988') && coreTeamPagers.discordIDs.includes(member.id)) {
await coreTeamPagers.updateOne({ $pull: { discordIDs: member.id } }); await coreTeamPagers.updateOne({ $pull: { discordIDs: member.id } });
const acknowledgement = resolveStaffInformation(member.id); const acknowledgement = resolveStaffInformation(member.id);
if (acknowledgement?.emailAddress) await coreTeamPagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } }); if (acknowledgement?.emailAddress) await coreTeamPagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } });
} }
} }
// Moderator // Moderator
const moderatorPagers = await client.db.PagerNumber.findOne({ num: '20' }); const moderatorPagers = await client.db.PagerNumber.findOne({ num: '20' });
for (const member of members.values()) { for (const member of members.values()) {
if (member.roles.includes('455972169449734144') && !moderatorPagers.discordIDs.includes(member.id)) { if (member.roles.includes('455972169449734144') && !moderatorPagers.discordIDs.includes(member.id)) {
await moderatorPagers.updateOne({ $addToSet: { discordIDs: member.id } }); await moderatorPagers.updateOne({ $addToSet: { discordIDs: member.id } });
const acknowledgement = resolveStaffInformation(member.id); const acknowledgement = resolveStaffInformation(member.id);
if (acknowledgement?.emailAddress) await moderatorPagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } }); if (acknowledgement?.emailAddress) await moderatorPagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } });
} }
if (!member.roles.includes('455972169449734144') && moderatorPagers.discordIDs.includes(member.id)) { if (!member.roles.includes('455972169449734144') && moderatorPagers.discordIDs.includes(member.id)) {
await moderatorPagers.updateOne({ $pull: { discordIDs: member.id } }); await moderatorPagers.updateOne({ $pull: { discordIDs: member.id } });
const acknowledgement = resolveStaffInformation(member.id); const acknowledgement = resolveStaffInformation(member.id);
if (acknowledgement?.emailAddress) await moderatorPagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } }); if (acknowledgement?.emailAddress) await moderatorPagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } });
} }
} }
// Technician // Technician
const technicianPagers = await client.db.PagerNumber.findOne({ num: '10' }); const technicianPagers = await client.db.PagerNumber.findOne({ num: '10' });
for (const member of members.values()) { for (const member of members.values()) {
if (member.roles.includes('701454780828221450') && !technicianPagers.discordIDs.includes(member.id)) { if (member.roles.includes('701454780828221450') && !technicianPagers.discordIDs.includes(member.id)) {
await technicianPagers.updateOne({ $addToSet: { discordIDs: member.id } }); await technicianPagers.updateOne({ $addToSet: { discordIDs: member.id } });
const acknowledgement = resolveStaffInformation(member.id); const acknowledgement = resolveStaffInformation(member.id);
if (acknowledgement?.emailAddress) await technicianPagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } }); if (acknowledgement?.emailAddress) await technicianPagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } });
} }
if (!member.roles.includes('701454780828221450') && technicianPagers.discordIDs.includes(member.id)) { if (!member.roles.includes('701454780828221450') && technicianPagers.discordIDs.includes(member.id)) {
await technicianPagers.updateOne({ $pull: { discordIDs: member.id } }); await technicianPagers.updateOne({ $pull: { discordIDs: member.id } });
const acknowledgement = resolveStaffInformation(member.id); const acknowledgement = resolveStaffInformation(member.id);
if (acknowledgement?.emailAddress) await technicianPagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } }); if (acknowledgement?.emailAddress) await technicianPagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } });
} }
} }
// Supervisor // Supervisor
const supervisorPagers = await client.db.PagerNumber.findOne({ num: '01' }); const supervisorPagers = await client.db.PagerNumber.findOne({ num: '01' });
for (const member of members.values()) { for (const member of members.values()) {
if (member.roles.includes('701454855952138300') && !supervisorPagers.discordIDs.includes(member.id)) { if (member.roles.includes('701454855952138300') && !supervisorPagers.discordIDs.includes(member.id)) {
await supervisorPagers.updateOne({ $addToSet: { discordIDs: member.id } }); await supervisorPagers.updateOne({ $addToSet: { discordIDs: member.id } });
const acknowledgement = resolveStaffInformation(member.id); const acknowledgement = resolveStaffInformation(member.id);
if (acknowledgement?.emailAddress) await supervisorPagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } }); if (acknowledgement?.emailAddress) await supervisorPagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } });
} }
if (!member.roles.includes('701454855952138300') && supervisorPagers.discordIDs.includes(member.id)) { if (!member.roles.includes('701454855952138300') && supervisorPagers.discordIDs.includes(member.id)) {
await supervisorPagers.updateOne({ $pull: { discordIDs: member.id } }); await supervisorPagers.updateOne({ $pull: { discordIDs: member.id } });
const acknowledgement = resolveStaffInformation(member.id); const acknowledgement = resolveStaffInformation(member.id);
if (acknowledgement?.emailAddress) await supervisorPagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } }); if (acknowledgement?.emailAddress) await supervisorPagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } });
} }
} }
// Board of Directors // Board of Directors
const directorPagers = await client.db.PagerNumber.findOne({ num: '00' }); const directorPagers = await client.db.PagerNumber.findOne({ num: '00' });
for (const member of members.values()) { for (const member of members.values()) {
if (member.roles.includes('662163685439045632') && !directorPagers.discordIDs.includes(member.id)) { if (member.roles.includes('662163685439045632') && !directorPagers.discordIDs.includes(member.id)) {
await directorPagers.updateOne({ $addToSet: { discordIDs: member.id } }); await directorPagers.updateOne({ $addToSet: { discordIDs: member.id } });
const acknowledgement = resolveStaffInformation(member.id); const acknowledgement = resolveStaffInformation(member.id);
if (acknowledgement?.emailAddress) await directorPagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } }); if (acknowledgement?.emailAddress) await directorPagers.updateOne({ $addToSet: { emailAddresses: acknowledgement.emailAddress } });
} }
if (!member.roles.includes('662163685439045632') && directorPagers.discordIDs.includes(member.id)) { if (!member.roles.includes('662163685439045632') && directorPagers.discordIDs.includes(member.id)) {
await directorPagers.updateOne({ $pull: { discordIDs: member.id } }); await directorPagers.updateOne({ $pull: { discordIDs: member.id } });
const acknowledgement = resolveStaffInformation(member.id); const acknowledgement = resolveStaffInformation(member.id);
if (acknowledgement?.emailAddress) await directorPagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } }); if (acknowledgement?.emailAddress) await directorPagers.updateOne({ $pull: { emailAddresses: acknowledgement.emailAddress } });
} }
} }
}, 300000); }, 300000);
return interval; return interval;
} }

View File

@ -1,11 +1,11 @@
import { Client } from '../class'; import { Client } from '../class';
let interval: NodeJS.Timeout; let interval: NodeJS.Timeout;
export default async function fetchMembers(client: Client): Promise<NodeJS.Timeout> { export default async function fetchMembers(client: Client): Promise<NodeJS.Timeout> {
await client.guilds.get(client.config.guildID)?.fetchAllMembers(); await client.guilds.get(client.config.guildID)?.fetchAllMembers();
interval = setInterval(async () => { interval = setInterval(async () => {
// await client.guilds.get(client.config.guildID).fetchAllMembers(); // await client.guilds.get(client.config.guildID).fetchAllMembers();
}, 1800000); }, 1800000);
return interval; return interval;
} }

View File

@ -1,23 +1,23 @@
/* DM Ramirez with the code below to claim 500 free Rubies! /* DM Ramirez with the code below to claim 500 free Rubies!
d2c3d8e14b d2c3d8e14b
*/ */
import { parse } from 'yaml'; import { parse } from 'yaml';
import { promises as fs } from 'fs'; import { promises as fs } from 'fs';
import { Client } from './class'; import { Client } from './class';
import * as eventFiles from './events'; import * as eventFiles from './events';
import * as commandFiles from './commands'; import * as commandFiles from './commands';
async function main(): Promise<void> { async function main(): Promise<void> {
const read = await fs.readFile('../config.yaml', 'utf8'); const read = await fs.readFile('../config.yaml', 'utf8');
const config: { token: string, prefix: string, guildID: string, mongoDB: string, emailPass: string } = parse(read); const config: { token: string, prefix: string, guildID: string, mongoDB: string, emailPass: string } = parse(read);
const client = new Client(config.token, { defaultImageFormat: 'png', restMode: true }); const client = new Client(config.token, { defaultImageFormat: 'png', restMode: true });
client.config = config; client.config = config;
await client.loadDatabase(); await client.loadDatabase();
client.loadPlugins(); client.loadPlugins();
await client.loadIntervals(); await client.loadIntervals();
await client.loadEvents(eventFiles); await client.loadEvents(eventFiles);
await client.loadCommands(commandFiles); await client.loadCommands(commandFiles);
client.connect(); client.connect();
} }
main(); main();

View File

@ -1,19 +1,19 @@
import { Document, Schema, model } from 'mongoose'; import { Document, Schema, model } from 'mongoose';
export interface MemberInterface extends Document { export interface MemberInterface extends Document {
userID: string userID: string
additional: { additional: {
langs: ['js', 'py', 'rb', 'ts', 'rs', 'go', 'cfam', 'csharp', 'swift', 'java', 'kt', 'asm'], langs: ['js', 'py', 'rb', 'ts', 'rs', 'go', 'cfam', 'csharp', 'swift', 'java', 'kt', 'asm'],
operatingSystems: ['arch', 'deb', 'cent', 'fedora', 'manjaro', 'mdarwin', 'redhat', 'ubuntu', 'win'], operatingSystems: ['arch', 'deb', 'cent', 'fedora', 'manjaro', 'mdarwin', 'redhat', 'ubuntu', 'win'],
}, },
} }
const Member: Schema = new Schema({ const Member: Schema = new Schema({
userID: String, userID: String,
additional: { additional: {
langs: Array, langs: Array,
operatingSystems: Array, operatingSystems: Array,
}, },
}); });
export default model<MemberInterface>('Member', Member); export default model<MemberInterface>('Member', Member);

View File

@ -1,37 +1,37 @@
import { Document, Schema, model } from 'mongoose'; import { Document, Schema, model } from 'mongoose';
export interface ModerationInterface extends Document { export interface ModerationInterface extends Document {
userID: string, userID: string,
logID: string, logID: string,
moderatorID: string, moderatorID: string,
reason: string, reason: string,
/** /**
* @field 0 - Warn * @field 0 - Warn
* @field 1 - Unmute * @field 1 - Unmute
* @field 2 - Mute * @field 2 - Mute
* @field 3 - Kick * @field 3 - Kick
* @field 4 - Unban * @field 4 - Unban
* @field 5 - Ban * @field 5 - Ban
*/ */
type: 0 | 1 | 2 | 3 | 4 | 5 type: 0 | 1 | 2 | 3 | 4 | 5
date: Date, date: Date,
expiration: { expiration: {
date: Date, date: Date,
processed: boolean processed: boolean
} }
} }
const Moderation: Schema = new Schema({ const Moderation: Schema = new Schema({
userID: String, userID: String,
logID: String, logID: String,
moderatorID: String, moderatorID: String,
reason: String, reason: String,
type: Number, type: Number,
date: Date, date: Date,
expiration: { expiration: {
date: Date, date: Date,
processed: Boolean, processed: Boolean,
}, },
}); });
export default model<ModerationInterface>('Moderation', Moderation); export default model<ModerationInterface>('Moderation', Moderation);

View File

@ -1,26 +1,26 @@
import { Document, Schema, model } from 'mongoose'; import { Document, Schema, model } from 'mongoose';
export interface PagerNumberRaw { export interface PagerNumberRaw {
num: string, num: string,
// This field will be "" if the pager number doesn't belong to an individual user // This field will be "" if the pager number doesn't belong to an individual user
individualAssignID: string, individualAssignID: string,
emailAddresses: string[], emailAddresses: string[],
discordIDs: string[], discordIDs: string[],
} }
export interface PagerNumberInterface extends Document { export interface PagerNumberInterface extends Document {
num: string, num: string,
// This field will be "" if the pager number doesn't belong to an individual user // This field will be "" if the pager number doesn't belong to an individual user
individualAssignID: string, individualAssignID: string,
emailAddresses: string[], emailAddresses: string[],
discordIDs: string[], discordIDs: string[],
} }
const PagerNumber: Schema = new Schema({ const PagerNumber: Schema = new Schema({
num: String, num: String,
individualAssignID: String, individualAssignID: String,
emailAddresses: Array, emailAddresses: Array,
discordIDs: Array, discordIDs: Array,
}); });
export default model<PagerNumberInterface>('PagerNumber', PagerNumber); export default model<PagerNumberInterface>('PagerNumber', PagerNumber);

17
src/models/Rank.ts Normal file
View File

@ -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<RankInterface>('Rank', Rank);

View File

@ -1,21 +1,21 @@
import { Document, Schema, model } from 'mongoose'; import { Document, Schema, model } from 'mongoose';
export interface RedirectInterface extends Document { export interface RedirectInterface extends Document {
key: string, key: string,
to: string, to: string,
visitedCount: number, visitedCount: number,
} }
export interface RedirectRaw { export interface RedirectRaw {
key: string, key: string,
to: string, to: string,
visitedCount: number, visitedCount: number,
} }
const Redirect: Schema = new Schema({ const Redirect: Schema = new Schema({
key: String, key: String,
to: String, to: String,
visitedCount: Number, visitedCount: Number,
}); });
export default model<RedirectInterface>('Redirect', Redirect); export default model<RedirectInterface>('Redirect', Redirect);

View File

@ -1,4 +1,5 @@
export { default as Member, MemberInterface } from './Member'; export { default as Member, MemberInterface } from './Member';
export { default as Moderation, ModerationInterface } from './Moderation'; export { default as Moderation, ModerationInterface } from './Moderation';
export { default as PagerNumber, PagerNumberInterface, PagerNumberRaw } from './PagerNumber'; export { default as PagerNumber, PagerNumberInterface, PagerNumberRaw } from './PagerNumber';
export { default as Redirect, RedirectInterface, RedirectRaw } from './Redirect'; export { default as Rank, RankInterface } from './Rank';
export { default as Redirect, RedirectInterface, RedirectRaw } from './Redirect';