Merge branch 'dev'

merge-requests/13/head
Matthew 2020-05-22 01:51:59 -04:00
commit 7c0c777ad3
No known key found for this signature in database
GPG Key ID: D499B75C1390E321
38 changed files with 179 additions and 169 deletions

View File

@ -39,6 +39,7 @@
"import/prefer-default-export": "off", "import/prefer-default-export": "off",
"no-useless-constructor": "off", "no-useless-constructor": "off",
"@typescript-eslint/no-useless-constructor": 2, "@typescript-eslint/no-useless-constructor": 2,
"import/extensions": "off" "import/extensions": "off",
"no-param-reassign": "off"
} }
} }

23
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,23 @@
stages:
- lint
- build
lint:
stage: lint
script: |
yarn install
yarn lint
only:
- pushes
- merge_requests
- web
tsc:
stage: build
script: |
yarn install
tsc -p tsconfig.json -noEmit
only:
- pushes
- merge_requests
- web

View File

@ -4,7 +4,7 @@ clean:
@-rm -rf build @-rm -rf build
build: build:
-tsc -p ./tsconfig.json -npx tsc -p ./tsconfig.json
run: run:
cd build && node main cd build && node main

5
src/api/index.ts Normal file
View File

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

View File

@ -1,6 +1,6 @@
import { Server, ServerManagement } from '../../class'; import { Server, ServerManagement } from '../../class';
export default function (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

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

View File

@ -1,16 +1,18 @@
import { Route, Server } from '../../../class'; import { Route, Server } from '../../../class';
import { RedirectInterface } 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, { path: '/' }); super(server);
this.conf = {
path: '/',
};
} }
public bind() { public bind() {
this.router.get('/:key', async (req, res) => { this.router.get('/:key', async (req, res) => {
try { try {
// @ts-ignore const link: RedirectRaw = await this.server.client.db.Redirect.findOne({ key: req.params.key }).lean().exec();
const link: RedirectInterface = 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 });
return res.redirect(link.to); return res.redirect(link.to);
} catch (err) { } catch (err) {

View File

@ -1 +0,0 @@

View File

@ -1,7 +1,7 @@
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 } from '.'; import { Collection, Command, Util, ServerManagement, Event } from '.';
import { Member, MemberInterface, Moderation, ModerationInterface, Redirect, RedirectInterface } from '../models'; import { Member, MemberInterface, Moderation, ModerationInterface, Redirect, RedirectInterface } from '../models';
export default class Client extends eris.Client { export default class Client extends eris.Client {
@ -9,7 +9,7 @@ export default class Client extends eris.Client {
public commands: Collection<Command>; public commands: Collection<Command>;
public events: Collection<Function>; public events: Collection<Event>;
public intervals: Collection<NodeJS.Timeout>; public intervals: Collection<NodeJS.Timeout>;
@ -17,15 +17,14 @@ export default class Client extends eris.Client {
public serverManagement: ServerManagement; public serverManagement: ServerManagement;
public db: { member: mongoose.Model<MemberInterface>, moderation: mongoose.Model<ModerationInterface>, redirect: mongoose.Model<RedirectInterface> }; public db: { Member: mongoose.Model<MemberInterface>, Moderation: mongoose.Model<ModerationInterface>, Redirect: mongoose.Model<RedirectInterface> };
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
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<Function>(); this.events = new Collection<Event>();
this.intervals = new Collection<NodeJS.Timeout>(); this.intervals = new Collection<NodeJS.Timeout>();
this.db = { member: Member, moderation: Moderation, redirect: Redirect }; this.db = { Member, Moderation, Redirect };
} }
public async loadDatabase() { public async loadDatabase() {
@ -48,27 +47,23 @@ export default class Client extends eris.Client {
}); });
} }
public async loadEvents() { public async loadEvents(eventFiles: { [s: string]: typeof Event; } | ArrayLike<typeof Event>) {
const evtFiles = await fs.readdir(`${__dirname}/../events`); const evtFiles = Object.entries<typeof Event>(eventFiles);
evtFiles.forEach((file) => { for (const [name, Ev] of evtFiles) {
const eventName = file.split('.')[0]; const event = new Ev(this);
if (file === 'index.js') return; this.events.add(event.event, event);
// eslint-disable-next-line this.on(event.event, event.run);
const event = new (require(`${__dirname}/../events/${file}`).default)(this); this.util.signale.success(`Successfully loaded event: ${name}`);
this.events.add(eventName, event); delete require.cache[require.resolve(`${__dirname}/../events/${name}`)];
this.on(eventName, (...args) => event.run(...args)); }
this.util.signale.success(`Successfully loaded event: ${eventName}`);
delete require.cache[require.resolve(`${__dirname}/../events/${file}`)];
});
} }
public async loadCommands() { public async loadCommands(commandFiles: { [s: string]: typeof Command; } | ArrayLike<typeof Command>) {
const commandFiles = await fs.readdir(`${__dirname}/../commands`); const cmdFiles = Object.values<typeof Command>(commandFiles);
commandFiles.forEach((file) => { for (const Cmd of cmdFiles) {
// eslint-disable-next-line new-cap const command = new Cmd(this);
const command: Command = new (require(`${__dirname}/../commands/${file}`).default)(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}`);
}); }
} }
} }

15
src/class/Event.ts Normal file
View File

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

View File

@ -111,7 +111,7 @@ export default class Moderation {
return mod.save(); return mod.save();
} }
public async kick(user: 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');

View File

@ -1,20 +1,8 @@
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
export interface EmbedData { import { EmbedOptions } from 'eris';
title?: string
description?: string
url?: string
timestamp?: Date
color?: number
footer?: { text: string, icon_url?: string, proxy_icon_url?: string}
image?: { url: string, proxy_url?: string, height?: number, width?: number }
thumbnail?: { url: string, proxy_url?: string, height?: number, width?: number }
video?: { url: string, height?: number, width?: number }
author?: { name: string, url?: string, proxy_icon_url?: string, icon_url?: string}
fields?: {name: string, value: string, inline?: boolean}[]
}
export default class RichEmbed implements EmbedData { export default class RichEmbed implements EmbedOptions {
title?: string title?: string
type?: string type?: string
@ -23,15 +11,15 @@ export default class RichEmbed implements EmbedData {
url?: string url?: string
timestamp?: 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 }
@ -41,7 +29,7 @@ export default class RichEmbed implements EmbedData {
fields?: {name: string, value: string, inline?: boolean}[] fields?: {name: string, value: string, inline?: boolean}[]
constructor(data: EmbedData = {}) { constructor(data: EmbedOptions = {}) {
/* /*
let types: { let types: {
title?: string, type?: string, description?: string, url?: string, timestamp?: Date, color?: number, fields?: {name: string, value: string, inline?: boolean}[] title?: string, type?: string, description?: string, url?: string, timestamp?: Date, color?: number, fields?: {name: string, value: string, inline?: boolean}[]
@ -124,8 +112,7 @@ export default class RichEmbed implements EmbedData {
* Sets the timestamp of this embed. * Sets the timestamp of this embed.
*/ */
setTimestamp(timestamp = new Date()) { setTimestamp(timestamp = new Date()) {
// eslint-disable-next-line no-restricted-globals if (Number.isNaN(timestamp.getTime())) throw new TypeError('Expecting ISO8601 (Date constructor)');
if (isNaN(timestamp.getTime())) throw new TypeError('Expecting ISO8601 (Date constructor)');
this.timestamp = timestamp; this.timestamp = timestamp;
return this; return this;
} }

View File

@ -9,9 +9,9 @@ export default class Route {
public router: Router; public router: Router;
constructor(server: Server, options: { path: string, deprecated?: boolean, maintenance?: boolean }) { constructor(server: Server) {
this.server = server; this.server = server;
this.conf = options; this.conf = { path: '' };
this.router = Router(); this.router = Router();
} }

View File

@ -31,10 +31,9 @@ export default class Server {
} }
public async loadRoutes() { public async loadRoutes() {
const routes = await fs.readdir(`${this.root}`); const routes = Object.values<typeof Route>(require(this.root));
for (const routeFile of routes) { for (const RouteFile of routes) {
// eslint-disable-next-line new-cap const route = new RouteFile(this);
const route: Route = new (require(`${this.root}/${routeFile}`).default)(this);
if (route.conf.deprecated) { if (route.conf.deprecated) {
route.deprecated(); route.deprecated();
} else if (route.conf.maintenance) { } else if (route.conf.maintenance) {

View File

@ -1,7 +1,5 @@
import express from 'express';
import { promises as fs } from 'fs';
import { Client, Collection, Server } from '.'; import { Client, Collection, Server } from '.';
// import serverSetup from '../api/server'; import serverSetup from '../api';
export default class ServerManagement { export default class ServerManagement {
public client: Client; public client: Client;
@ -15,12 +13,9 @@ export default class ServerManagement {
} }
public async loadServers() { public async loadServers() {
const apiRoot = await fs.readdir(`${__dirname}/../api`); const apiRoot = Object.entries<(management: ServerManagement) => Server>(serverSetup);
for (const api of apiRoot) { for (const [api, server] of apiRoot) {
// eslint-disable-next-line no-continue this.servers.add(api, server(this));
if (api === 'server.js') continue;
const server: Server = require(`${__dirname}/../api/${api}/main.js`).default(this);
this.servers.add(api, server);
this.client.util.signale.success(`Successfully loaded server '${api}'.`); this.client.util.signale.success(`Successfully loaded server '${api}'.`);
} }
} }

View File

@ -37,8 +37,6 @@ export default class Util {
*/ */
public resolveCommand(query: string | string[]): Promise<{cmd: Command, args: string[] }> { public resolveCommand(query: string | string[]): Promise<{cmd: Command, args: string[] }> {
try { try {
// let resolvedCommand: Command;
// eslint-disable-next-line no-param-reassign
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()));
@ -91,7 +89,6 @@ export default class Util {
} }
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) : [];
// eslint-disable-next-line no-param-reassign
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) {
@ -101,7 +98,6 @@ export default class Util {
public splitString(string: string, length: number): string[] { public splitString(string: string, length: number): string[] {
if (!string) return []; if (!string) return [];
// eslint-disable-next-line no-param-reassign
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[] = [];
@ -111,7 +107,6 @@ export default class Util {
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);
// eslint-disable-next-line no-param-reassign
string = string.substr(pos); string = string.substr(pos);
arrayString.push(str); arrayString.push(str);
} }

View File

@ -1,6 +1,7 @@
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 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';

View File

@ -23,10 +23,9 @@ export default class AddItem extends Command {
return message.channel.createMessage({ embed }); return message.channel.createMessage({ embed });
} }
if (args[0].split('-')[0] === 'os' && ['arch', 'deb', 'cent', 'fedora', 'manjaro', 'mdarwin', 'redhat', 'ubuntu', 'win'].includes(args[0].split('-')[1])) { if (args[0].split('-')[0] === 'os' && ['arch', 'deb', 'cent', 'fedora', 'manjaro', 'mdarwin', 'redhat', 'ubuntu', 'win'].includes(args[0].split('-')[1])) {
const account = await this.client.db.member.findOne({ userID: message.member.id }); const account = await this.client.db.Member.findOne({ userID: message.member.id });
if (!account) { if (!account) {
// eslint-disable-next-line new-cap const newAccount = new this.client.db.Member({
const newAccount = new this.client.db.member({
userID: message.member.id, userID: message.member.id,
additional: { additional: {
operatingSystems: [args[0].split('-')[1]], operatingSystems: [args[0].split('-')[1]],
@ -39,10 +38,9 @@ export default class AddItem extends Command {
return message.channel.createMessage(`***${this.client.util.emojis.SUCCESS} Added OS code ${args[0]} to profile.***`); return message.channel.createMessage(`***${this.client.util.emojis.SUCCESS} Added OS code ${args[0]} to profile.***`);
} }
if (args[0].split('-')[0] === 'lang' && ['js', 'py', 'rb', 'ts', 'rs', 'go', 'cfam', 'csharp', 'swift', 'java', 'kt', 'asm'].includes(args[0].split('-')[1])) { if (args[0].split('-')[0] === 'lang' && ['js', 'py', 'rb', 'ts', 'rs', 'go', 'cfam', 'csharp', 'swift', 'java', 'kt', 'asm'].includes(args[0].split('-')[1])) {
const account = await this.client.db.member.findOne({ userID: message.member.id }); const account = await this.client.db.Member.findOne({ userID: message.member.id });
if (!account) { if (!account) {
// eslint-disable-next-line new-cap const newAccount = new this.client.db.Member({
const newAccount = new this.client.db.member({
userID: message.member.id, userID: message.member.id,
additional: { additional: {
langs: [args[0].split('-')[1]], langs: [args[0].split('-')[1]],

View File

@ -15,7 +15,7 @@ export default class AddRedirect extends Command {
public async run(message: Message, args: string[]) { public async run(message: Message, args: string[]) {
try { try {
if (!args[0]) return this.client.commands.get('help').run(message, [this.name]); if (!args[0]) return this.client.commands.get('help').run(message, [this.name]);
const check = await this.client.db.redirect.findOne({ key: args[1].toLowerCase() }); const check = await this.client.db.Redirect.findOne({ key: args[1].toLowerCase() });
if (check) return this.error(message.channel, `Redirect key ${args[1].toLowerCase()} already exists. Linked to: ${check.to}`); if (check) return this.error(message.channel, `Redirect key ${args[1].toLowerCase()} already exists. Linked to: ${check.to}`);
try { try {
const test = new URL(args[0]); const test = new URL(args[0]);
@ -24,8 +24,7 @@ export default class AddRedirect extends Command {
return this.error(message.channel, 'This doesn\'t appear to be a valid URL.'); return this.error(message.channel, 'This doesn\'t appear to be a valid URL.');
} }
if ((/^[a-zA-Z0-9]+$/gi.test(args[1].toLowerCase().replace('-', '').trim()) === false) || args[1].toLowerCase().length > 15) return this.error(message.channel, 'Invalid key. The key must be alphanumeric and less than 16 characters.'); if ((/^[a-zA-Z0-9]+$/gi.test(args[1].toLowerCase().replace('-', '').trim()) === false) || args[1].toLowerCase().length > 15) return this.error(message.channel, 'Invalid key. The key must be alphanumeric and less than 16 characters.');
// eslint-disable-next-line new-cap const redirect = new this.client.db.Redirect({
const redirect = new this.client.db.redirect({
key: args[1].toLowerCase(), key: args[1].toLowerCase(),
to: args[0], to: args[0],
}); });

View File

@ -23,16 +23,16 @@ export default class DelItem extends Command {
return message.channel.createMessage({ embed }); return message.channel.createMessage({ embed });
} }
if (args[0].split('-')[0] === 'os' && ['arch', 'deb', 'cent', 'fedora', 'manjaro', 'mdarwin', 'redhat', 'ubuntu', 'win'].includes(args[0].split('-')[1])) { if (args[0].split('-')[0] === 'os' && ['arch', 'deb', 'cent', 'fedora', 'manjaro', 'mdarwin', 'redhat', 'ubuntu', 'win'].includes(args[0].split('-')[1])) {
const account = await this.client.db.member.findOne({ userID: message.member.id }); const account = await this.client.db.Member.findOne({ userID: message.member.id });
if (!account || !account?.additional.operatingSystems || account?.additional.operatingSystems.length < 1) { if (account?.additional.operatingSystems.length < 1) {
return message.channel.createMessage(`***${this.client.util.emojis.ERROR} You don't have any operating systems to remove.***`); return message.channel.createMessage(`***${this.client.util.emojis.ERROR} You don't have any operating systems to remove.***`);
} }
await account.updateOne({ $pull: { 'additional.operatingSystems': args[0].split('-')[1] } }); await account.updateOne({ $pull: { 'additional.operatingSystems': args[0].split('-')[1] } });
return message.channel.createMessage(`***${this.client.util.emojis.SUCCESS} Removed OS code ${args[0]} from profile.***`); return message.channel.createMessage(`***${this.client.util.emojis.SUCCESS} Removed OS code ${args[0]} from profile.***`);
} }
if (args[0].split('-')[0] === 'lang' && ['js', 'py', 'rb', 'ts', 'rs', 'go', 'cfam', 'csharp', 'swift', 'java', 'kt', 'asm'].includes(args[0].split('-')[1])) { if (args[0].split('-')[0] === 'lang' && ['js', 'py', 'rb', 'ts', 'rs', 'go', 'cfam', 'csharp', 'swift', 'java', 'kt', 'asm'].includes(args[0].split('-')[1])) {
const account = await this.client.db.member.findOne({ userID: message.member.id }); const account = await this.client.db.Member.findOne({ userID: message.member.id });
if (!account || !account?.additional.langs || account?.additional.langs.length < 1) { if (account?.additional.langs.length < 1) {
return message.channel.createMessage(`***${this.client.util.emojis.ERROR} You don't have any languages to remove.***`); return message.channel.createMessage(`***${this.client.util.emojis.ERROR} You don't have any languages to remove.***`);
} }
await account.updateOne({ $pull: { 'additional.langs': args[0].split('-')[1] } }); await account.updateOne({ $pull: { 'additional.langs': args[0].split('-')[1] } });

View File

@ -15,9 +15,9 @@ export default class DelRedirect extends Command {
public async run(message: Message, args: string[]) { public async run(message: Message, args: string[]) {
try { try {
if (!args[0]) return this.client.commands.get('help').run(message, [this.name]); if (!args[0]) return this.client.commands.get('help').run(message, [this.name]);
const check = await this.client.db.redirect.findOne({ key: args[0].toLowerCase() }); const check = await this.client.db.Redirect.findOne({ key: args[0].toLowerCase() });
if (!check) return this.error(message.channel, `Redirect key ${args[0].toLowerCase()} doesn't exist.`); if (!check) return this.error(message.channel, `Redirect key ${args[0].toLowerCase()} doesn't exist.`);
await this.client.db.redirect.deleteOne({ key: args[0].toLowerCase() }); await this.client.db.Redirect.deleteOne({ key: args[0].toLowerCase() });
return this.success(message.channel, `Deleted redirect https://loc.sh/${args[0].toLowerCase()}.`); return this.success(message.channel, `Deleted redirect https://loc.sh/${args[0].toLowerCase()}.`);
} catch (err) { } catch (err) {
return this.client.util.handleError(err, message, this); return this.client.util.handleError(err, message, this);

View File

@ -1,5 +1,5 @@
import { Message } from 'eris'; import { Message, EmbedOptions } from 'eris';
import axios from 'axios'; import axios, { AxiosResponse } from 'axios';
import { Client, Command, RichEmbed } from '../class'; import { Client, Command, RichEmbed } from '../class';
export default class DJS extends Command { export default class DJS extends Command {
@ -17,34 +17,10 @@ export default class DJS extends Command {
try { try {
if (!args[0]) return this.client.commands.get('help').run(message, [this.name]); if (!args[0]) return this.client.commands.get('help').run(message, [this.name]);
let res; const { data }: AxiosResponse<EmbedOptions> = await axios.get(`https://djsdocs.sorta.moe/v2/embed?src=master&q=${args[0]}`);
try {
res = await axios.get(`https://djsdocs.sorta.moe/v2/embed?src=master&q=${args[0]}`);
} catch (err) {
this.error(message.channel, 'Please try again later, something unexpected happened.');
return this.client.util.handleError(err, message, this);
}
const { data } = res;
if (!data) return this.error(message.channel, 'Could not find information. Try something else.'); if (!data) return this.error(message.channel, 'Could not find information. Try something else.');
const name: string = data.author?.name || ''; const embed = new RichEmbed(data);
const icon_url: string = data.author?.icon_url || '';
const author_url: string = data.author?.url || '';
const description: string = data.description || 'None';
const title: string = data.title || '';
const embed = new RichEmbed();
embed.setAuthor(name, icon_url, author_url);
embed.setColor(0x2296f3);
embed.setTitle(title);
embed.setDescription(description);
if (data.fields !== undefined && data.fields.length > 0) {
data.fields.forEach((field) => {
embed.addField(field.name, field.value);
});
}
embed.setFooter(this.client.user.username, this.client.user.avatarURL);
embed.setTimestamp();
return message.channel.createMessage({ embed }); return message.channel.createMessage({ embed });
} catch (err) { } catch (err) {
return this.client.util.handleError(err, message, this); return this.client.util.handleError(err, message, this);

View File

@ -47,13 +47,9 @@ export default class Game extends Command {
embed.setColor('#1ed760'); embed.setColor('#1ed760');
embed.addField('Song', mainStatus.details, true); embed.addField('Song', mainStatus.details, true);
embed.addField('Artist', mainStatus.state, true); embed.addField('Artist', mainStatus.state, true);
// @ts-ignore
embed.addField('Album', mainStatus.assets.large_text); embed.addField('Album', mainStatus.assets.large_text);
// @ts-ignore
embed.addField('Start', `${new Date(mainStatus.timestamps.start).toLocaleTimeString('en-us')} ET`, true); embed.addField('Start', `${new Date(mainStatus.timestamps.start).toLocaleTimeString('en-us')} ET`, true);
// @ts-ignore
embed.addField('End', `${new Date(mainStatus.timestamps.end).toLocaleTimeString('en-us')} ET`, true); embed.addField('End', `${new Date(mainStatus.timestamps.end).toLocaleTimeString('en-us')} ET`, true);
// @ts-ignore
embed.setThumbnail(`https://i.scdn.co/image/${mainStatus.assets.large_image.split(':')[1]}`); embed.setThumbnail(`https://i.scdn.co/image/${mainStatus.assets.large_image.split(':')[1]}`);
embed.setFooter(`Listening to Spotify | ${this.client.user.username}`, 'https://media.discordapp.net/attachments/358674161566220288/496894273304920064/2000px-Spotify_logo_without_text.png'); embed.setFooter(`Listening to Spotify | ${this.client.user.username}`, 'https://media.discordapp.net/attachments/358674161566220288/496894273304920064/2000px-Spotify_logo_without_text.png');
embed.setTimestamp(); embed.setTimestamp();

View File

@ -12,7 +12,6 @@ export default class Help extends Command {
this.enabled = true; this.enabled = true;
} }
// eslint-disable-next-line consistent-return
public async run(message: Message, args: string[]) { public async run(message: Message, args: string[]) {
try { try {
if (args.length > 0) { if (args.length > 0) {
@ -110,7 +109,7 @@ export default class Help extends Command {
if (cmdPages.length === 1) return message.channel.createMessage({ embed: cmdPages[0] }); if (cmdPages.length === 1) return message.channel.createMessage({ embed: cmdPages[0] });
return createPaginationEmbed(message, cmdPages); return createPaginationEmbed(message, cmdPages);
} catch (err) { } catch (err) {
this.client.util.handleError(err, message, this, false); return this.client.util.handleError(err, message, this, false);
} }
} }
} }

17
src/commands/index.ts Normal file
View File

@ -0,0 +1,17 @@
export { default as additem } from './additem';
export { default as addredirect } from './addredirect';
export { default as ban } from './ban';
export { default as delitem } from './delitem';
export { default as delredirect } from './delredirect';
export { default as djs } from './djs';
export { default as eval } from './eval';
export { default as game } from './game';
export { default as help } from './help';
export { default as info } from './info';
export { default as kick } from './kick';
export { default as listredirects } from './listredirects';
export { default as npm } from './npm';
export { default as ping } from './ping';
export { default as roleinfo } from './roleinfo';
export { default as unban } from './unban';
export { default as whois } from './whois';

View File

@ -1,4 +1,4 @@
import { Message, User } from 'eris'; import { Message, User, Member } from 'eris';
import { Client, Command } from '../class'; import { Client, Command } from '../class';
export default class Kick extends Command { export default class Kick extends Command {
@ -15,16 +15,15 @@ export default class Kick extends Command {
public async run(message: Message, args: string[]) { public async run(message: Message, args: string[]) {
try { try {
if (!args[0]) return this.client.commands.get('help').run(message, [this.name]); if (!args[0]) return this.client.commands.get('help').run(message, [this.name]);
const member = this.client.util.resolveMember(args[0], this.client.guilds.get(this.client.config.guildID)); let user: Member = this.client.util.resolveMember(args[0], this.client.guilds.get(this.client.config.guildID));
let user: User; if (!user) {
if (!member) {
try { try {
user = await this.client.getRESTUser(args[0]); user = await this.client.getRESTGuildMember(this.client.config.guildID, args[0]);
} catch { } catch {
return this.error(message.channel, 'Cannot find user.'); return this.error(message.channel, 'Cannot find user.');
} }
} }
if (member && !this.client.util.moderation.checkPermissions(member, message.member)) return this.error(message.channel, 'Permission Denied.'); if (user && !this.client.util.moderation.checkPermissions(user, message.member)) return this.error(message.channel, 'Permission Denied.');
message.delete(); message.delete();
const reason: string = args[1]; const reason: string = args[1];

View File

@ -16,7 +16,7 @@ export default class DelRedirect extends Command {
public async run(message: Message, args: string[]) { public async run(message: Message, args: string[]) {
try { try {
if (args[0]) { if (args[0]) {
const redirects = await this.client.db.redirect.find({ $or: [{ key: args[0].toLowerCase() }, { to: args[0].toLowerCase() }] }); const redirects = await this.client.db.Redirect.find({ $or: [{ key: args[0].toLowerCase() }, { to: args[0].toLowerCase() }] });
if (redirects.length <= 0) return this.error(message.channel, 'Could not find an entry matching that query.'); if (redirects.length <= 0) return this.error(message.channel, 'Could not find an entry matching that query.');
const embed = new RichEmbed(); const embed = new RichEmbed();
embed.setTitle('Redirect Information'); embed.setTitle('Redirect Information');
@ -27,7 +27,7 @@ export default class DelRedirect extends Command {
embed.setTimestamp(); embed.setTimestamp();
return message.channel.createMessage({ embed }); return message.channel.createMessage({ embed });
} }
const redirects = await this.client.db.redirect.find(); const redirects = await this.client.db.Redirect.find();
if (!redirects) return this.error(message.channel, 'No redirect links found.'); if (!redirects) return this.error(message.channel, 'No redirect links found.');
const redirectArray: [{ name: string, value: string }?] = []; const redirectArray: [{ name: string, value: string }?] = [];
for (const redirect of redirects) { for (const redirect of redirects) {

View File

@ -34,8 +34,7 @@ export default class NPM extends Command {
license = data.license; license = data.license;
} }
let dependencies: string = 'None'; let dependencies: string = 'None';
// eslint-disable-next-line no-prototype-builtins if (version !== 'Unknown' && data.versions[version].dependencies !== undefined && Object.keys(data.versions[version].dependencies).length > 0) {
if (version !== 'Unknown' && data.versions[version].hasOwnProperty('dependencies') && Object.keys(data.versions[version].dependencies).length > 0) {
dependencies = Object.keys(data.versions[version].dependencies).join(', '); dependencies = Object.keys(data.versions[version].dependencies).join(', ');
} }
const name: string = data.name || 'None'; const name: string = data.name || 'None';

View File

@ -41,22 +41,20 @@ export default class Whois extends Command {
embed.setThumbnail(member.avatarURL); embed.setThumbnail(member.avatarURL);
const ackResolve = this.resolveStaffInformation(member.id); const ackResolve = this.resolveStaffInformation(member.id);
let description = ''; let description = '';
if (ackResolve) { if (ackResolve?.title) {
if (ackResolve?.title) { description += `${emotes.titleAndDepartment} __**${ackResolve.title}**__\n\n`;
description += `${emotes.titleAndDepartment} __**${ackResolve.title}**__\n\n`; }
} if (ackResolve?.emailAddress) {
if (ackResolve?.emailAddress) { description += `${emotes.email} ${ackResolve.emailAddress}\n`;
description += `${emotes.email} ${ackResolve.emailAddress}\n`; }
} if (ackResolve?.gitlab) {
if (ackResolve?.gitlab) { description += `${emotes.gitlab} ${ackResolve.gitlab}\n`;
description += `${emotes.gitlab} ${ackResolve.gitlab}\n`; }
} if (ackResolve?.github) {
if (ackResolve?.github) { description += `${emotes.github} ${ackResolve.github}\n`;
description += `${emotes.github} ${ackResolve.github}\n`; }
} if (ackResolve?.bio) {
if (ackResolve?.bio) { description += `${emotes.bio} *${ackResolve.bio}*\n`;
description += `${emotes.bio} *${ackResolve.bio}*\n`;
}
} }
description += `\n<@${member.id}>`; description += `\n<@${member.id}>`;
embed.setDescription(description); embed.setDescription(description);
@ -95,7 +93,7 @@ export default class Whois extends Command {
if ((bit | 1073741824) === bit) permissions.push('Manage Emojis'); if ((bit | 1073741824) === bit) permissions.push('Manage Emojis');
if ((bit | 4) === bit) permissions.push('Ban Members'); if ((bit | 4) === bit) permissions.push('Ban Members');
if ((bit | 2) === bit) permissions.push('Kick Members'); if ((bit | 2) === bit) permissions.push('Kick Members');
const account = await this.client.db.member.findOne({ userID: member.id }); const account = await this.client.db.Member.findOne({ userID: member.id });
if (account?.additional?.langs.length > 0) { if (account?.additional?.langs.length > 0) {
const langs: string[] = []; const langs: string[] = [];
for (const lang of account.additional.langs.sort((a, b) => a.localeCompare(b))) { for (const lang of account.additional.langs.sort((a, b) => a.localeCompare(b))) {

View File

@ -37,7 +37,7 @@
{ {
"name": "Midori", "name": "Midori",
"id": "109122112643440640", "id": "109122112643440640",
"title": "Board of Directors", "title": "Board of Directors [ON LEAVE]",
"emailAddress": "midori@staff.libraryofcode.org" "emailAddress": "midori@staff.libraryofcode.org"
}, },
{ {
@ -101,13 +101,6 @@
"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": "Realitus",
"id": "156450671338586112",
"title": "Associate",
"github": "https://github.com/Realitus",
"bio": "A hobbyist software developer with some rather strange ideas, and even stranger implementations."
},
{ {
"name": "KhaaZ", "name": "KhaaZ",
"id": "179908288337412096", "id": "179908288337412096",
@ -133,6 +126,13 @@
"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",
"id": "273999507174195203",
"title": "Instructor & Associate",
"emailAddress": "nicolas@staff.libraryofcode.org",
"bio": "I write C++ to pay off my student loans"
},
{ {
"name": "Null", "name": "Null",
"id": "323673862971588609", "id": "323673862971588609",

View File

@ -1,12 +1,13 @@
/* eslint-disable no-useless-return */ /* eslint-disable no-useless-return */
import { Message, TextChannel, NewsChannel } from 'eris'; import { Message, TextChannel, NewsChannel } from 'eris';
import { Client } from '../class'; import { Client, Event } from '../class';
export default class { export default class CommandHandler extends Event {
public client: Client; public client: Client;
constructor(client: Client) { constructor(client: Client) {
this.client = client; super(client);
this.event = 'messageCreate';
} }
public async run(message: Message) { public async run(message: Message) {

2
src/events/index.ts Normal file
View File

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

View File

@ -1,10 +1,11 @@
import { Client } from '../class'; import { Client, Event } from '../class';
export default class { export default class Ready extends Event {
public client: Client; public client: Client;
constructor(client: Client) { constructor(client: Client) {
this.client = client; super(client);
this.event = 'ready';
} }
public async run() { public async run() {

View File

@ -5,7 +5,7 @@ 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;

View File

@ -3,7 +3,7 @@ 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);

View File

@ -4,6 +4,8 @@
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 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');
@ -13,8 +15,8 @@ async function main(): Promise<void> {
await client.loadDatabase(); await client.loadDatabase();
client.loadPlugins(); client.loadPlugins();
await client.loadIntervals(); await client.loadIntervals();
await client.loadEvents(); await client.loadEvents(eventFiles);
await client.loadCommands(); await client.loadCommands(commandFiles);
client.connect(); client.connect();
} }

View File

@ -5,6 +5,11 @@ export interface RedirectInterface extends Document {
to: string, to: string,
} }
export interface RedirectRaw {
key: string,
to: string,
}
const Redirect: Schema = new Schema({ const Redirect: Schema = new Schema({
key: String, key: String,
to: String, to: String,

View File

@ -1,3 +1,3 @@
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 Redirect, RedirectInterface } from './Redirect'; export { default as Redirect, RedirectInterface, RedirectRaw } from './Redirect';