From 6903b3f304e709cfd4169700557bd68c1246d264 Mon Sep 17 00:00:00 2001 From: Bsian Date: Fri, 13 Mar 2020 23:57:16 +0000 Subject: [PATCH] Got rid of as many errors as possible --- package.json | 95 +++++---- src/class/Collection.ts | 308 +++++++++++++-------------- src/class/Util.ts | 1 - src/commands/cwg_data.ts | 114 +++++----- src/commands/cwg_delete.ts | 124 ++++++----- src/commands/disk.ts | 88 ++++---- src/commands/help.ts | 137 ++++++------ src/commands/lock.ts | 109 +++++----- src/commands/modlogs.ts | 133 ++++++------ src/commands/notify.ts | 104 +++++---- src/commands/parse.ts | 139 ++++++------ src/commands/parseall.ts | 147 +++++++------ src/commands/securesign_account.ts | 93 ++++---- src/commands/securesign_build.ts | 71 +++--- src/commands/securesign_createcrt.ts | 129 ++++++----- src/commands/securesign_init.ts | 111 +++++----- src/commands/sysinfo.ts | 71 +++--- src/commands/whois.ts | 127 ++++++----- src/commands/whois_user.ts | 105 +++++---- src/index.ts | 14 +- tsconfig.json | 128 +++++------ types/moment.d.ts | 15 ++ 22 files changed, 1176 insertions(+), 1187 deletions(-) create mode 100644 types/moment.d.ts diff --git a/package.json b/package.json index 8a7d054..c503294 100644 --- a/package.json +++ b/package.json @@ -1,47 +1,48 @@ -{ - "name": "cloudservices-rewrite", - "version": "1.2.0", - "description": "The official LOC Cloud Services system, this is a rewrite of the original version. ", - "main": "dist/Client.js", - "scripts": { - "lint": "eslint ./ --ext ts --fix", - "build": "make", - "lint-find": "eslint ./ --ext ts" - }, - "author": "Library of Code sp-us Engineering Team", - "license": "AGPL-3.0-only", - "private": false, - "dependencies": { - "@ghaiklor/x509": "^1.0.0", - "axios": "^0.19.0", - "body-parser": "^1.19.0", - "eris": "abalabahaha/eris#dev", - "eris-pagination": "bsian03/eris-pagination", - "express": "^4.17.1", - "fs-extra": "^8.1.0", - "helmet": "^3.21.2", - "ioredis": "^4.14.1", - "moment": "^2.24.0", - "moment-precise-range-plugin": "^1.3.0", - "mongoose": "^5.7.4", - "nodemailer": "^6.3.1", - "signale": "^1.4.0", - "uuid": "^3.3.3" - }, - "devDependencies": { - "@types/express": "^4.17.2", - "@types/fs-extra": "^8.0.0", - "@types/helmet": "^0.0.45", - "@types/ioredis": "^4.0.18", - "@types/mongoose": "^5.5.20", - "@types/nodemailer": "^6.2.1", - "@types/signale": "^1.2.1", - "@types/uuid": "^3.4.5", - "@typescript-eslint/eslint-plugin": "^2.4.0", - "@typescript-eslint/parser": "^2.4.0", - "eslint": "^6.5.1", - "eslint-config-airbnb-base": "^14.0.0", - "eslint-plugin-import": "^2.18.2", - "typescript": "^3.6.4" - } -} +{ + "name": "cloudservices-rewrite", + "version": "1.2.0", + "description": "The official LOC Cloud Services system, this is a rewrite of the original version. ", + "main": "dist/Client.js", + "scripts": { + "lint": "eslint ./ --ext ts --fix", + "build": "make", + "lint-find": "eslint ./ --ext ts" + }, + "author": "Library of Code sp-us Engineering Team", + "license": "AGPL-3.0-only", + "private": false, + "dependencies": { + "@ghaiklor/x509": "^1.0.0", + "axios": "^0.19.0", + "body-parser": "^1.19.0", + "eris": "abalabahaha/eris#dev", + "eris-pagination": "bsian03/eris-pagination", + "express": "^4.17.1", + "fs-extra": "^8.1.0", + "helmet": "^3.21.2", + "ioredis": "^4.14.1", + "moment": "^2.24.0", + "moment-precise-range-plugin": "^1.3.0", + "mongoose": "^5.7.4", + "nodemailer": "^6.3.1", + "signale": "^1.4.0", + "uuid": "^3.3.3" + }, + "devDependencies": { + "@types/express": "^4.17.2", + "@types/fs-extra": "^8.0.0", + "@types/helmet": "^0.0.45", + "@types/ioredis": "^4.0.18", + "@types/moment-precise-range-plugin": "^0.2.0", + "@types/mongoose": "^5.5.20", + "@types/nodemailer": "^6.2.1", + "@types/signale": "^1.2.1", + "@types/uuid": "^3.4.5", + "@typescript-eslint/eslint-plugin": "^2.4.0", + "@typescript-eslint/parser": "^2.4.0", + "eslint": "^6.5.1", + "eslint-config-airbnb-base": "^14.0.0", + "eslint-plugin-import": "^2.18.2", + "typescript": "^3.6.4" + } +} diff --git a/src/class/Collection.ts b/src/class/Collection.ts index 71e8a07..12b5b02 100644 --- a/src/class/Collection.ts +++ b/src/class/Collection.ts @@ -1,155 +1,153 @@ -/** - * Hold a bunch of something - */ -export default class Collection extends Map { - baseObject: any - - /** - * Creates an instance of Collection - */ - constructor(iterable: any[]|object = null) { - if (iterable && iterable instanceof Array) { - // @ts-ignore - super(iterable); - } else if (iterable && iterable instanceof Object) { - // @ts-ignore - super(Object.entries(iterable)); - } else { - super(); - } - } - - /** - * Map to array - * ```js - * [value, value, value] - * ``` - */ - toArray(): V[] { - return [...this.values()]; - } - - /** - * Map to object - * ```js - * { key: value, key: value, key: value } - * ``` - */ - toObject(): object { - const obj: object = {}; - for (const [key, value] of this.entries()) { - obj[key] = value; - } - return obj; - } - - /** - * Add an object - * - * If baseObject, add only if instance of baseObject - * - * If no baseObject, add - * @param key The key of the object - * @param value The object data - * @param replace Whether to replace an existing object with the same key - * @return The existing or newly created object - */ - add(key: string, value: V, replace: boolean = false): V { - if (this.has(key) && !replace) { - return this.get(key); - } - if (this.baseObject && !(value instanceof this.baseObject)) return null; - - this.set(key, value); - return value; - } - - /** - * Return the first object to make the function evaluate true - * @param func A function that takes an object and returns something - * @return The first matching object, or `null` if no match - */ - find(func: Function): V { - for (const item of this.values()) { - if (func(item)) return item; - } - return null; - } - - /** - * Return an array with the results of applying the given function to each element - * @param callbackfn A function that takes an object and returns something - */ - map(callbackfn: (value?: V, index?: number, array?: V[]) => U): U[] { - const arr = []; - for (const item of this.values()) { - arr.push(callbackfn(item)); - } - return arr; - } - - /** - * Return all the objects that make the function evaluate true - * @param func A function that takes an object and returns true if it matches - */ - filter(func: Function): V[] { - const arr = []; - for (const item of this.values()) { - if (func(item)) { - arr.push(item); - } - } - return arr; - } - - /** - * Test if at least one element passes the test implemented by the provided function. Returns true if yes, or false if not. - * @param func A function that takes an object and returns true if it matches - */ - some(func: Function) { - for (const item of this.values()) { - if (func(item)) { - return true; - } - } - return false; - } - - /** - * Update an object - * @param key The key of the object - * @param value The updated object data - */ - update(key: string, value: V) { - return this.add(key, value, true); - } - - /** - * Remove an object - * @param key The key of the object - * @returns The removed object, or `null` if nothing was removed - */ - remove(key: string): V { - const item = this.get(key); - if (!item) { - return null; - } - this.delete(key); - return item; - } - - /** - * Get a random object from the Collection - * @returns The random object or `null` if empty - */ - random(): V { - if (!this.size) { - return null; - } - return Array.from(this.values())[Math.floor(Math.random() * this.size)]; - } - - toString() { - return `[Collection<${this.baseObject.name}>]`; - } -} +/** + * Hold a bunch of something + */ +export default class Collection extends Map { + baseObject: any + + /** + * Creates an instance of Collection + */ + constructor(iterable: any[]|object = null) { + if (iterable && iterable instanceof Array) { + super(iterable); + } else if (iterable && iterable instanceof Object) { + super(Object.entries(iterable)); + } else { + super(); + } + } + + /** + * Map to array + * ```js + * [value, value, value] + * ``` + */ + toArray(): V[] { + return [...this.values()]; + } + + /** + * Map to object + * ```js + * { key: value, key: value, key: value } + * ``` + */ + toObject(): object { + const obj: object = {}; + for (const [key, value] of this.entries()) { + obj[key] = value; + } + return obj; + } + + /** + * Add an object + * + * If baseObject, add only if instance of baseObject + * + * If no baseObject, add + * @param key The key of the object + * @param value The object data + * @param replace Whether to replace an existing object with the same key + * @return The existing or newly created object + */ + add(key: string, value: V, replace: boolean = false): V { + if (this.has(key) && !replace) { + return this.get(key); + } + if (this.baseObject && !(value instanceof this.baseObject)) return null; + + this.set(key, value); + return value; + } + + /** + * Return the first object to make the function evaluate true + * @param func A function that takes an object and returns something + * @return The first matching object, or `null` if no match + */ + find(func: Function): V { + for (const item of this.values()) { + if (func(item)) return item; + } + return null; + } + + /** + * Return an array with the results of applying the given function to each element + * @param callbackfn A function that takes an object and returns something + */ + map(callbackfn: (value?: V, index?: number, array?: V[]) => U): U[] { + const arr = []; + for (const item of this.values()) { + arr.push(callbackfn(item)); + } + return arr; + } + + /** + * Return all the objects that make the function evaluate true + * @param func A function that takes an object and returns true if it matches + */ + filter(func: Function): V[] { + const arr = []; + for (const item of this.values()) { + if (func(item)) { + arr.push(item); + } + } + return arr; + } + + /** + * Test if at least one element passes the test implemented by the provided function. Returns true if yes, or false if not. + * @param func A function that takes an object and returns true if it matches + */ + some(func: Function) { + for (const item of this.values()) { + if (func(item)) { + return true; + } + } + return false; + } + + /** + * Update an object + * @param key The key of the object + * @param value The updated object data + */ + update(key: string, value: V) { + return this.add(key, value, true); + } + + /** + * Remove an object + * @param key The key of the object + * @returns The removed object, or `null` if nothing was removed + */ + remove(key: string): V { + const item = this.get(key); + if (!item) { + return null; + } + this.delete(key); + return item; + } + + /** + * Get a random object from the Collection + * @returns The random object or `null` if empty + */ + random(): V { + if (!this.size) { + return null; + } + return Array.from(this.values())[Math.floor(Math.random() * this.size)]; + } + + toString() { + return `[Collection<${this.baseObject.name}>]`; + } +} diff --git a/src/class/Util.ts b/src/class/Util.ts index 22f7b5a..f37ba33 100644 --- a/src/class/Util.ts +++ b/src/class/Util.ts @@ -242,7 +242,6 @@ export default class Util { .setTimestamp(); if (reason) embed.addField('Reason', reason || 'Not specified'); if (type === 2) embed.addField('Lock Expiration', `${date ? moment(date).format('dddd, MMMM Do YYYY, h:mm:ss A') : 'Indefinitely'}`); - // @ts-ignore this.client.createMessage('580950455581147146', { embed }); this.client.getDMChannel(userID).then((channel) => channel.createMessage({ embed })).catch(); return Promise.resolve(log); diff --git a/src/commands/cwg_data.ts b/src/commands/cwg_data.ts index 60dad9a..354fc07 100644 --- a/src/commands/cwg_data.ts +++ b/src/commands/cwg_data.ts @@ -1,58 +1,56 @@ -import fs from 'fs'; -import moment from 'moment'; -import x509 from '@ghaiklor/x509'; -import { createPaginationEmbed } from 'eris-pagination'; -import { Message } from 'eris'; -import { Command, RichEmbed } from '../class'; -import { Client } from '..'; - -export default class CWG_Data extends Command { - constructor(client: Client) { - super(client); - this.name = 'data'; - this.description = 'Check CWG data'; - this.usage = `${this.client.config.prefix}cwg data [Domain | Port]`; - this.permissions = { roles: ['446104438969466890'] }; - this.enabled = true; - } - - public async run(message: Message, args: string[]) { - try { - if (!args[0]) return this.client.commands.get('help').run(message, ['cwg', this.name]); - const dom = await this.client.db.Domain.find({ $or: [{ domain: args[0] }, { port: Number(args[0]) || '' }] }); - if (!dom.length) { - if (!Number.isNaN(Number(args[0]))) { - try { - await this.client.util.exec(`fuser ${args[0]}/tcp`); - return message.channel.createMessage(`***${this.client.stores.emojis.error} The port you provided is being used by a system process.***`); - } catch (error) { - return message.channel.createMessage(`***${this.client.stores.emojis.error} The domain or port you provided could not be found.***`); - } - } - return message.channel.createMessage(`***${this.client.stores.emojis.error} The domain or port you provided could not be found.***`); - } - const embeds = dom.map((domain) => { - const cert = fs.readFileSync(domain.x509.cert, { encoding: 'utf8' }); - const embed = new RichEmbed(); - embed.setTitle('Domain Information'); - embed.addField('Account Username', domain.account.username, true); - embed.addField('Account ID', domain.account.userID, true); - embed.addField('Domain', domain.domain, true); - embed.addField('Port', String(domain.port), true); - embed.addField('Certificate Issuer', x509.getIssuer(cert).organizationName, true); - embed.addField('Certificate Subject', x509.getSubject(cert).commonName, true); - embed.addField('Certificate Expiration Date', moment(x509.parseCert(cert).notAfter).format('dddd, MMMM Do YYYY, h:mm:ss A'), true); - embed.setFooter(this.client.user.username, this.client.user.avatarURL); - embed.setTimestamp(); - return embed; - }); - this.client.signale.log(embeds); - // @ts-ignore - if (embeds.length === 1) return message.channel.createMessage({ embed: embeds[0] }); - // @ts-ignore - return createPaginationEmbed(message, this.client, embeds, {}); - } catch (error) { - return this.client.util.handleError(error, message, this); - } - } -} +import fs from 'fs'; +import moment from 'moment'; +import x509 from '@ghaiklor/x509'; +import { createPaginationEmbed } from 'eris-pagination'; +import { Message } from 'eris'; +import { Command, RichEmbed } from '../class'; +import { Client } from '..'; + +export default class CWG_Data extends Command { + constructor(client: Client) { + super(client); + this.name = 'data'; + this.description = 'Check CWG data'; + this.usage = `${this.client.config.prefix}cwg data [Domain | Port]`; + this.permissions = { roles: ['446104438969466890'] }; + this.enabled = true; + } + + public async run(message: Message, args: string[]) { + try { + if (!args[0]) return this.client.commands.get('help').run(message, ['cwg', this.name]); + const dom = await this.client.db.Domain.find({ $or: [{ domain: args[0] }, { port: Number(args[0]) || '' }] }); + if (!dom.length) { + if (!Number.isNaN(Number(args[0]))) { + try { + await this.client.util.exec(`fuser ${args[0]}/tcp`); + return message.channel.createMessage(`***${this.client.stores.emojis.error} The port you provided is being used by a system process.***`); + } catch (error) { + return message.channel.createMessage(`***${this.client.stores.emojis.error} The domain or port you provided could not be found.***`); + } + } + return message.channel.createMessage(`***${this.client.stores.emojis.error} The domain or port you provided could not be found.***`); + } + const embeds = dom.map((domain) => { + const cert = fs.readFileSync(domain.x509.cert, { encoding: 'utf8' }); + const embed = new RichEmbed(); + embed.setTitle('Domain Information'); + embed.addField('Account Username', domain.account.username, true); + embed.addField('Account ID', domain.account.userID, true); + embed.addField('Domain', domain.domain, true); + embed.addField('Port', String(domain.port), true); + embed.addField('Certificate Issuer', x509.getIssuer(cert).organizationName, true); + embed.addField('Certificate Subject', x509.getSubject(cert).commonName, true); + embed.addField('Certificate Expiration Date', moment(x509.parseCert(cert).notAfter).format('dddd, MMMM Do YYYY, h:mm:ss A'), true); + embed.setFooter(this.client.user.username, this.client.user.avatarURL); + embed.setTimestamp(); + return embed; + }); + this.client.signale.log(embeds); + if (embeds.length === 1) return message.channel.createMessage({ embed: embeds[0] }); + return createPaginationEmbed(message, this.client, embeds, {}); + } catch (error) { + return this.client.util.handleError(error, message, this); + } + } +} diff --git a/src/commands/cwg_delete.ts b/src/commands/cwg_delete.ts index cf58e16..f142d10 100644 --- a/src/commands/cwg_delete.ts +++ b/src/commands/cwg_delete.ts @@ -1,63 +1,61 @@ -import fs from 'fs-extra'; -import axios from 'axios'; -import { Message } from 'eris'; -import { Command, RichEmbed } from '../class'; -import { Client } from '..'; - -export default class CWG_Delete extends Command { - constructor(client: Client) { - super(client); - this.name = 'delete'; - this.description = 'Unbind a domain to the CWG'; - this.usage = `${this.client.config.prefix}cwg delete [Domain | Port]`; - this.permissions = { roles: ['525441307037007902'] }; - this.aliases = ['unbind']; - this.enabled = true; - } - - public async run(message: Message, args: string[]) { - try { - if (!args[0]) return this.client.commands.get('help').run(message, ['cwg', this.name]); - const domain = await this.client.db.Domain.findOne({ $or: [{ domain: args[0] }, { port: Number(args[0]) || '' }] }); - if (!domain) return message.channel.createMessage(`***${this.client.stores.emojis.error} The domain or port you provided could not be found.***`); - const edit = await message.channel.createMessage(`***${this.client.stores.emojis.loading} Deleting domain...***`); - const embed = new RichEmbed(); - embed.setTitle('Domain Deletion'); - embed.addField('Account Username', domain.account.username, true); - embed.addField('Account ID', domain.account.userID, true); - embed.addField('Domain', domain.domain, true); - embed.addField('Port', String(domain.port), true); - embed.setFooter(this.client.user.username, this.client.user.avatarURL); - embed.setTimestamp(); - if (domain.domain.includes('cloud.libraryofcode.org')) { - const resultID = await axios({ - method: 'get', - url: `https://api.cloudflare.com/client/v4/zones/5e82fc3111ed4fbf9f58caa34f7553a7/dns_records?name=${domain.domain}`, - headers: { Authorization: `Bearer ${this.client.config.cloudflare}` }, - }); - this.client.signale.debug(resultID.data); - if (resultID.data.result[0]) { - const recordID = resultID.data.result[0].id; - await axios({ - method: 'delete', - url: `https://api.cloudflare.com/client/v4/zones/5e82fc3111ed4fbf9f58caa34f7553a7/dns_records/${recordID}`, - headers: { Authorization: `Bearer ${this.client.config.cloudflare}` }, - }); - } - } - try { - await fs.unlink(`/etc/nginx/sites-enabled/${domain.domain}`); - await fs.unlink(`/etc/nginx/sites-available/${domain.domain}`); - } catch (e) { this.client.signale.error(e); } - await this.client.db.Domain.deleteOne({ domain: domain.domain }); - await this.client.util.exec('systemctl reload nginx'); - edit.edit(`***${this.client.stores.emojis.success} Domain ${domain.domain} with port ${domain.port} has been successfully deleted.***`); - // @ts-ignore - this.client.createMessage('580950455581147146', { embed }); - // @ts-ignore - return this.client.getDMChannel(domain.account.userID).then((channel) => channel.createMessage({ embed })).catch(() => {}); - } catch (error) { - return this.client.util.handleError(error, message, this); - } - } -} +import fs from 'fs-extra'; +import axios from 'axios'; +import { Message } from 'eris'; +import { Command, RichEmbed } from '../class'; +import { Client } from '..'; + +export default class CWG_Delete extends Command { + constructor(client: Client) { + super(client); + this.name = 'delete'; + this.description = 'Unbind a domain to the CWG'; + this.usage = `${this.client.config.prefix}cwg delete [Domain | Port]`; + this.permissions = { roles: ['525441307037007902'] }; + this.aliases = ['unbind']; + this.enabled = true; + } + + public async run(message: Message, args: string[]) { + try { + if (!args[0]) return this.client.commands.get('help').run(message, ['cwg', this.name]); + const domain = await this.client.db.Domain.findOne({ $or: [{ domain: args[0] }, { port: Number(args[0]) || '' }] }); + if (!domain) return message.channel.createMessage(`***${this.client.stores.emojis.error} The domain or port you provided could not be found.***`); + const edit = await message.channel.createMessage(`***${this.client.stores.emojis.loading} Deleting domain...***`); + const embed = new RichEmbed(); + embed.setTitle('Domain Deletion'); + embed.addField('Account Username', domain.account.username, true); + embed.addField('Account ID', domain.account.userID, true); + embed.addField('Domain', domain.domain, true); + embed.addField('Port', String(domain.port), true); + embed.setFooter(this.client.user.username, this.client.user.avatarURL); + embed.setTimestamp(); + if (domain.domain.includes('cloud.libraryofcode.org')) { + const resultID = await axios({ + method: 'get', + url: `https://api.cloudflare.com/client/v4/zones/5e82fc3111ed4fbf9f58caa34f7553a7/dns_records?name=${domain.domain}`, + headers: { Authorization: `Bearer ${this.client.config.cloudflare}` }, + }); + this.client.signale.debug(resultID.data); + if (resultID.data.result[0]) { + const recordID = resultID.data.result[0].id; + await axios({ + method: 'delete', + url: `https://api.cloudflare.com/client/v4/zones/5e82fc3111ed4fbf9f58caa34f7553a7/dns_records/${recordID}`, + headers: { Authorization: `Bearer ${this.client.config.cloudflare}` }, + }); + } + } + try { + await fs.unlink(`/etc/nginx/sites-enabled/${domain.domain}`); + await fs.unlink(`/etc/nginx/sites-available/${domain.domain}`); + } catch (e) { this.client.signale.error(e); } + await this.client.db.Domain.deleteOne({ domain: domain.domain }); + await this.client.util.exec('systemctl reload nginx'); + edit.edit(`***${this.client.stores.emojis.success} Domain ${domain.domain} with port ${domain.port} has been successfully deleted.***`); + this.client.createMessage('580950455581147146', { embed }); + return this.client.getDMChannel(domain.account.userID).then((channel) => channel.createMessage({ embed })).catch(() => {}); + } catch (error) { + return this.client.util.handleError(error, message, this); + } + } +} diff --git a/src/commands/disk.ts b/src/commands/disk.ts index b1ac0d6..b53b45c 100644 --- a/src/commands/disk.ts +++ b/src/commands/disk.ts @@ -1,45 +1,43 @@ -import { Message } from 'eris'; -import moment from 'moment'; -import { Client } from '..'; -import { RichEmbed, Command } from '../class'; -import { dataConversion } from '../functions'; -// eslint-disable-next-line import/no-unresolved -import 'moment-precise-range-plugin'; - -export default class Disk extends Command { - constructor(client: Client) { - super(client); - this.name = 'disk'; - this.description = 'Checks the used disk space by a user'; - this.usage = `${this.client.config.prefix}disk [Username/User ID/Email]`; - this.permissions = { roles: ['446104438969466890'] }; - this.enabled = false; - } - - async run(message: Message, args: string[]) { - try { - if (!args[0]) return this.client.commands.get('help').run(message, [this.name]); - const account = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { userID: args[0] }, { emailAddress: args[0] }] }); - if (!account) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Account not found***`); - if (account.root || args[0].includes('./')) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Permission denied***`); - const diskReply = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Fetching total disk size may up to 10 minutes. This message will edit when the disk size has been located.***`); - const start = Date.now(); - const result = await this.client.util.exec(`du -s ${account.homepath}`); - const end = Date.now(); - // @ts-ignore - const totalTime: string = moment.preciseDiff(start, end); - const embed = new RichEmbed(); - embed.setTitle('Disk Usage'); - embed.setColor('ff0000'); - embed.setDescription(result.split(/ +/g)[1]); - embed.addField('Result', dataConversion(Number(result.split(/ +/g)[0])), true); - embed.addField('Time taken', totalTime, true); - embed.setFooter(`Requested by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL); - embed.setTimestamp(); - // @ts-ignore - return diskReply.edit({ content: '', embed }); - } catch (error) { - return this.client.util.handleError(error, message, this); - } - } -} +import { Message } from 'eris'; +import moment from 'moment'; +import { Client } from '..'; +import { RichEmbed, Command } from '../class'; +import { dataConversion } from '../functions'; +// eslint-disable-next-line import/no-unresolved +import 'moment-precise-range-plugin'; + +export default class Disk extends Command { + constructor(client: Client) { + super(client); + this.name = 'disk'; + this.description = 'Checks the used disk space by a user'; + this.usage = `${this.client.config.prefix}disk [Username/User ID/Email]`; + this.permissions = { roles: ['446104438969466890'] }; + this.enabled = false; + } + + async run(message: Message, args: string[]) { + try { + if (!args[0]) return this.client.commands.get('help').run(message, [this.name]); + const account = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { userID: args[0] }, { emailAddress: args[0] }] }); + if (!account) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Account not found***`); + if (account.root || args[0].includes('./')) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Permission denied***`); + const diskReply = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Fetching total disk size may up to 10 minutes. This message will edit when the disk size has been located.***`); + const start = Date.now(); + const result = await this.client.util.exec(`du -s ${account.homepath}`); + const end = Date.now(); + const totalTime: string = moment.preciseDiff(start, end); + const embed = new RichEmbed(); + embed.setTitle('Disk Usage'); + embed.setColor('ff0000'); + embed.setDescription(result.split(/ +/g)[1]); + embed.addField('Result', dataConversion(Number(result.split(/ +/g)[0])), true); + embed.addField('Time taken', totalTime, true); + embed.setFooter(`Requested by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL); + embed.setTimestamp(); + return diskReply.edit({ content: '', embed }); + } catch (error) { + return this.client.util.handleError(error, message, this); + } + } +} diff --git a/src/commands/help.ts b/src/commands/help.ts index 6ad81aa..ac915e1 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -1,70 +1,67 @@ -import { Message } from 'eris'; -import { createPaginationEmbed } from 'eris-pagination'; -import { Client } from '..'; -import { Command, RichEmbed } from '../class'; - -export default class Help extends Command { - constructor(client: Client) { - super(client); - this.name = 'help'; - this.description = 'Display a list of commands'; - this.usage = `${this.client.config.prefix}help | ${this.client.config.prefix}help ping`; - this.aliases = ['commands']; - this.enabled = true; - } - - // eslint-disable-next-line consistent-return - public async run(message: Message, args?: string[]) { - try { - if (!args[0]) { - const cmdList: Command[] = []; - this.client.commands.forEach((c) => cmdList.push(c)); - const commands = this.client.commands.map((c) => { - const aliases = c.aliases.map((alias) => `${this.client.config.prefix}${alias}`).join(', '); - const perms: string[] = []; - let allowedRoles = c.permissions && c.permissions.roles && c.permissions.roles.map((r) => `<@&${r}>`).join(', '); - if (allowedRoles) { allowedRoles = `**Roles:** ${allowedRoles}`; perms.push(allowedRoles); } - let allowedUsers = c.permissions && c.permissions.users && c.permissions.users.map((u) => `<@${u}>`).join(', '); - if (allowedUsers) { allowedUsers = `**Users:** ${allowedUsers}`; perms.push(allowedUsers); } - const displayedPerms = perms.length ? `**Permissions:**\n${perms.join('\n')}` : ''; - return { name: `${this.client.config.prefix}${c.name}`, value: `**Description:** ${c.description}\n**Aliases:** ${aliases}\n**Usage:** ${c.usage}\n${displayedPerms}`, inline: false }; - }); - - const splitCommands = this.client.util.splitFields(commands); - const cmdPages: RichEmbed[] = []; - splitCommands.forEach((splitCmd) => { - const embed = new RichEmbed(); - embed.setTimestamp(); embed.setFooter(`Requested by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL); - embed.setAuthor(`${this.client.user.username}#${this.client.user.discriminator}`, this.client.user.avatarURL); - embed.setDescription(`Command list for ${this.client.user.username}`); - splitCmd.forEach((c) => embed.addField(c.name, c.value, c.inline)); - return cmdPages.push(embed); - }); - // @ts-ignore - if (cmdPages.length === 1) return message.channel.createMessage({ embed: cmdPages[0] }); - // @ts-ignore - return createPaginationEmbed(message, this.client, cmdPages); - } - const resolved = await this.client.util.resolveCommand(args, message); - if (!resolved) return message.channel.createMessage(`${this.client.stores.emojis.error} **Command not found!**`); - const { cmd } = resolved; - const perms: string[] = []; - let allowedRoles = cmd.permissions && cmd.permissions.roles && cmd.permissions.roles.map((r) => `<@&${r}>`).join(', '); - if (allowedRoles) { allowedRoles = `**Roles:** ${allowedRoles}`; perms.push(allowedRoles); } - let allowedUsers = cmd.permissions && cmd.permissions.users && cmd.permissions.users.map((u) => `<@${u}>`).join(', '); - if (allowedUsers) { allowedUsers = `**Users:** ${allowedUsers}`; perms.push(allowedUsers); } - const displayedPerms = perms.length ? `\n**Permissions:**\n${perms.join('\n')}` : ''; - const aliases = cmd.aliases.length ? `\n**Aliases:** ${cmd.aliases.map((alias) => `${this.client.config.prefix}${cmd.parentName ? `${cmd.parentName} ` : ''}${alias}`).join(', ')}` : ''; - const subcommands = cmd.subcommands.size ? `\n**Subcommands:** ${cmd.subcommands.map((s) => `${cmd.name} ${s.name}`).join(', ')}` : ''; - const embed = new RichEmbed(); - embed.setTimestamp(); embed.setFooter(`Requested by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL); - embed.setTitle(`${this.client.config.prefix}${cmd.parentName ? `${cmd.parentName}${cmd.name}` : cmd.name}`); embed.setAuthor(`${this.client.user.username}#${this.client.user.discriminator}`, this.client.user.avatarURL); - const description = `**Description**: ${cmd.description}\n**Usage:** ${cmd.usage}${aliases}${displayedPerms}${subcommands}`; - embed.setDescription(description); - // @ts-ignore - message.channel.createMessage({ embed }); - } catch (error) { - this.client.util.handleError(error, message, this); - } - } -} +import { Message } from 'eris'; +import { createPaginationEmbed } from 'eris-pagination'; +import { Client } from '..'; +import { Command, RichEmbed } from '../class'; + +export default class Help extends Command { + constructor(client: Client) { + super(client); + this.name = 'help'; + this.description = 'Display a list of commands'; + this.usage = `${this.client.config.prefix}help | ${this.client.config.prefix}help ping`; + this.aliases = ['commands']; + this.enabled = true; + } + + // eslint-disable-next-line consistent-return + public async run(message: Message, args?: string[]) { + try { + if (!args[0]) { + const cmdList: Command[] = []; + this.client.commands.forEach((c) => cmdList.push(c)); + const commands = this.client.commands.map((c) => { + const aliases = c.aliases.map((alias) => `${this.client.config.prefix}${alias}`).join(', '); + const perms: string[] = []; + let allowedRoles = c.permissions && c.permissions.roles && c.permissions.roles.map((r) => `<@&${r}>`).join(', '); + if (allowedRoles) { allowedRoles = `**Roles:** ${allowedRoles}`; perms.push(allowedRoles); } + let allowedUsers = c.permissions && c.permissions.users && c.permissions.users.map((u) => `<@${u}>`).join(', '); + if (allowedUsers) { allowedUsers = `**Users:** ${allowedUsers}`; perms.push(allowedUsers); } + const displayedPerms = perms.length ? `**Permissions:**\n${perms.join('\n')}` : ''; + return { name: `${this.client.config.prefix}${c.name}`, value: `**Description:** ${c.description}\n**Aliases:** ${aliases}\n**Usage:** ${c.usage}\n${displayedPerms}`, inline: false }; + }); + + const splitCommands = this.client.util.splitFields(commands); + const cmdPages: RichEmbed[] = []; + splitCommands.forEach((splitCmd) => { + const embed = new RichEmbed(); + embed.setTimestamp(); embed.setFooter(`Requested by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL); + embed.setAuthor(`${this.client.user.username}#${this.client.user.discriminator}`, this.client.user.avatarURL); + embed.setDescription(`Command list for ${this.client.user.username}`); + splitCmd.forEach((c) => embed.addField(c.name, c.value, c.inline)); + return cmdPages.push(embed); + }); + if (cmdPages.length === 1) return message.channel.createMessage({ embed: cmdPages[0] }); + return createPaginationEmbed(message, this.client, cmdPages); + } + const resolved = await this.client.util.resolveCommand(args, message); + if (!resolved) return message.channel.createMessage(`${this.client.stores.emojis.error} **Command not found!**`); + const { cmd } = resolved; + const perms: string[] = []; + let allowedRoles = cmd.permissions && cmd.permissions.roles && cmd.permissions.roles.map((r) => `<@&${r}>`).join(', '); + if (allowedRoles) { allowedRoles = `**Roles:** ${allowedRoles}`; perms.push(allowedRoles); } + let allowedUsers = cmd.permissions && cmd.permissions.users && cmd.permissions.users.map((u) => `<@${u}>`).join(', '); + if (allowedUsers) { allowedUsers = `**Users:** ${allowedUsers}`; perms.push(allowedUsers); } + const displayedPerms = perms.length ? `\n**Permissions:**\n${perms.join('\n')}` : ''; + const aliases = cmd.aliases.length ? `\n**Aliases:** ${cmd.aliases.map((alias) => `${this.client.config.prefix}${cmd.parentName ? `${cmd.parentName} ` : ''}${alias}`).join(', ')}` : ''; + const subcommands = cmd.subcommands.size ? `\n**Subcommands:** ${cmd.subcommands.map((s) => `${cmd.name} ${s.name}`).join(', ')}` : ''; + const embed = new RichEmbed(); + embed.setTimestamp(); embed.setFooter(`Requested by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL); + embed.setTitle(`${this.client.config.prefix}${cmd.parentName ? `${cmd.parentName}${cmd.name}` : cmd.name}`); embed.setAuthor(`${this.client.user.username}#${this.client.user.discriminator}`, this.client.user.avatarURL); + const description = `**Description**: ${cmd.description}\n**Usage:** ${cmd.usage}${aliases}${displayedPerms}${subcommands}`; + embed.setDescription(description); + message.channel.createMessage({ embed }); + } catch (error) { + this.client.util.handleError(error, message, this); + } + } +} diff --git a/src/commands/lock.ts b/src/commands/lock.ts index b85ec33..245013a 100644 --- a/src/commands/lock.ts +++ b/src/commands/lock.ts @@ -1,54 +1,55 @@ -import moment from 'moment'; -import { Message } from 'eris'; -import { Client } from '..'; -import { Command } from '../class'; - -export default class Lock extends Command { - constructor(client: Client) { - super(client); - this.name = 'lock'; - this.description = 'Locks an account.'; - this.permissions = { roles: ['455972169449734144', '662163685439045632'] }; - this.enabled = true; - } - - public async run(message: Message, args: string[]) { // eslint-disable-line - try { - if (!args.length) return this.client.commands.get('help').run(message, [this.name]); - const account = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { userID: args[0].replace(/[<@!>]/gi, '') }] }); - if (!account) return message.channel.createMessage(`***${this.client.stores.emojis.error} Cannot find user.***`); - if (account.locked) return message.channel.createMessage(`***${this.client.stores.emojis.error} This account is already locked.***`); - const edit = await message.channel.createMessage(`***${this.client.stores.emojis.loading} Locking account...***`); - if (account.username === 'matthew' || account.root) return edit.edit(`***${this.client.stores.emojis.error} Permission denied.***`); - await this.client.util.exec(`lock ${account.username}`); - await account.updateOne({ locked: true }); - - const expiry = new Date(); - const lockLength = args[1].match(/[a-z]+|[^a-z]+/gi); - // @ts-ignore - const momentMilliseconds = moment.duration(Number(lockLength[0]), lockLength[1]).asMilliseconds(); - const reason = momentMilliseconds ? args.slice(2).join(' ') : args.slice(1).join(' '); - - await this.client.util.createModerationLog(account.userID, message.member, 2, reason, momentMilliseconds); - edit.edit(`***${this.client.stores.emojis.success} Account ${account.username} has been locked by Moderator ${message.author.username}#${message.author.discriminator}.***`); - message.delete(); - - this.client.util.transport.sendMail({ - to: account.emailAddress, - from: 'Library of Code sp-us | Cloud Services ', - subject: 'Your account has been locked', - html: ` -

Library of Code | Cloud Services

-

Your Cloud Account has been locked until ${momentMilliseconds ? moment(expiry).calendar() : 'indefinitely'} under the EULA.

-

Reason: ${momentMilliseconds ? args.slice(2).join(' ') : args.slice(1).join(' ')}

-

Supervisor: ${message.author.username}

-

Expiration: ${momentMilliseconds ? moment(expiry).format('dddd, MMMM Do YYYY, h:mm:ss A') : 'N/A'}

- - Library of Code sp-us | Support Team - `, - }); - } catch (error) { - return this.client.util.handleError(error, message, this); - } - } -} +import moment, { unitOfTime } from 'moment'; +import { Message } from 'eris'; +import { Client } from '..'; +import { Command } from '../class'; + +export default class Lock extends Command { + constructor(client: Client) { + super(client); + this.name = 'lock'; + this.description = 'Locks an account.'; + this.permissions = { roles: ['455972169449734144', '662163685439045632'] }; + this.enabled = true; + } + + public async run(message: Message, args: string[]) { // eslint-disable-line + try { + if (!args.length) return this.client.commands.get('help').run(message, [this.name]); + const account = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { userID: args[0].replace(/[<@!>]/gi, '') }] }); + if (!account) return message.channel.createMessage(`***${this.client.stores.emojis.error} Cannot find user.***`); + if (account.locked) return message.channel.createMessage(`***${this.client.stores.emojis.error} This account is already locked.***`); + const edit = await message.channel.createMessage(`***${this.client.stores.emojis.loading} Locking account...***`); + if (account.username === 'matthew' || account.root) return edit.edit(`***${this.client.stores.emojis.error} Permission denied.***`); + await this.client.util.exec(`lock ${account.username}`); + await account.updateOne({ locked: true }); + + const expiry = new Date(); + const lockLength = args[1].match(/[a-z]+|[^a-z]+/gi); + const length = Number(lockLength[0]); + const unit = lockLength[1] as unitOfTime.Base; + const momentMilliseconds = moment.duration(length, unit).asMilliseconds(); + const reason = momentMilliseconds ? args.slice(2).join(' ') : args.slice(1).join(' '); + + await this.client.util.createModerationLog(account.userID, message.member, 2, reason, momentMilliseconds); + edit.edit(`***${this.client.stores.emojis.success} Account ${account.username} has been locked by Moderator ${message.author.username}#${message.author.discriminator}.***`); + message.delete(); + + this.client.util.transport.sendMail({ + to: account.emailAddress, + from: 'Library of Code sp-us | Cloud Services ', + subject: 'Your account has been locked', + html: ` +

Library of Code | Cloud Services

+

Your Cloud Account has been locked until ${momentMilliseconds ? moment(expiry).calendar() : 'indefinitely'} under the EULA.

+

Reason: ${momentMilliseconds ? args.slice(2).join(' ') : args.slice(1).join(' ')}

+

Supervisor: ${message.author.username}

+

Expiration: ${momentMilliseconds ? moment(expiry).format('dddd, MMMM Do YYYY, h:mm:ss A') : 'N/A'}

+ + Library of Code sp-us | Support Team + `, + }); + } catch (error) { + return this.client.util.handleError(error, message, this); + } + } +} diff --git a/src/commands/modlogs.ts b/src/commands/modlogs.ts index dc23c45..fd974ed 100644 --- a/src/commands/modlogs.ts +++ b/src/commands/modlogs.ts @@ -1,68 +1,65 @@ -import { Message } from 'eris'; -// eslint-disable-next-line import/no-unresolved -import { createPaginationEmbed } from 'eris-pagination'; -import { Client } from '..'; -import { Command, RichEmbed } from '../class'; - -export default class Modlogs extends Command { - constructor(client: Client) { - super(client); - this.name = 'modlogs'; - this.description = 'Check a user\'s Cloud Modlogs'; - this.aliases = ['infractions', 'modlog']; - this.enabled = true; - this.permissions = { roles: ['446104438969466890'] }; - } - - public async run(message: Message, args: string[]) { - try { - if (!args.length) return this.client.commands.get('help').run(message, [this.name]); - const msg: Message = await message.channel.createMessage(`***${this.client.stores.emojis.loading} Locating modlogs...***`); - const query = await this.client.db.Moderation.find({ $or: [{ username: args.join(' ') }, { userID: args.filter((a) => a)[0].replace(/[<@!>]/g, '') }] }); - if (!query.length) return msg.edit(`***${this.client.stores.emojis.error} Cannot locate modlogs for ${args.join(' ')}***`); - - // @ts-ignore - const formatted = query.sort((a, b) => a.date - b.date).map((log) => { - const { username, moderatorID, reason, type, date, logID } = log; - let name: string; - switch (type) { - default: name = 'Generic'; break; - case 0: name = 'Create'; break; - case 1: name = 'Warn'; break; - case 2: name = 'Lock'; break; - case 3: name = 'Unlock'; break; - case 4: name = 'Delete'; break; - } - const value = `**ID:** ${logID}\n**Account name:** ${username}\n**Moderator:** <@${moderatorID}>\n**Reason:** ${reason || 'Not supplied'}\n**Date:** ${date.toLocaleString('en-us')} EST`; - const inline = true; - return { name, value, inline }; - }); - const users = [...new Set(query.map((log) => log.userID))].map((u) => `<@${u}>`); - - const logs = this.client.util.splitFields(formatted); - - const embeds = logs.map((l) => { - const embed = new RichEmbed(); - embed.setDescription(`List of Cloud moderation logs for ${users.join(', ')}`); - embed.setAuthor('Library of Code | Cloud Services', this.client.user.avatarURL, 'https://libraryofcode.org/'); - embed.setTitle('Cloud Modlogs/Infractions'); - embed.setFooter(`Requested by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL); - l.forEach((f) => embed.addField(f.name, f.value, f.inline)); - embed.setTimestamp(); - embed.setColor(3447003); - return embed; - }); - - if (embeds.length === 1) { - // @ts-ignore - msg.edit({ content: '', embed: embeds[0] }); - } else { - // @ts-ignore - createPaginationEmbed(message, this.client, embeds, {}, msg); - } - return msg; - } catch (error) { - return this.client.util.handleError(error, message, this); - } - } -} +import { Message } from 'eris'; +// eslint-disable-next-line import/no-unresolved +import { createPaginationEmbed } from 'eris-pagination'; +import { Client } from '..'; +import { Command, RichEmbed } from '../class'; + +export default class Modlogs extends Command { + constructor(client: Client) { + super(client); + this.name = 'modlogs'; + this.description = 'Check a user\'s Cloud Modlogs'; + this.aliases = ['infractions', 'modlog']; + this.enabled = true; + this.permissions = { roles: ['446104438969466890'] }; + } + + public async run(message: Message, args: string[]) { + try { + if (!args.length) return this.client.commands.get('help').run(message, [this.name]); + const msg: Message = await message.channel.createMessage(`***${this.client.stores.emojis.loading} Locating modlogs...***`); + const query = await this.client.db.Moderation.find({ $or: [{ username: args.join(' ') }, { userID: args.filter((a) => a)[0].replace(/[<@!>]/g, '') }] }); + if (!query.length) return msg.edit(`***${this.client.stores.emojis.error} Cannot locate modlogs for ${args.join(' ')}***`); + + const formatted = query.sort((a, b) => a.date.getTime() - b.date.getTime()).map((log) => { + const { username, moderatorID, reason, type, date, logID } = log; + let name: string; + switch (type) { + default: name = 'Generic'; break; + case 0: name = 'Create'; break; + case 1: name = 'Warn'; break; + case 2: name = 'Lock'; break; + case 3: name = 'Unlock'; break; + case 4: name = 'Delete'; break; + } + const value = `**ID:** ${logID}\n**Account name:** ${username}\n**Moderator:** <@${moderatorID}>\n**Reason:** ${reason || 'Not supplied'}\n**Date:** ${date.toLocaleString('en-us')} EST`; + const inline = true; + return { name, value, inline }; + }); + const users = [...new Set(query.map((log) => log.userID))].map((u) => `<@${u}>`); + + const logs = this.client.util.splitFields(formatted); + + const embeds = logs.map((l) => { + const embed = new RichEmbed(); + embed.setDescription(`List of Cloud moderation logs for ${users.join(', ')}`); + embed.setAuthor('Library of Code | Cloud Services', this.client.user.avatarURL, 'https://libraryofcode.org/'); + embed.setTitle('Cloud Modlogs/Infractions'); + embed.setFooter(`Requested by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL); + l.forEach((f) => embed.addField(f.name, f.value, f.inline)); + embed.setTimestamp(); + embed.setColor(3447003); + return embed; + }); + + if (embeds.length === 1) { + msg.edit({ content: '', embed: embeds[0] }); + } else { + createPaginationEmbed(message, this.client, embeds, {}, msg); + } + return msg; + } catch (error) { + return this.client.util.handleError(error, message, this); + } + } +} diff --git a/src/commands/notify.ts b/src/commands/notify.ts index ff3ff1e..c7b4cfd 100644 --- a/src/commands/notify.ts +++ b/src/commands/notify.ts @@ -1,53 +1,51 @@ -/* eslint-disable consistent-return */ -import { Message } from 'eris'; -import { Client } from '..'; -import { Command, RichEmbed } from '../class'; - -export default class Notify extends Command { - constructor(client: Client) { - super(client); - this.name = 'notify'; - this.description = 'Sends a notification to a user.'; - this.usage = `${this.client.config.prefix}notify [username | user ID]`; - this.permissions = { roles: ['446104438969466890'] }; - this.enabled = true; - } - - public async run(message: Message, args: string[]) { - try { - if (!args.length) return this.client.commands.get('help').run(message, [this.name]); - const edit = await message.channel.createMessage(`***${this.client.stores.emojis.loading} Sending notification...***`); - const account = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { userID: args[0].replace(/[<@!>]/gi, '') }] }); - if (!account) return edit.edit(`***${this.client.stores.emojis.error} Cannot find user.***`); - const embed = new RichEmbed() - .setTitle('Cloud Account | Notification') - .setDescription(args.slice(1).join(' ')) - .addField('Moderator', `<@${message.author.id}>`, true) - .setFooter(this.client.user.username, this.client.user.avatarURL) - .setTimestamp(); - this.client.getDMChannel(account.userID).then((channel) => { - // @ts-ignore - channel.createMessage({ embed }); - }); - embed.addField('User', `${account.username} | <@${account.userID}>`, true); - // @ts-ignore - this.client.createMessage('580950455581147146', { embed }); - this.client.util.transport.sendMail({ - to: account.emailAddress, - from: 'Library of Code sp-us | Cloud Services ', - subject: 'Notification', - html: ` -

Library of Code sp-us | Cloud Services

-

${args.slice(1).join(' ')}

-

Moderator: ${message.author.username}

- - Library of Code sp-us | Support Team - `, - }); - message.delete(); - edit.edit(`***${this.client.stores.emojis.success} Send notification to ${account.username}.***`); - } catch (error) { - await this.client.util.handleError(error, message, this); - } - } -} +/* eslint-disable consistent-return */ +import { Message } from 'eris'; +import { Client } from '..'; +import { Command, RichEmbed } from '../class'; + +export default class Notify extends Command { + constructor(client: Client) { + super(client); + this.name = 'notify'; + this.description = 'Sends a notification to a user.'; + this.usage = `${this.client.config.prefix}notify [username | user ID]`; + this.permissions = { roles: ['446104438969466890'] }; + this.enabled = true; + } + + public async run(message: Message, args: string[]) { + try { + if (!args.length) return this.client.commands.get('help').run(message, [this.name]); + const edit = await message.channel.createMessage(`***${this.client.stores.emojis.loading} Sending notification...***`); + const account = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { userID: args[0].replace(/[<@!>]/gi, '') }] }); + if (!account) return edit.edit(`***${this.client.stores.emojis.error} Cannot find user.***`); + const embed = new RichEmbed() + .setTitle('Cloud Account | Notification') + .setDescription(args.slice(1).join(' ')) + .addField('Moderator', `<@${message.author.id}>`, true) + .setFooter(this.client.user.username, this.client.user.avatarURL) + .setTimestamp(); + this.client.getDMChannel(account.userID).then((channel) => { + channel.createMessage({ embed }); + }); + embed.addField('User', `${account.username} | <@${account.userID}>`, true); + this.client.createMessage('580950455581147146', { embed }); + this.client.util.transport.sendMail({ + to: account.emailAddress, + from: 'Library of Code sp-us | Cloud Services ', + subject: 'Notification', + html: ` +

Library of Code sp-us | Cloud Services

+

${args.slice(1).join(' ')}

+

Moderator: ${message.author.username}

+ + Library of Code sp-us | Support Team + `, + }); + message.delete(); + edit.edit(`***${this.client.stores.emojis.success} Send notification to ${account.username}.***`); + } catch (error) { + await this.client.util.handleError(error, message, this); + } + } +} diff --git a/src/commands/parse.ts b/src/commands/parse.ts index 2bd957b..f23f682 100644 --- a/src/commands/parse.ts +++ b/src/commands/parse.ts @@ -1,70 +1,69 @@ -import fs from 'fs-extra'; -import { parseCert } from '@ghaiklor/x509'; -import { Message } from 'eris'; -import { Client } from '..'; -import { Command, RichEmbed } from '../class'; -import { parseCertificate, Certificate } from '../functions'; - -export default class Parse extends Command { - constructor(client: Client) { - super(client); - this.name = 'parse'; - this.description = 'Gets information on a user\'s x509 certificate.'; - this.usage = `${this.client.config.prefix}parse [username || user ID]`; - this.permissions = { roles: ['446104438969466890'] }; - } - - public async run(message: Message, args: string[]) { // eslint-disable-line - try { - if (!args.length) return this.client.commands.get('help').run(message, [this.name]); - const account = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { userID: args[0].replace(/[<@!>]/gi, '') }] }); - if (!account) return message.channel.createMessage(`***${this.client.stores.emojis.error} Cannot find user.***`); - let dir: string[]; - try { - dir = await fs.readdir(`${account.homepath}/Validation`); - } catch (err) { - return message.channel.createMessage(`***${this.client.stores.emojis.error} Cannot locate Validation directory.***`); - } - if (!dir.length) return message.channel.createMessage(`***${this.client.stores.emojis.error} Cannot locate certificate.***`); - let cert: Certificate; - try { - cert = await parseCertificate(this.client, `${account.homepath}/Validation/${dir[0]}`); - } catch (error) { - if (error.message.includes('panic: Certificate PEM Encode == nil')) return message.channel.createMessage(`***${this.client.stores.emojis.error} Invalid certificate.***`); - } - // const cert = parseCert(`${account.homepath}/Validation/${dir[0]}`); - const subjectCommonName = cert.subject.commonName || 'Not Specified'; - const subjectEmailAddress = cert.subject.emailAddress || 'Not Specified'; - const subjectOrganization = cert.subject.organizationName || 'Not Specified'; - const subjectOrganizationalUnit = cert.subject.organizationalUnitName || 'Not Specified'; - const subjectCountry = cert.subject.countryName || 'Not Specified'; - const issuerCommonName = cert.issuer.commonName || 'Not Specified'; - const issuerEmailAddress = cert.issuer.emailAddress || 'Not Specified'; - const issuerOrganization = cert.issuer.organizationName || 'Not Specified'; - const issuerOrganizationalUnit = cert.issuer.organizationalUnitName || 'Not Specified'; - const issuerCountry = cert.issuer.countryName || 'Not Specified'; - const user = this.client.users.get(account.userID) || await this.client.getRESTUser(account.userID); - const embed = new RichEmbed(); - embed.setTitle('Parse x509 Certificate'); - embed.setDescription(`${account.homepath}/Validation/${dir[0]} | ${account.username} <@${user.id}>`); - embed.setColor(3447003); - embed.addField('Subject', `**Common Name:** ${subjectCommonName}\n**Email Address:** ${subjectEmailAddress}\n**Organization:** ${subjectOrganization}\n**Organizational Unit:** ${subjectOrganizationalUnit}\n**Country:** ${subjectCountry}`, true); - embed.addField('Issuer', `**Common Name:** ${issuerCommonName}\n**Email Address:** ${issuerEmailAddress}\n**Organization:** ${issuerOrganization}\n**Organizational Unit:** ${issuerOrganizationalUnit}\n**Country:** ${issuerCountry}`, true); - embed.addField('Serial Number', cert.serial, true); - embed.addField('Fingerprint', cert.fingerPrint, true); - embed.addField('Signature Algorithm', cert.signatureAlgorithm, true); - embed.addField('Public Key Algorithm', cert.publicKeyAlgorithm, true); - embed.addField('Key Usage', cert.extensions.keyUsage, true); - embed.addField('Extended Key Usage', cert.extensions.extendedKeyUsage.join(', '), true); - embed.addField('Policies', cert.extensions.certificatePolicies.join(', '), true); - embed.addField('Issued On', new Date(cert.notBefore).toLocaleString('en-us'), true); - embed.addField('Expires On', new Date(cert.notAfter).toLocaleString('en-us'), true); - embed.setFooter(this.client.user.username, this.client.user.avatarURL); - embed.setTimestamp(); - // @ts-ignore - message.channel.createMessage({ embed }); - } catch (error) { - await this.client.util.handleError(error, message, this); - } - } -} +import fs from 'fs-extra'; +import { parseCert } from '@ghaiklor/x509'; +import { Message } from 'eris'; +import { Client } from '..'; +import { Command, RichEmbed } from '../class'; +import { parseCertificate, Certificate } from '../functions'; + +export default class Parse extends Command { + constructor(client: Client) { + super(client); + this.name = 'parse'; + this.description = 'Gets information on a user\'s x509 certificate.'; + this.usage = `${this.client.config.prefix}parse [username || user ID]`; + this.permissions = { roles: ['446104438969466890'] }; + } + + public async run(message: Message, args: string[]) { // eslint-disable-line + try { + if (!args.length) return this.client.commands.get('help').run(message, [this.name]); + const account = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { userID: args[0].replace(/[<@!>]/gi, '') }] }); + if (!account) return message.channel.createMessage(`***${this.client.stores.emojis.error} Cannot find user.***`); + let dir: string[]; + try { + dir = await fs.readdir(`${account.homepath}/Validation`); + } catch (err) { + return message.channel.createMessage(`***${this.client.stores.emojis.error} Cannot locate Validation directory.***`); + } + if (!dir.length) return message.channel.createMessage(`***${this.client.stores.emojis.error} Cannot locate certificate.***`); + let cert: Certificate; + try { + cert = await parseCertificate(this.client, `${account.homepath}/Validation/${dir[0]}`); + } catch (error) { + if (error.message.includes('panic: Certificate PEM Encode == nil')) return message.channel.createMessage(`***${this.client.stores.emojis.error} Invalid certificate.***`); + } + // const cert = parseCert(`${account.homepath}/Validation/${dir[0]}`); + const subjectCommonName = cert.subject.commonName || 'Not Specified'; + const subjectEmailAddress = cert.subject.emailAddress || 'Not Specified'; + const subjectOrganization = cert.subject.organizationName || 'Not Specified'; + const subjectOrganizationalUnit = cert.subject.organizationalUnitName || 'Not Specified'; + const subjectCountry = cert.subject.countryName || 'Not Specified'; + const issuerCommonName = cert.issuer.commonName || 'Not Specified'; + const issuerEmailAddress = cert.issuer.emailAddress || 'Not Specified'; + const issuerOrganization = cert.issuer.organizationName || 'Not Specified'; + const issuerOrganizationalUnit = cert.issuer.organizationalUnitName || 'Not Specified'; + const issuerCountry = cert.issuer.countryName || 'Not Specified'; + const user = this.client.users.get(account.userID) || await this.client.getRESTUser(account.userID); + const embed = new RichEmbed(); + embed.setTitle('Parse x509 Certificate'); + embed.setDescription(`${account.homepath}/Validation/${dir[0]} | ${account.username} <@${user.id}>`); + embed.setColor(3447003); + embed.addField('Subject', `**Common Name:** ${subjectCommonName}\n**Email Address:** ${subjectEmailAddress}\n**Organization:** ${subjectOrganization}\n**Organizational Unit:** ${subjectOrganizationalUnit}\n**Country:** ${subjectCountry}`, true); + embed.addField('Issuer', `**Common Name:** ${issuerCommonName}\n**Email Address:** ${issuerEmailAddress}\n**Organization:** ${issuerOrganization}\n**Organizational Unit:** ${issuerOrganizationalUnit}\n**Country:** ${issuerCountry}`, true); + embed.addField('Serial Number', cert.serial, true); + embed.addField('Fingerprint', cert.fingerPrint, true); + embed.addField('Signature Algorithm', cert.signatureAlgorithm, true); + embed.addField('Public Key Algorithm', cert.publicKeyAlgorithm, true); + embed.addField('Key Usage', cert.extensions.keyUsage, true); + embed.addField('Extended Key Usage', cert.extensions.extendedKeyUsage.join(', '), true); + embed.addField('Policies', cert.extensions.certificatePolicies.join(', '), true); + embed.addField('Issued On', new Date(cert.notBefore).toLocaleString('en-us'), true); + embed.addField('Expires On', new Date(cert.notAfter).toLocaleString('en-us'), true); + embed.setFooter(this.client.user.username, this.client.user.avatarURL); + embed.setTimestamp(); + message.channel.createMessage({ embed }); + } catch (error) { + await this.client.util.handleError(error, message, this); + } + } +} diff --git a/src/commands/parseall.ts b/src/commands/parseall.ts index 65e1531..7e1164a 100644 --- a/src/commands/parseall.ts +++ b/src/commands/parseall.ts @@ -1,75 +1,72 @@ -import { parseCert } from '@ghaiklor/x509'; -import { Message } from 'eris'; -import { readdirSync } from 'fs'; -import moment from 'moment'; -import { Client } from '..'; -import { Command, RichEmbed } from '../class'; -import { parseCertificate, Certificate } from '../functions'; - -export default class Parseall extends Command { - constructor(client: Client) { - super(client); - - this.name = 'parseall'; - this.description = 'Displays certificate validation for all accounts'; - this.usage = `${this.client.config.prefix}parseall`; - this.permissions = { roles: ['446104438969466890'] }; - this.aliases = ['checkcerts', 'verifyall', 'verifycerts']; - } - - public async run(message: Message, args: string[]) { - try { - const msg = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Loading...***`); - const embed = new RichEmbed(); - embed.setTitle('Certificate Validation'); - embed.setAuthor(this.client.user.username, this.client.user.avatarURL); - embed.setFooter(`Requested by ${message.member.username}#${message.member.discriminator}`, message.member.avatarURL); - embed.setTimestamp(); - const search = await this.client.db.Account.find(); - - const files = search.map((acc) => { - let certfile: string; - try { certfile = readdirSync(`${acc.homepath}/Validation`)[0] } catch (error) { if (error.message.includes('no such file or directory') || error.message.includes('File doesn\'t exist.')) certfile = 'not_found.crt' } // eslint-disable-line - return `${acc.homepath}/Validation/${certfile}`; - }); - - // @ts-ignore - const parsed: ({ status: 'fulfilled', value: Certificate } | { status: 'rejected', reason: Error })[] = await Promise.allSettled(files.map((c) => parseCertificate(this.client, c))); - - const final: string[] = await Promise.all(search.map(async (a) => { - const result = parsed[search.findIndex((acc) => acc === a)]; - if (result.status === 'rejected') { - if (result.reason.message.includes('no such file or directory') || result.reason.message.includes('File doesn\'t exist.')) return `${this.client.stores.emojis.error} **${a.username}** Unable to locate certificate`; - if (result.reason.message.includes('panic: Certificate PEM Encode == nil')) return `${this.client.stores.emojis.error} **${a.username}** Invalid certificate`; - throw result.reason; - } - const { notAfter } = result.value; - // @ts-ignore - const timeObject: {years: number, months: number, days: number, hours: number, minutes: number, seconds: number, firstDateWasLater: boolean} = moment.preciseDiff(new Date(), notAfter, true); - const precise: [number, string][] = []; - // @ts-ignore - const timeArray: number[] = Object.values(timeObject).filter((v) => typeof v === 'number'); - timeArray.forEach((t) => { // eslint-disable-line - const index = timeArray.indexOf(t); - const measurements = ['yr', 'mo', 'd', 'h', 'm', 's']; - precise.push([t, measurements[index]]); - }); - const time = precise.filter((n) => n[0]).map(((v) => v.join(''))).join(', '); - - if (notAfter < new Date()) return `${this.client.stores.emojis.error} **${a.username}** Expired ${time} ago`; - return `${this.client.stores.emojis.success} **${a.username}** Expires in ${time}`; - })); - - if (final.join('\n').length < 2048) embed.setDescription(final.join('\n')); - else { - const split = this.client.util.splitString(final.join('\n'), 1024); - split.forEach((s) => embed.addField('\u200B', s)); - } - - // @ts-ignore - return await msg.edit({ content: '', embed }); - } catch (error) { - return this.client.util.handleError(error, message, this); - } - } -} +import { parseCert } from '@ghaiklor/x509'; +import { Message } from 'eris'; +import { readdirSync } from 'fs'; +import moment from 'moment'; +import 'moment-precise-range-plugin'; +import { Client } from '..'; +import { Command, RichEmbed } from '../class'; +import { parseCertificate, Certificate } from '../functions'; + +export default class Parseall extends Command { + constructor(client: Client) { + super(client); + + this.name = 'parseall'; + this.description = 'Displays certificate validation for all accounts'; + this.usage = `${this.client.config.prefix}parseall`; + this.permissions = { roles: ['446104438969466890'] }; + this.aliases = ['checkcerts', 'verifyall', 'verifycerts']; + } + + public async run(message: Message, args: string[]) { + try { + const msg = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Loading...***`); + const embed = new RichEmbed(); + embed.setTitle('Certificate Validation'); + embed.setAuthor(this.client.user.username, this.client.user.avatarURL); + embed.setFooter(`Requested by ${message.member.username}#${message.member.discriminator}`, message.member.avatarURL); + embed.setTimestamp(); + const search = await this.client.db.Account.find(); + + const files = search.map((acc) => { + let certfile: string; + try { certfile = readdirSync(`${acc.homepath}/Validation`)[0] } catch (error) { if (error.message.includes('no such file or directory') || error.message.includes('File doesn\'t exist.')) certfile = 'not_found.crt' } // eslint-disable-line + return `${acc.homepath}/Validation/${certfile}`; + }); + + const parsed = await Promise.allSettled(files.map((c) => parseCertificate(this.client, c))); + + const final: string[] = await Promise.all(search.map(async (a) => { + const result = parsed[search.findIndex((acc) => acc === a)]; + if (result.status === 'rejected') { + if (result.reason.message.includes('no such file or directory') || result.reason.message.includes('File doesn\'t exist.')) return `${this.client.stores.emojis.error} **${a.username}** Unable to locate certificate`; + if (result.reason.message.includes('panic: Certificate PEM Encode == nil')) return `${this.client.stores.emojis.error} **${a.username}** Invalid certificate`; + throw result.reason; + } + const { notAfter } = result.value; + const timeObject = moment.preciseDiff(new Date(), notAfter, true); + const precise: [number, string][] = []; + const timeArray: number[] = Object.values(timeObject).filter((v) => typeof v === 'number'); + timeArray.forEach((t) => { // eslint-disable-line + const index = timeArray.indexOf(t); + const measurements = ['yr', 'mo', 'd', 'h', 'm', 's']; + precise.push([t, measurements[index]]); + }); + const time = precise.filter((n) => n[0]).map(((v) => v.join(''))).join(', '); + + if (notAfter < new Date()) return `${this.client.stores.emojis.error} **${a.username}** Expired ${time} ago`; + return `${this.client.stores.emojis.success} **${a.username}** Expires in ${time}`; + })); + + if (final.join('\n').length < 2048) embed.setDescription(final.join('\n')); + else { + const split = this.client.util.splitString(final.join('\n'), 1024); + split.forEach((s) => embed.addField('\u200B', s)); + } + + return await msg.edit({ content: '', embed }); + } catch (error) { + return this.client.util.handleError(error, message, this); + } + } +} diff --git a/src/commands/securesign_account.ts b/src/commands/securesign_account.ts index 64b4ed6..0ec1efc 100644 --- a/src/commands/securesign_account.ts +++ b/src/commands/securesign_account.ts @@ -1,47 +1,46 @@ -import { Message, PrivateChannel } from 'eris'; -import { Client } from '..'; -import { Command, RichEmbed } from '../class'; -import { AccountInterface } from '../models'; - -export default class SecureSign_Account extends Command { - constructor(client: Client) { - super(client); - this.name = 'account'; - this.description = 'Provides SecureSign account details for currently logged in user'; - this.usage = `${this.client.config.prefix}securesign account`; - this.enabled = true; - this.guildOnly = false; - } - - public async run(message: Message, args: string[]) { - try { - const user = await this.client.db.Account.findOne({ userID: message.author.id }); - if (!user || (!user.permissions.staff && !(message.channel instanceof PrivateChannel))) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Run this command in your DMs!***`); - - let account: AccountInterface; - if (!args[0] || !user.permissions.staff) account = user; - else account = await this.client.db.Account.findOne({ $or: [{ userID: args[0] }, { username: args[0] }, { emailAddress: args[0] }] }); - - if (!account) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Account not found***`); - if (!account.hash) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Account not initialized***`); - const msg = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Loading account details...***`); - - const details = await this.client.util.exec(`sudo -H -u ${account.username} bash -c 'securesign-canary account'`); - const info = details.replace(/^\s+|\s+$/g, '').replace(/\n/g, '\n**').replace(/: /g, ':** ').split('\n'); - const title = info.shift(); - const description = info.join('\n'); - const content = ''; - - const embed = new RichEmbed(); - embed.setTitle(title); - embed.setDescription(description); - embed.setAuthor(this.client.user.username, this.client.user.avatarURL); - embed.setFooter(`Requested by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL); - - // @ts-ignore - return msg.edit({ content, embed }); - } catch (error) { - return this.client.util.handleError(error, message, this); - } - } -} +import { Message, PrivateChannel } from 'eris'; +import { Client } from '..'; +import { Command, RichEmbed } from '../class'; +import { AccountInterface } from '../models'; + +export default class SecureSign_Account extends Command { + constructor(client: Client) { + super(client); + this.name = 'account'; + this.description = 'Provides SecureSign account details for currently logged in user'; + this.usage = `${this.client.config.prefix}securesign account`; + this.enabled = true; + this.guildOnly = false; + } + + public async run(message: Message, args: string[]) { + try { + const user = await this.client.db.Account.findOne({ userID: message.author.id }); + if (!user || (!user.permissions.staff && !(message.channel instanceof PrivateChannel))) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Run this command in your DMs!***`); + + let account: AccountInterface; + if (!args[0] || !user.permissions.staff) account = user; + else account = await this.client.db.Account.findOne({ $or: [{ userID: args[0] }, { username: args[0] }, { emailAddress: args[0] }] }); + + if (!account) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Account not found***`); + if (!account.hash) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Account not initialized***`); + const msg = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Loading account details...***`); + + const details = await this.client.util.exec(`sudo -H -u ${account.username} bash -c 'securesign-canary account'`); + const info = details.replace(/^\s+|\s+$/g, '').replace(/\n/g, '\n**').replace(/: /g, ':** ').split('\n'); + const title = info.shift(); + const description = info.join('\n'); + const content = ''; + + const embed = new RichEmbed(); + embed.setTitle(title); + embed.setDescription(description); + embed.setAuthor(this.client.user.username, this.client.user.avatarURL); + embed.setFooter(`Requested by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL); + + return msg.edit({ content, embed }); + } catch (error) { + return this.client.util.handleError(error, message, this); + } + } +} diff --git a/src/commands/securesign_build.ts b/src/commands/securesign_build.ts index fb28b3e..48f5d2a 100644 --- a/src/commands/securesign_build.ts +++ b/src/commands/securesign_build.ts @@ -1,36 +1,35 @@ -import { Message } from 'eris'; -import { Client } from '..'; -import { Command, RichEmbed } from '../class'; - -export default class SecureSign_Build extends Command { - constructor(client: Client) { - super(client); - this.name = 'build'; - this.description = 'Shows information about the current build of the CLI'; - this.usage = `${this.client.config.prefix}securesign build`; - this.enabled = true; - } - - public async run(message: Message, args: string[]) { - try { - const msg = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Loading build information...***`); - - const build = await this.client.util.exec("sudo -H -u root bash -c 'securesign-canary build'"); - const info = build.replace(/^\s+|\s+$/g, '').replace(/\n/g, '\n**').replace(/: /g, ':** ').split('\n'); - const title = info.shift(); - const description = info.join('\n'); - const content = ''; - - const embed = new RichEmbed(); - embed.setTitle(title); - embed.setDescription(description); - embed.setAuthor(this.client.user.username, this.client.user.avatarURL); - embed.setFooter(`Requested by ${message.member.username}#${message.member.discriminator}`, message.member.avatarURL); - - // @ts-ignore - msg.edit({ content, embed }); - } catch (error) { - this.client.util.handleError(error, message, this); - } - } -} +import { Message } from 'eris'; +import { Client } from '..'; +import { Command, RichEmbed } from '../class'; + +export default class SecureSign_Build extends Command { + constructor(client: Client) { + super(client); + this.name = 'build'; + this.description = 'Shows information about the current build of the CLI'; + this.usage = `${this.client.config.prefix}securesign build`; + this.enabled = true; + } + + public async run(message: Message, args: string[]) { + try { + const msg = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Loading build information...***`); + + const build = await this.client.util.exec("sudo -H -u root bash -c 'securesign-canary build'"); + const info = build.replace(/^\s+|\s+$/g, '').replace(/\n/g, '\n**').replace(/: /g, ':** ').split('\n'); + const title = info.shift(); + const description = info.join('\n'); + const content = ''; + + const embed = new RichEmbed(); + embed.setTitle(title); + embed.setDescription(description); + embed.setAuthor(this.client.user.username, this.client.user.avatarURL); + embed.setFooter(`Requested by ${message.member.username}#${message.member.discriminator}`, message.member.avatarURL); + + msg.edit({ content, embed }); + } catch (error) { + this.client.util.handleError(error, message, this); + } + } +} diff --git a/src/commands/securesign_createcrt.ts b/src/commands/securesign_createcrt.ts index 80e5d8a..85052db 100644 --- a/src/commands/securesign_createcrt.ts +++ b/src/commands/securesign_createcrt.ts @@ -1,65 +1,64 @@ -import { Message, PrivateChannel, TextChannel } from 'eris'; -import axios from 'axios'; -import { Client } from '..'; -import { Command } from '../class'; - -export default class SecureSign_Init extends Command { - constructor(client: Client) { - super(client); - this.name = 'createcrt'; - this.description = 'Creates a new certificate'; - this.usage = `${this.client.config.prefix}securesign createcrt [-s sign] [-c class] [-m digest]\n\`sign\`: Sign type (ecc/rsa)\n\`class\`: Certificate Class (1/2/3)\n\`digest\`: SHA Digest (256/384/512)`; - this.enabled = true; - this.guildOnly = false; - } - - public async run(message: Message, args: string[]) { - try { - const account = await this.client.db.Account.findOne({ userID: message.author.id }); - if (!account) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Account not found***`); - if (!account.hash) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Account not initialized***`); - - // @ts-ignore - const options: { s?: string, c?: string, m?: string } = args.length ? Object.fromEntries(` ${args.join(' ')}`.split(' -').filter((a) => a).map((a) => a.split(/ (.+)/)).filter((a) => a.length > 1)) : {}; - if (options.s && options.s.toLowerCase() !== 'ecc' && options.s.toLowerCase() !== 'rsa') return message.channel.createMessage(`${this.client.stores.emojis.error} ***Invalid signing type, choose between \`ecc\` or \`rsa\``); - if (options.c && (!Number(options.c) || Number(options.c) < 1 || Number(options.c) > 3)) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Invalid class selected, choose between Class \`1\`, \`2\` or \`3\``); - if (options.m && (!Number(options.m) || (options.m !== '256' && options.m !== '384' && options.m !== '512'))) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Invalid SHA Digest selected, choose between \`256\`, \`384\` or \`512\``); - if (Number(options.c) === 3 && (!options.s || options.s.toLowerCase() === 'ecc')) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Class 3 ECC certificates are not supported, please use the \`-s rsa\` option instead***`); - - const msg = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Creating certificate...***`); - if (options.s) options.s = options.s.toLowerCase(); - const hash = this.client.util.getAcctHash(account.homepath); - - // Check if they can generate certificate - try { - const { data } = await axios({ - method: 'GET', - url: 'https://api.securesign.org/account/details', - headers: { Authorization: hash, 'Content-Type': 'application/json' }, - }); - - const { total, allowed } = data.message; - if (total >= allowed) return msg.edit(`${this.client.stores.emojis.error} ***Not enough certificate allowances - please ask a member of staff to increase this limit from ${total}***`); - if (Number(options.c) > data.message.class) return msg.edit(`${this.client.stores.emojis.error} ***Class too low, you are on a class ${data.message.class} account***`); - } catch (error) { - const { code } = error.response.data; - if (code === 1001) { - await this.client.db.Account.updateOne({ userID: account.userID }, { $set: { hash: false } }); - this.client.getDMChannel(account.userID).then((channel) => channel.createMessage('Your SecureSign password has been reset - please reinitialize your SecureSign account')).catch(); - return msg.edit(`${this.client.stores.emojis.error} ***Authentication failed***`); - } - throw error; - } - - const execoptions = `${options.s ? ` -s ${options.s}` : ''}${options.c ? ` -c ${options.c}` : ''}${options.m ? ` -m ${options.m}` : ''}`; - const cmd = `sudo -H -u ${account.username} bash -c 'securesign-canary createcrt${execoptions}'`; - - const exec = await this.client.util.exec(cmd); - if (!exec.replace(/^\s+|\s+$/g, '').endsWith('Successfully wrote certificate.')) throw new Error(`Certificate generation did not complete successfully:\n${cmd}`); - - return msg.edit(`${this.client.stores.emojis.success} ***Successfully created certificate***`); - } catch (error) { - return this.client.util.handleError(error, message, this); - } - } -} +import { Message, PrivateChannel, TextChannel } from 'eris'; +import axios from 'axios'; +import { Client } from '..'; +import { Command } from '../class'; + +export default class SecureSign_Init extends Command { + constructor(client: Client) { + super(client); + this.name = 'createcrt'; + this.description = 'Creates a new certificate'; + this.usage = `${this.client.config.prefix}securesign createcrt [-s sign] [-c class] [-m digest]\n\`sign\`: Sign type (ecc/rsa)\n\`class\`: Certificate Class (1/2/3)\n\`digest\`: SHA Digest (256/384/512)`; + this.enabled = true; + this.guildOnly = false; + } + + public async run(message: Message, args: string[]) { + try { + const account = await this.client.db.Account.findOne({ userID: message.author.id }); + if (!account) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Account not found***`); + if (!account.hash) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Account not initialized***`); + + const options: { s?: string, c?: string, m?: string } = args.length ? Object.fromEntries(` ${args.join(' ')}`.split(' -').filter((a) => a).map((a) => a.split(/ (.+)/)).filter((a) => a.length > 1)) : {}; + if (options.s && options.s.toLowerCase() !== 'ecc' && options.s.toLowerCase() !== 'rsa') return message.channel.createMessage(`${this.client.stores.emojis.error} ***Invalid signing type, choose between \`ecc\` or \`rsa\``); + if (options.c && (!Number(options.c) || Number(options.c) < 1 || Number(options.c) > 3)) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Invalid class selected, choose between Class \`1\`, \`2\` or \`3\``); + if (options.m && (!Number(options.m) || (options.m !== '256' && options.m !== '384' && options.m !== '512'))) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Invalid SHA Digest selected, choose between \`256\`, \`384\` or \`512\``); + if (Number(options.c) === 3 && (!options.s || options.s.toLowerCase() === 'ecc')) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Class 3 ECC certificates are not supported, please use the \`-s rsa\` option instead***`); + + const msg = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Creating certificate...***`); + if (options.s) options.s = options.s.toLowerCase(); + const hash = this.client.util.getAcctHash(account.homepath); + + // Check if they can generate certificate + try { + const { data } = await axios({ + method: 'GET', + url: 'https://api.securesign.org/account/details', + headers: { Authorization: hash, 'Content-Type': 'application/json' }, + }); + + const { total, allowed } = data.message; + if (total >= allowed) return msg.edit(`${this.client.stores.emojis.error} ***Not enough certificate allowances - please ask a member of staff to increase this limit from ${total}***`); + if (Number(options.c) > data.message.class) return msg.edit(`${this.client.stores.emojis.error} ***Class too low, you are on a class ${data.message.class} account***`); + } catch (error) { + const { code } = error.response.data; + if (code === 1001) { + await this.client.db.Account.updateOne({ userID: account.userID }, { $set: { hash: false } }); + this.client.getDMChannel(account.userID).then((channel) => channel.createMessage('Your SecureSign password has been reset - please reinitialize your SecureSign account')).catch(); + return msg.edit(`${this.client.stores.emojis.error} ***Authentication failed***`); + } + throw error; + } + + const execoptions = `${options.s ? ` -s ${options.s}` : ''}${options.c ? ` -c ${options.c}` : ''}${options.m ? ` -m ${options.m}` : ''}`; + const cmd = `sudo -H -u ${account.username} bash -c 'securesign-canary createcrt${execoptions}'`; + + const exec = await this.client.util.exec(cmd); + if (!exec.replace(/^\s+|\s+$/g, '').endsWith('Successfully wrote certificate.')) throw new Error(`Certificate generation did not complete successfully:\n${cmd}`); + + return msg.edit(`${this.client.stores.emojis.success} ***Successfully created certificate***`); + } catch (error) { + return this.client.util.handleError(error, message, this); + } + } +} diff --git a/src/commands/securesign_init.ts b/src/commands/securesign_init.ts index 1ccccfa..ecaacb3 100644 --- a/src/commands/securesign_init.ts +++ b/src/commands/securesign_init.ts @@ -1,56 +1,55 @@ -import { Message, PrivateChannel, TextChannel } from 'eris'; -import axios, { AxiosResponse } from 'axios'; -import { Client } from '..'; -import { Command } from '../class'; - -export default class SecureSign_Init extends Command { - constructor(client: Client) { - super(client); - this.name = 'init'; - this.description = 'Inits configuration files and environment variables (DM only)'; - this.usage = `${this.client.config.prefix}securesign init [hash]`; - this.enabled = true; - this.guildOnly = false; - } - - public async run(message: Message, args: string[]) { - try { - if (!args[0]) return this.client.commands.get('help').run(message, ['securesign', this.name]); - if (!(message.channel instanceof PrivateChannel)) { - message.delete(); - return message.channel.createMessage(`${this.client.stores.emojis.error} ***Run this command in your DMs!***`); - } - const account = await this.client.db.Account.findOne({ userID: message.author.id }); - if (!account) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Account not registered***`); - if (account.locked) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Your account is locked***`); - const msg = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Initializing account...***`); - let verify: AxiosResponse; - try { - verify = await axios({ - method: 'get', - url: 'https://api.securesign.org/account/details', - headers: { Authorization: args[0] }, - }); - } catch (error) { - const { status } = error.response; - if (status === 400 || status === 401 || status === 403 || status === 404) return msg.edit(`${this.client.stores.emojis.error} ***Credentials incorrect***`); - throw error; - } - const { id } = verify.data.message; - if (id !== message.author.id && !account.root) { - // @ts-ignore - const channel: TextChannel = this.client.guilds.get('446067825673633794').channels.get('501089664040697858'); - channel.createMessage(`**__UNAUTHORIZED ACCESS ALERT__**\n${message.author.mention} tried to initialize their account using <@${id}>'s SecureSign credentials.\nTheir account has been locked under Section 5.2 of the EULA.`); - const tasks = [this.client.util.exec(`lock ${account.username}`), account.updateOne({ locked: true }), this.client.util.createModerationLog(account.userID, this.client.user, 2, 'Violation of Section 5.2 of the EULA')]; - await Promise.all(tasks); - return msg.edit(`${this.client.stores.emojis.error} ***Credentials incorrect***`); - } - const init = await this.client.util.exec(`sudo -H -u ${account.username} bash -c 'securesign-canary init -a ${args[0]}'`); - if (!init.replace(/^\s+|\s+$/g, '').endsWith('Initialization sequence completed.')) throw new Error(`Account initialization did not complete successfully:\n${init}`); - await this.client.db.Account.updateOne({ userID: message.author.id }, { $set: { hash: true } }); - return msg.edit(`${this.client.stores.emojis.success} ***Account initialized***`); - } catch (error) { - return this.client.util.handleError(error, message, this); - } - } -} +import { Message, PrivateChannel, TextChannel } from 'eris'; +import axios, { AxiosResponse } from 'axios'; +import { Client } from '..'; +import { Command } from '../class'; + +export default class SecureSign_Init extends Command { + constructor(client: Client) { + super(client); + this.name = 'init'; + this.description = 'Inits configuration files and environment variables (DM only)'; + this.usage = `${this.client.config.prefix}securesign init [hash]`; + this.enabled = true; + this.guildOnly = false; + } + + public async run(message: Message, args: string[]) { + try { + if (!args[0]) return this.client.commands.get('help').run(message, ['securesign', this.name]); + if (!(message.channel instanceof PrivateChannel)) { + message.delete(); + return message.channel.createMessage(`${this.client.stores.emojis.error} ***Run this command in your DMs!***`); + } + const account = await this.client.db.Account.findOne({ userID: message.author.id }); + if (!account) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Account not registered***`); + if (account.locked) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Your account is locked***`); + const msg = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Initializing account...***`); + let verify: AxiosResponse; + try { + verify = await axios({ + method: 'get', + url: 'https://api.securesign.org/account/details', + headers: { Authorization: args[0] }, + }); + } catch (error) { + const { status } = error.response; + if (status === 400 || status === 401 || status === 403 || status === 404) return msg.edit(`${this.client.stores.emojis.error} ***Credentials incorrect***`); + throw error; + } + const { id } = verify.data.message; + if (id !== message.author.id && !account.root) { + const channel = this.client.guilds.get('446067825673633794').channels.get('501089664040697858') as TextChannel; + channel.createMessage(`**__UNAUTHORIZED ACCESS ALERT__**\n${message.author.mention} tried to initialize their account using <@${id}>'s SecureSign credentials.\nTheir account has been locked under Section 5.2 of the EULA.`); + const tasks = [this.client.util.exec(`lock ${account.username}`), account.updateOne({ locked: true }), this.client.util.createModerationLog(account.userID, this.client.user, 2, 'Violation of Section 5.2 of the EULA')]; + await Promise.all(tasks); + return msg.edit(`${this.client.stores.emojis.error} ***Credentials incorrect***`); + } + const init = await this.client.util.exec(`sudo -H -u ${account.username} bash -c 'securesign-canary init -a ${args[0]}'`); + if (!init.replace(/^\s+|\s+$/g, '').endsWith('Initialization sequence completed.')) throw new Error(`Account initialization did not complete successfully:\n${init}`); + await this.client.db.Account.updateOne({ userID: message.author.id }, { $set: { hash: true } }); + return msg.edit(`${this.client.stores.emojis.success} ***Account initialized***`); + } catch (error) { + return this.client.util.handleError(error, message, this); + } + } +} diff --git a/src/commands/sysinfo.ts b/src/commands/sysinfo.ts index 3f880a2..8925cae 100644 --- a/src/commands/sysinfo.ts +++ b/src/commands/sysinfo.ts @@ -1,36 +1,35 @@ -import moment from 'moment'; -import { Message } from 'eris'; -import os, { totalmem } from 'os'; -import { Command, RichEmbed } from '../class'; -import { dataConversion } from '../functions'; -import { Client } from '..'; - -export default class SysInfo extends Command { - constructor(client: Client) { - super(client); - this.name = 'sysinfo'; - this.description = 'Provides system information.'; - this.enabled = true; - } - - public async run(message: Message) { - const availableMemory: string = await this.client.util.exec('free -b'); - const usedMemory = dataConversion(totalmem() - Number(availableMemory.split('\n')[1].split(' ').slice(-1)[0])); - const date = new Date(); - date.setMilliseconds(-(moment.duration(os.uptime(), 's').asMilliseconds())); - - const embed = new RichEmbed(); - embed.setTitle('System Information & Statistics'); - embed.addField('Hostname', os.hostname(), true); - embed.addField('Uptime', `${moment.duration(os.uptime(), 's').humanize()} | Last restart was on ${moment(date).format('dddd, MMMM Do YYYY, h:mm:ss A')} EST`, true); - embed.addField('CPU', `${os.cpus()[0].model} ${os.cpus()[0].speed / 1000}GHz | ${os.cpus().length} Cores | ${os.arch()}`, true); - embed.addField('Load Average (last 15 minutes)', os.loadavg()[2].toFixed(3), true); - embed.addField('Memory/RAM', `${usedMemory} / ${dataConversion(totalmem())}`, true); - embed.addField('Network Interfaces (IPv4)', os.networkInterfaces().eth0.filter((r) => r.family === 'IPv4')[0].address, true); - embed.addField('Network Interfaces (IPv6)', os.networkInterfaces().eth0.filter((r) => r.family === 'IPv6')[0].address.replace(/:/gi, '\:'), true); // eslint-disable-line - embed.setFooter(this.client.user.username, this.client.user.avatarURL); - embed.setTimestamp(); - // @ts-ignore - message.channel.createMessage({ embed }); - } -} +import moment from 'moment'; +import { Message } from 'eris'; +import os, { totalmem } from 'os'; +import { Command, RichEmbed } from '../class'; +import { dataConversion } from '../functions'; +import { Client } from '..'; + +export default class SysInfo extends Command { + constructor(client: Client) { + super(client); + this.name = 'sysinfo'; + this.description = 'Provides system information.'; + this.enabled = true; + } + + public async run(message: Message) { + const availableMemory: string = await this.client.util.exec('free -b'); + const usedMemory = dataConversion(totalmem() - Number(availableMemory.split('\n')[1].split(' ').slice(-1)[0])); + const date = new Date(); + date.setMilliseconds(-(moment.duration(os.uptime(), 's').asMilliseconds())); + + const embed = new RichEmbed(); + embed.setTitle('System Information & Statistics'); + embed.addField('Hostname', os.hostname(), true); + embed.addField('Uptime', `${moment.duration(os.uptime(), 's').humanize()} | Last restart was on ${moment(date).format('dddd, MMMM Do YYYY, h:mm:ss A')} EST`, true); + embed.addField('CPU', `${os.cpus()[0].model} ${os.cpus()[0].speed / 1000}GHz | ${os.cpus().length} Cores | ${os.arch()}`, true); + embed.addField('Load Average (last 15 minutes)', os.loadavg()[2].toFixed(3), true); + embed.addField('Memory/RAM', `${usedMemory} / ${dataConversion(totalmem())}`, true); + embed.addField('Network Interfaces (IPv4)', os.networkInterfaces().eth0.filter((r) => r.family === 'IPv4')[0].address, true); + embed.addField('Network Interfaces (IPv6)', os.networkInterfaces().eth0.filter((r) => r.family === 'IPv6')[0].address.replace(/:/gi, '\:'), true); // eslint-disable-line + embed.setFooter(this.client.user.username, this.client.user.avatarURL); + embed.setTimestamp(); + message.channel.createMessage({ embed }); + } +} diff --git a/src/commands/whois.ts b/src/commands/whois.ts index 58082cb..07f3004 100644 --- a/src/commands/whois.ts +++ b/src/commands/whois.ts @@ -1,64 +1,63 @@ -/* eslint-disable consistent-return */ -import moment from 'moment'; -import { Message } from 'eris'; -import { Client } from '..'; -import { Command, RichEmbed } from '../class'; -import { dataConversion } from '../functions'; -import User from './whois_user'; - -export default class Whois extends Command { - constructor(client: Client) { - super(client); - this.name = 'whois'; - this.description = 'Views information for a cloud account.'; - this.aliases = ['account', 'user']; - this.usage = `${this.client.config.prefix}account [User Name | User ID | Email Address]`; - this.permissions = { roles: ['446104438969466890'] }; - this.subcmds = [User]; - 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 account = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { userID: args[0] }, { emailAddress: args[0] }] }); - if (!account) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Account not found.***`); - const embed = new RichEmbed(); - embed.setTitle('Account Information'); - if (this.client.users.get(account.userID)) embed.setThumbnail(this.client.users.get(account.userID).avatarURL); - embed.setColor(0x36393f); - let fingerInformation: string; - const result = await this.client.util.exec(`finger ${account.username}`); - if (message.member && !message.member.roles.includes('143414786913206272')) { - fingerInformation = result.replace(/((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))/g, '[MASKED IP ADDRESS]'); - } else { - fingerInformation = result; - } - embed.setDescription(`${fingerInformation}\n${await this.client.util.exec(`chage -l ${account.username}`)}`); - embed.addField('Username', `${account.username} | <@${account.userID}>`, true); - embed.addField('ID', account.userID, true); - embed.addField('Email Address', account.emailAddress, true); - embed.addField('Created By', `<@${this.client.users.get(account.createdBy).id}>`, true); - embed.addField('Created At', moment(account.createdAt).format('dddd, MMMM Do YYYY, h:mm:ss A'), true); - const cpuUsage = await this.client.util.exec(`top -b -n 1 -u ${account.username} | awk 'NR>7 { sum += $9; } END { print sum; }'`); - embed.addField('CPU Usage', cpuUsage.split('\n')[0] ? `${cpuUsage.split('\n')[0]}%` : '0%', true); - embed.addField('Memory', dataConversion(Number(await this.client.util.exec(`memory ${account.username}`)) * 1000), true); - const data = await this.client.redis.get(`storage-${account.username}`) ? dataConversion(Number(await this.client.redis.get(`storage-${account.username}`))) : 'N/A'; - embed.addField('Storage', data, true); - let details = ''; - if (account.locked) details += 'This account is currently locked.\n'; - if (account.permissions.engineer) details += 'This account belongs to an Engineer.\n'; - else if (account.permissions.communityManager) details += 'This account belongs to a Community Manager.\n'; - else if (account.permissions.supervisor) details += 'This account belongs to a Supervisor.\n'; - else if (account.permissions.staff) details += 'This account belongs to a Staff member.\n'; - if (account.root) details += 'This account has root/administrative privileges.\n'; - if (details) embed.addField('Additional Details', details, true); - embed.setFooter(this.client.user.username, this.client.user.avatarURL); - embed.setTimestamp(); - // @ts-ignore - message.channel.createMessage({ embed }); - } catch (error) { - await this.client.util.handleError(error, message, this); - } - } -} +/* eslint-disable consistent-return */ +import moment from 'moment'; +import { Message } from 'eris'; +import { Client } from '..'; +import { Command, RichEmbed } from '../class'; +import { dataConversion } from '../functions'; +import User from './whois_user'; + +export default class Whois extends Command { + constructor(client: Client) { + super(client); + this.name = 'whois'; + this.description = 'Views information for a cloud account.'; + this.aliases = ['account', 'user']; + this.usage = `${this.client.config.prefix}account [User Name | User ID | Email Address]`; + this.permissions = { roles: ['446104438969466890'] }; + this.subcmds = [User]; + 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 account = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { userID: args[0] }, { emailAddress: args[0] }] }); + if (!account) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Account not found.***`); + const embed = new RichEmbed(); + embed.setTitle('Account Information'); + if (this.client.users.get(account.userID)) embed.setThumbnail(this.client.users.get(account.userID).avatarURL); + embed.setColor(0x36393f); + let fingerInformation: string; + const result = await this.client.util.exec(`finger ${account.username}`); + if (message.member && !message.member.roles.includes('143414786913206272')) { + fingerInformation = result.replace(/((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))/g, '[MASKED IP ADDRESS]'); + } else { + fingerInformation = result; + } + embed.setDescription(`${fingerInformation}\n${await this.client.util.exec(`chage -l ${account.username}`)}`); + embed.addField('Username', `${account.username} | <@${account.userID}>`, true); + embed.addField('ID', account.userID, true); + embed.addField('Email Address', account.emailAddress, true); + embed.addField('Created By', `<@${this.client.users.get(account.createdBy).id}>`, true); + embed.addField('Created At', moment(account.createdAt).format('dddd, MMMM Do YYYY, h:mm:ss A'), true); + const cpuUsage = await this.client.util.exec(`top -b -n 1 -u ${account.username} | awk 'NR>7 { sum += $9; } END { print sum; }'`); + embed.addField('CPU Usage', cpuUsage.split('\n')[0] ? `${cpuUsage.split('\n')[0]}%` : '0%', true); + embed.addField('Memory', dataConversion(Number(await this.client.util.exec(`memory ${account.username}`)) * 1000), true); + const data = await this.client.redis.get(`storage-${account.username}`) ? dataConversion(Number(await this.client.redis.get(`storage-${account.username}`))) : 'N/A'; + embed.addField('Storage', data, true); + let details = ''; + if (account.locked) details += 'This account is currently locked.\n'; + if (account.permissions.engineer) details += 'This account belongs to an Engineer.\n'; + else if (account.permissions.communityManager) details += 'This account belongs to a Community Manager.\n'; + else if (account.permissions.supervisor) details += 'This account belongs to a Supervisor.\n'; + else if (account.permissions.staff) details += 'This account belongs to a Staff member.\n'; + if (account.root) details += 'This account has root/administrative privileges.\n'; + if (details) embed.addField('Additional Details', details, true); + embed.setFooter(this.client.user.username, this.client.user.avatarURL); + embed.setTimestamp(); + message.channel.createMessage({ embed }); + } catch (error) { + await this.client.util.handleError(error, message, this); + } + } +} diff --git a/src/commands/whois_user.ts b/src/commands/whois_user.ts index a635a8c..cd152de 100644 --- a/src/commands/whois_user.ts +++ b/src/commands/whois_user.ts @@ -1,53 +1,52 @@ -/* eslint-disable consistent-return */ -import moment from 'moment'; -import { Message } from 'eris'; -import { Client } from '..'; -import { Command, RichEmbed } from '../class'; -import { dataConversion } from '../functions'; -import { AccountInterface } from '../models'; - -export default class Whois_User extends Command { - constructor(client: Client) { - super(client); - this.name = 'user'; - this.description = 'Gets information about your account.'; - this.usage = `${this.client.config.prefix}whois user `; - this.enabled = true; - } - - public async run(message: Message, args: string[]) { - try { - let account: AccountInterface; - if (!args[0]) account = await this.client.db.Account.findOne({ userID: message.author.id }); - else account = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { userID: args[0] }] }); - if (!account) return message.channel.createMessage(`***${this.client.stores.emojis.error} You don't have an account.***`); - const embed = new RichEmbed(); - embed.setTitle('Account Information'); - if (this.client.users.get(account.userID)) embed.setThumbnail(this.client.users.get(account.userID).avatarURL); - embed.setColor(0x36393f); - embed.addField('Username', `${account.username} | <@${account.userID}>`, true); - embed.addField('ID', account.userID, true); - embed.addField('Created By', `<@${this.client.users.get(account.createdBy).id}>`, true); - embed.addField('Created At', moment(account.createdAt).format('dddd, MMMM Do YYYY, h:mm:ss A'), true); - const cpuUsage = await this.client.util.exec(`top -b -n 1 -u ${account.username} | awk 'NR>7 { sum += $9; } END { print sum; }'`); - embed.addField('CPU Usage', cpuUsage.split('\n')[0] ? `${cpuUsage.split('\n')[0]}%` : '0%', true); - embed.addField('Memory', dataConversion(Number(await this.client.util.exec(`memory ${account.username}`)) * 1000), true); - const data = await this.client.redis.get(`storage-${account.username}`) ? dataConversion(Number(await this.client.redis.get(`storage-${account.username}`))) : 'N/A'; - embed.addField('Storage', data, true); - let details = ''; - if (account.locked) details += 'This account is currently locked.\n'; - if (account.permissions.engineer) details += 'This account belongs to an Engineer.\n'; - else if (account.permissions.communityManager) details += 'This account belongs to a Community Manager.\n'; - else if (account.permissions.supervisor) details += 'This account belongs to a Supervisor.\n'; - else if (account.permissions.staff) details += 'This account belongs to a Staff member.\n'; - if (account.root) details += 'This account has root/administrative privileges.\n'; - if (details) embed.addField('Additional Details', details, true); - embed.setFooter(this.client.user.username, this.client.user.avatarURL); - embed.setTimestamp(); - // @ts-ignore - message.channel.createMessage({ embed }); - } catch (error) { - this.client.util.handleError(error, message, this); - } - } -} +/* eslint-disable consistent-return */ +import moment from 'moment'; +import { Message } from 'eris'; +import { Client } from '..'; +import { Command, RichEmbed } from '../class'; +import { dataConversion } from '../functions'; +import { AccountInterface } from '../models'; + +export default class Whois_User extends Command { + constructor(client: Client) { + super(client); + this.name = 'user'; + this.description = 'Gets information about your account.'; + this.usage = `${this.client.config.prefix}whois user `; + this.enabled = true; + } + + public async run(message: Message, args: string[]) { + try { + let account: AccountInterface; + if (!args[0]) account = await this.client.db.Account.findOne({ userID: message.author.id }); + else account = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { userID: args[0] }] }); + if (!account) return message.channel.createMessage(`***${this.client.stores.emojis.error} You don't have an account.***`); + const embed = new RichEmbed(); + embed.setTitle('Account Information'); + if (this.client.users.get(account.userID)) embed.setThumbnail(this.client.users.get(account.userID).avatarURL); + embed.setColor(0x36393f); + embed.addField('Username', `${account.username} | <@${account.userID}>`, true); + embed.addField('ID', account.userID, true); + embed.addField('Created By', `<@${this.client.users.get(account.createdBy).id}>`, true); + embed.addField('Created At', moment(account.createdAt).format('dddd, MMMM Do YYYY, h:mm:ss A'), true); + const cpuUsage = await this.client.util.exec(`top -b -n 1 -u ${account.username} | awk 'NR>7 { sum += $9; } END { print sum; }'`); + embed.addField('CPU Usage', cpuUsage.split('\n')[0] ? `${cpuUsage.split('\n')[0]}%` : '0%', true); + embed.addField('Memory', dataConversion(Number(await this.client.util.exec(`memory ${account.username}`)) * 1000), true); + const data = await this.client.redis.get(`storage-${account.username}`) ? dataConversion(Number(await this.client.redis.get(`storage-${account.username}`))) : 'N/A'; + embed.addField('Storage', data, true); + let details = ''; + if (account.locked) details += 'This account is currently locked.\n'; + if (account.permissions.engineer) details += 'This account belongs to an Engineer.\n'; + else if (account.permissions.communityManager) details += 'This account belongs to a Community Manager.\n'; + else if (account.permissions.supervisor) details += 'This account belongs to a Supervisor.\n'; + else if (account.permissions.staff) details += 'This account belongs to a Staff member.\n'; + if (account.root) details += 'This account has root/administrative privileges.\n'; + if (details) embed.addField('Additional Details', details, true); + embed.setFooter(this.client.user.username, this.client.user.avatarURL); + embed.setTimestamp(); + message.channel.createMessage({ embed }); + } catch (error) { + this.client.util.handleError(error, message, this); + } + } +} diff --git a/src/index.ts b/src/index.ts index 4d8c716..3f299c9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ -export { default as Client } from './Client'; -export { default as config } from './config.json'; -export { default as Classes } from './class'; -export { default as Commands } from './commands'; -export { default as Events } from './events'; -export { default as Models } from './models'; -export { default as Stores } from './stores'; +export { default as Client } from './Client'; +export { default as config } from './config.json'; +export * from './class'; +export * from './commands'; +export * from './events'; +export * from './models'; +export * from './stores'; diff --git a/tsconfig.json b/tsconfig.json index d97cc52..04f4976 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,64 +1,64 @@ -{ - "compilerOptions": { - /* Basic Options */ - // "incremental": true, /* Enable incremental compilation */ - "target": "ES2017", /* 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. */ - // "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - "declaration": false, /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./dist", /* Redirect output structure to the directory. */ - "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - - /* Strict Type-Checking Options */ - "strict": false, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - // "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - - /* Module Resolution Options */ - "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - "resolveJsonModule": true, - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - } -} +{ + "compilerOptions": { + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "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. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": false, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./dist", /* Redirect output structure to the directory. */ + "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": false, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + "resolveJsonModule": true, + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + } +} diff --git a/types/moment.d.ts b/types/moment.d.ts new file mode 100644 index 0000000..a90caea --- /dev/null +++ b/types/moment.d.ts @@ -0,0 +1,15 @@ +import moment from 'moment'; + +declare module 'moment' { + interface PreciseRangeValueObject extends moment.MomentObjectOutput { + firstDateWasLater: boolean; + } + + interface Moment { + preciseDiff(d2: moment.MomentInput, returnValueObject?: false): string; + preciseDiff(d2: moment.MomentInput, returnValueObject: true): PreciseRangeValueObject; + } + + function preciseDiff(d1: moment.MomentInput, d2: moment.MomentInput, returnValueObject?: false): string; + function preciseDiff(d1: moment.MomentInput, d2: moment.MomentInput, returnValueObject: true): PreciseRangeValueObject; +}