Fix everything

Matt please don't change the ECMA target


(cherry picked from commit cfe5e65ed8)
merge-requests/9/merge
Bsian 2020-04-17 02:56:52 +00:00
parent 4069a20a31
commit 9dab377235
14 changed files with 107 additions and 93 deletions

View File

@ -22,7 +22,7 @@
},
"dependencies": {
"axios": "^0.19.2",
"eris": "^0.11.2",
"eris": "abalabahaha/eris#dev",
"moment": "^2.24.0",
"mongoose": "^5.9.9",
"signale": "^1.4.0",

View File

@ -2,12 +2,12 @@
* Hold a bunch of something
*/
export default class Collection<V> extends Map<string, V> {
baseObject: any
baseObject: new (...args: any[]) => V;
/**
* Creates an instance of Collection
*/
constructor(iterable: any[]|object = null) {
constructor(iterable: Iterable<[string, V]>|object = null) {
if (iterable && iterable instanceof Array) {
super(iterable);
} else if (iterable && iterable instanceof Object) {
@ -33,8 +33,8 @@ export default class Collection<V> extends Map<string, V> {
* { key: value, key: value, key: value }
* ```
*/
toObject(): object {
const obj: object = {};
toObject(): { [key: string]: V } {
const obj: { [key: string]: V } = {};
for (const [key, value] of this.entries()) {
obj[key] = value;
}
@ -90,7 +90,7 @@ export default class Collection<V> extends Map<string, V> {
* Return all the objects that make the function evaluate true
* @param func A function that takes an object and returns true if it matches
*/
filter(func: Function): V[] {
filter(func: (value: V) => boolean): V[] {
const arr = [];
for (const item of this.values()) {
if (func(item)) {
@ -104,7 +104,7 @@ export default class Collection<V> extends Map<string, V> {
* Test if at least one element passes the test implemented by the provided function. Returns true if yes, or false if not.
* @param func A function that takes an object and returns true if it matches
*/
some(func: Function) {
some(func: (value: V) => boolean) {
for (const item of this.values()) {
if (func(item)) {
return true;

View File

@ -1,4 +1,4 @@
import { Member, Message } from 'eris';
import { Member, Message, TextableChannel } from 'eris';
import { Client } from '.';
export default class Command {
@ -68,4 +68,16 @@ export default class Command {
return false;
}
}
public error(channel: TextableChannel, text: string): Promise<Message> {
return channel.createMessage(`***${this.client.util.emojis.ERROR} ${text}***`);
}
public success(channel: TextableChannel, text: string): Promise<Message> {
return channel.createMessage(`***${this.client.util.emojis.SUCCESS} ${text}***`);
}
public loading(channel: TextableChannel, text: string): Promise<Message> {
return channel.createMessage(`***${this.client.util.emojis.LOADING} ${text}***`);
}
}

View File

@ -41,13 +41,14 @@ export default class Moderation {
}
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');
await this.client.guilds.get(this.client.config.guildID).banMember(user.id, 7, reason);
const logID = uuid();
const mod = new ModerationModel({
userID: user.id,
logID,
moderatorID: moderator.id,
reason: reason ?? null,
reason: reason || null,
type: 5,
date: new Date(),
});
@ -89,7 +90,7 @@ export default class Moderation {
userID,
logID,
moderatorID: moderator.id,
reason: reason ?? null,
reason: reason || null,
type: 4,
date: new Date(),
});

View File

@ -1,6 +1,20 @@
/* eslint-disable no-param-reassign */
export default class RichEmbed {
export interface EmbedData {
title?: string
description?: string
url?: string
timestamp?: Date
color?: number
footer?: { text: string, icon_url?: string, proxy_icon_url?: string}
image?: { url: string, proxy_url?: string, height?: number, width?: number }
thumbnail?: { url: string, proxy_url?: string, height?: number, width?: number }
video?: { url: string, height?: number, width?: number }
author?: { name: string, url?: string, proxy_icon_url?: string, icon_url?: string}
fields?: {name: string, value: string, inline?: boolean}[]
}
export default class RichEmbed implements EmbedData {
title?: string
type?: string
@ -17,7 +31,7 @@ export default class RichEmbed {
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 }
@ -27,12 +41,7 @@ export default class RichEmbed {
fields?: {name: string, value: string, inline?: boolean}[]
constructor(data: {
title?: string, type?: string, description?: string, url?: string, timestamp?: Date, color?: number, fields?: {name: string, value: string, inline?: boolean}[]
footer?: { text: string, icon_url?: string, proxy_icon_url?: string}, image?: { url: string, proxy_url?: string, height?: number, width?: number },
thumbnail?: { url: string, proxy_url?: string, height?: number, width?: number }, video?: { url: string, height?: number, width?: number },
provider?: { name: string, url?: string}, author?: { name: string, url?: string, proxy_icon_url?: string, icon_url?: string},
} = {}) {
constructor(data: EmbedData = {}) {
/*
let types: {
title?: string, type?: string, description?: string, url?: string, timestamp?: Date, color?: number, fields?: {name: string, value: string, inline?: boolean}[]
@ -79,7 +88,7 @@ export default class RichEmbed {
setURL(url: string) {
if (typeof url !== 'string') throw new TypeError('RichEmbed URLs must be a string.');
if (!url.startsWith('http://') || !url.startsWith('https://')) url = `https://${url}`;
this.url = url;
this.url = encodeURI(url);
return this;
}

View File

@ -1,3 +1,4 @@
/* eslint-disable no-bitwise */
import signale from 'signale';
import { Member, Message, Guild, PrivateChannel, GroupChannel, Role, AnyGuildChannel } from 'eris';
import { Client, Command, Moderation, RichEmbed } from '.';
@ -50,17 +51,21 @@ export default class Util {
}
}
public resolveGuildChannel(query: string, { channels }: Guild): AnyGuildChannel | undefined {
const nchannels = channels.map((c) => c).sort((a: AnyGuildChannel, b: AnyGuildChannel) => a.type - b.type);
return nchannels.find((c) => (c.id === query || c.name === query || c.name.toLowerCase() === query.toLowerCase() || c.name.toLowerCase().startsWith(query.toLowerCase())));
public resolveGuildChannel(query: string, { channels }: Guild, categories = false): AnyGuildChannel | undefined {
const ch: AnyGuildChannel[] = channels.filter((c) => (!categories ? c.type !== 4 : true));
return ch.find((c) => c.id === query.replace(/[<#>]/g, '') || c.name === query)
|| ch.find((c) => c.name.toLowerCase() === query.toLowerCase())
|| ch.find((c) => c.name.toLowerCase().startsWith(query.toLowerCase()));
}
public resolveRole(query: string, { roles }: Guild): Role | undefined {
return roles.find((r) => r.id === query || r.name === query || r.name.toLowerCase() === query.toLowerCase() || r.name.toLowerCase().startsWith(query.toLowerCase()));
return roles.find((r) => r.id === query.replace(/[<@&>]/g, '') || r.name === query)
|| roles.find((r) => r.name.toLowerCase() === query.toLowerCase())
|| roles.find((r) => r.name.toLowerCase().startsWith(query.toLowerCase()));
}
public resolveMember(query: string, { members }: Guild): Member | undefined {
return members.find((m) => m.mention.replace('!', '') === query.replace('!', '') || `${m.username}#${m.discriminator}` === query || m.username === query || m.id === query || 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().startsWith(query.toLowerCase()) || (m.nick && m.nick.toLowerCase().startsWith(query.toLowerCase())));
}
@ -85,10 +90,10 @@ export default class Util {
info.embed = embed;
}
await this.client.createMessage('595788220764127272', info);
const msg = 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 (message) message.channel.createMessage(`***${this.emojis.ERROR} An unexpected error has occured - please contact a Faculty Marshal.${command ? ' This command has been disabled.' : ''}***`);
if (message) message.channel.createMessage(`***${this.emojis.ERROR} An unexpected error has occured - please contact a Faculty Marshal.${command && disable ? ' This command has been disabled.' : ''}***`);
} catch (err) {
this.signale.error(err);
}
@ -112,4 +117,13 @@ export default class Util {
}
return arrayString;
}
public decimalToHex(int: number): string {
const red = (int && 0x0000ff) << 16;
const green = int && 0x00ff00;
const blue = (int && 0xff0000) >>> 16;
const number = red | green | blue;
const asHex = number.toString(16);
return '#000000'.substring(0, 7 - asHex.length) + asHex;
}
}

View File

@ -1,5 +1,5 @@
import moment, { unitOfTime } from 'moment';
import { Message, User } from 'eris';
import { Message, User, PrivateChannel, GroupChannel } from 'eris';
import { Client, Command } from '../class';
export default class Ban extends Command {
@ -15,24 +15,20 @@ export default class Ban extends Command {
public async run(message: Message, args: string[]) {
try {
// @ts-ignore
const member = this.client.util.resolveMember(args[0], message.channel.guild);
const member = this.client.util.resolveMember(args[0], message.member.guild);
let user: User;
if (!member) {
try {
user = await this.client.getRESTUser(args[0]);
} catch {
return message.channel.createMessage(`***${this.client.util.emojis.ERROR} Cannot find user.***`);
return this.error(message.channel, 'Cannot find user.');
}
}
try {
await this.client.guilds.get(this.client.config.guildID).getBan(args[0]);
return message.channel.createMessage(`***${this.client.util.emojis.ERROR} This user is already banned.***`);
} catch {
// eslint-disable-next-line no-unused-expressions
undefined;
}
if (member && !this.client.util.moderation.checkPermissions(member, message.member)) return message.channel.createMessage(`***${this.client.util.emojis.ERROR} Permission denied.***`);
return this.error(message.channel, 'This user is already banned.');
} catch {} // eslint-disable-line no-empty
if (member && !this.client.util.moderation.checkPermissions(member, message.member)) return this.error(message.channel, 'Permission Denied.');
message.delete();
let momentMilliseconds: number;
@ -43,9 +39,10 @@ export default class Ban extends Command {
const unit = lockLength[1] as unitOfTime.Base;
momentMilliseconds = moment.duration(length, unit).asMilliseconds();
reason = momentMilliseconds ? args.slice(2).join(' ') : args.slice(1).join(' ');
if (reason.length > 512) return this.error(message.channel, 'Ban reasons cannot be longer than 512 characters.');
}
await this.client.util.moderation.ban(user, message.member, momentMilliseconds, reason);
return message.channel.createMessage(`***${this.client.util.emojis.SUCCESS} ${user.username}#${user.id} has been banned.***`);
return this.success(message.channel, `${user.username}#${user.id} has been banned.`);
} catch (err) {
return this.client.util.handleError(err, message, this, false);
}

View File

@ -54,9 +54,9 @@ export default class Eval extends Command {
if (display[5]) {
try {
const { data } = await axios.post('https://snippets.cloud.libraryofcode.org/documents', display.join(''));
return message.channel.createMessage(`${this.client.util.emojis.SUCCESS} Your evaluation evaled can be found on https://snippets.cloud.libraryofcode.org/${data.key}`);
return this.success(message.channel, `Your evaluation evaled can be found on https://snippets.cloud.libraryofcode.org/${data.key}`);
} catch (error) {
return message.channel.createMessage(`${this.client.util.emojis.ERROR} ${error}`);
return this.error(message.channel, `${error}`);
}
}

View File

@ -1,5 +1,5 @@
/* eslint-disable prefer-destructuring */
import { Activity, Member, Message } from 'eris';
import { Activity, Member, Message, PrivateChannel, GroupChannel } from 'eris';
import { Client, Command, RichEmbed } from '../class';
export default class Game extends Command {
@ -19,10 +19,9 @@ export default class Game extends Command {
let member: Member;
if (!args[0]) member = message.member;
else {
// @ts-ignore
member = this.client.util.resolveMember(message, args[0], message.channel.guild);
member = this.client.util.resolveMember(args.join(' '), message.member.guild);
if (!member) {
return message.channel.createMessage(`***${this.client.util.emojis.ERROR} Member not found.***`);
return this.error(message.channel, 'Member not found.');
}
}
if (member.activities.length <= 0) return message.channel.createMessage(`***${this.client.util.emojis.ERROR} Cannot find a game for this member.***`);
@ -35,7 +34,7 @@ export default class Game extends Command {
mainStatus = member.activities[0];
}
embed.setAuthor(member.user.username, member.user.avatarURL);
if (mainStatus?.name === 'Spotify') {
if (mainStatus.type === 4) {
embed.setTitle('Spotify');
embed.setColor('#1ed760');
embed.addField('Song', mainStatus.details, true);
@ -51,7 +50,7 @@ export default class Game extends Command {
embed.setFooter(`Listening to Spotify | ${this.client.user.username}`, 'https://media.discordapp.net/attachments/358674161566220288/496894273304920064/2000px-Spotify_logo_without_text.png');
embed.setTimestamp();
} else {
return message.channel.createMessage(`***${this.client.util.emojis.ERROR} Only Spotify games are supported at this time.***`);
return this.error(message.channel, 'Only Spotify games are supported at this time.');
}
return message.channel.createMessage({ embed });
} catch (err) {

View File

@ -14,19 +14,13 @@ export default class Roleinfo extends Command {
public async run(message: Message, args: string[]) {
try {
if (!args[0]) return message.channel.createMessage(`***${this.client.util.emojis.ERROR} You need to specifiy a role ID or a role name.***`);
if (!args[0]) return this.error(message.channel, 'You need to specifiy a role ID or a role name.');
// @ts-ignore
let role: Role = message.channel.guild.roles.find((r: Role) => r.id === args[0]);
let role: Role = message.member.guild.roles.find((r: Role) => r.id === args[0]);
if (!role) { // if it's a role name
// @ts-ignore
role = message.channel.guild.roles.find((r: Role) => r.name.toLowerCase().includes(args.join(' ').toLowerCase()));
role = message.member.guild.roles.find((r: Role) => r.name.toLowerCase().includes(args.join(' ').toLowerCase()));
}
if (!role) return this.client.createMessage(message.channel.id, `***${this.client.util.emojis.ERROR} Could not find role.***`);
const ms = role.createdAt;
const date = new Date(ms).toLocaleDateString('en-us');
const time = new Date(ms).toLocaleTimeString('en-us');
if (!role) return this.error(message.channel, 'Could not find role.');
const perms = role.permissions;
const permsArray: string[] = [];
@ -45,11 +39,11 @@ export default class Roleinfo extends Command {
embed.setDescription(`<@&${role.id}> ID: \`${role.id}\``);
embed.setColor(role.color);
embed.addField('Name', role.name, true);
embed.addField('Color', `#${role.color.toString(16)}`, true);
embed.addField('Hoisted', role.hoist.toString(), true);
embed.addField('Position', role.position.toString(), true);
embed.addField('Creation Date', `${date} ${time}`, true);
embed.addField('Mentionnable', role.mentionable.toString(), true);
embed.addField('Color', role.color ? this.client.util.decimalToHex(role.color) : 'None', true);
embed.addField('Hoisted', role.hoist ? 'Yes' : 'No', true);
embed.addField('Position', role.position ? 'Yes' : 'No', true);
embed.addField('Creation Date', new Date(role.createdAt).toLocaleString(), true);
embed.addField('Mentionable', role.mentionable ? 'Yes' : 'No', true);
embed.setFooter(this.client.user.username, this.client.user.avatarURL);
embed.setTimestamp();

View File

@ -14,29 +14,21 @@ export default class Unban extends Command {
public async run(message: Message, args: string[]) {
try {
// @ts-ignore
let user: User;
try {
user = await this.client.getRESTUser(args[0]);
} catch {
return message.channel.createMessage(`***${this.client.util.emojis.ERROR} Could find find user.***`);
}
try {
if (await this.client.getRESTGuildMember(this.client.config.guildID, args[0])) return message.channel.createMessage(`***${this.client.util.emojis.ERROR} This member exists in the server.***`);
} catch {
// eslint-disable-next-line no-unused-expressions
undefined;
return this.error(message.channel, 'Could find find user.');
}
try {
await this.client.guilds.get(this.client.config.guildID).getBan(args[0]);
} catch {
return message.channel.createMessage(`***${this.client.util.emojis.ERROR} This user is not banned.***`);
return this.error(message.channel, 'This user is not banned.');
}
if (!user) return message.channel.createMessage(`***${this.client.util.emojis.ERROR} Unable to locate user.***`);
message.delete();
await this.client.util.moderation.unban(user.id, message.member, args.slice(1).join(' '));
return message.channel.createMessage(`***${this.client.util.emojis.SUCCESS} ${user.username}#${user.discriminator} has been unbanned.***`);
return this.success(message.channel, `${user.username}#${user.discriminator} has been unbanned.`);
} catch (err) {
return this.client.util.handleError(err, message, this, false);
}

View File

@ -1,6 +1,6 @@
/* eslint-disable no-bitwise */
import moment from 'moment';
import { Message, Member } from 'eris';
import { Message, Member, PrivateChannel, GroupChannel } from 'eris';
import { Client, Command, RichEmbed } from '../class';
import acknowledgements from '../configs/acknowledgements.json';
import { whois as emotes } from '../configs/emotes.json';
@ -21,11 +21,11 @@ export default class Whois extends Command {
let member: Member;
if (!args[0]) member = message.member;
else {
// @ts-ignore
member = this.client.util.resolveMember(args.join(' '), message.channel.guild);
if (!member) {
return message.channel.createMessage(`***${this.client.util.emojis.ERROR} Member not found.***`);
member = this.client.util.resolveMember(args.join(' '), message.member.guild);
}
if (!member) {
return this.error(message.channel, 'Member not found.');
}
const embed = new RichEmbed();
embed.setAuthor(`${member.user.username}#${member.user.discriminator}`, member.user.avatarURL);
@ -58,18 +58,15 @@ export default class Whois extends Command {
}
description += `\n<@${member.id}>`;
embed.setDescription(description);
// @ts-ignore
for (const role of member.roles.map((r) => message.channel.guild.roles.get(r)).sort((a, b) => b.position - a.position)) {
if (role.color !== 0) {
embed.setColor(role.color);
break;
}
}
const roles = member.roles.map((r) => message.member.guild.roles.get(r)).sort((a, b) => b.position - a.position);
const { color } = roles.find((r) => r.color);
embed.setColor(color);
embed.addField('Status', `${member.status[0].toUpperCase()}${member.status.slice(1)}`, true);
embed.addField('Joined At', `${moment(new Date(message.member.joinedAt)).format('dddd, MMMM Do YYYY, h:mm:ss A')} ET`, true);
embed.addField('Created At', `${moment(new Date(message.author.createdAt)).format('dddd, MMMM Do YYYY, h:mm:ss A')} ET`, true);
// @ts-ignore
embed.addField(`Roles [${member.roles.length}]`, member.roles.map((r) => message.channel.guild.roles.get(r)).sort((a, b) => b.position - a.position).map((r) => `<@&${r.id}>`).join(', '));
embed.addField(`Roles [${roles.length}]`, roles.map((r) => `<@&${r.id}>`).join(', '));
const permissions: string[] = [];
const serverAcknowledgements: string[] = [];
const bit = member.permission.allow;
@ -101,7 +98,6 @@ export default class Whois extends Command {
}
public resolveStaffInformation(id: string) {
const ack = acknowledgements.find((m) => m.id === id);
return ack;
return acknowledgements.find((m) => m.id === id);
}
}

View File

@ -16,8 +16,8 @@ export default class {
this.client.util.handleError(err);
process.exit(1);
});
process.on('unhandledRejection', (err) => {
this.client.util.handleError(new Error(err.toString()));
process.on('unhandledRejection', (err: Error) => {
this.client.util.handleError(err);
});
}
}

View File

@ -2,7 +2,7 @@
"compilerOptions": {
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "ES2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */