diff --git a/src/commands/announce.ts b/src/commands/announce.ts index a8f92c1..47bc21f 100644 --- a/src/commands/announce.ts +++ b/src/commands/announce.ts @@ -16,7 +16,7 @@ export default class Announce extends Command { public async run(message: Message, args?: string[]) { try { if (!args.length) return this.client.commands.get('help').run(message, [this.name]); - const notification = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Sending announcement, please wait...***`); + const notification = await this.loading(message.channel, 'Sending announcement, please wait...'); if (args[0] === '-e') await this.client.util.exec(`echo "\n\n**************************************************************************\nEMERGENCY SYSTEM BROADCAST MESSAGE | Library of Code sp-us (root enforced)\n--------------------------------------------------------------------------\n\n\n${args.slice(1).join(' ').trim()}\n\n\n\n\n\n\n\n\n\n\n\n\n" | wall -n`); else await this.client.util.exec(`echo "\nSYSTEM BROADCAST MESSAGE | Library of Code sp-us (root enforced)\n\n\n${args.join(' ').trim()}" | wall -n`); message.delete(); diff --git a/src/commands/bearer.ts b/src/commands/bearer.ts index 74d9cf8..19751c9 100644 --- a/src/commands/bearer.ts +++ b/src/commands/bearer.ts @@ -15,12 +15,12 @@ export default class Bearer extends Command { public async run(message: Message) { 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) return this.error(message.channel, 'Account not found.'); // eslint-disable-next-line no-underscore-dangle const bearer = await this.client.server.security.createBearer(account._id); const dm = await this.client.getDMChannel(message.author.id); const msg = await dm.createMessage(`__**Library of Code sp-us | Cloud Services [API]**__\n*This message will automatically be deleted in 60 seconds, copy the token and save it. You cannot recover it.*\n\n${bearer}`); - message.channel.createMessage(`***${this.client.stores.emojis.success} Bearer token sent to direct messages.***`); + this.error(message.channel, 'Bearer token sent to direct messages.'); return setTimeout(() => { msg.delete(); }, 60000); diff --git a/src/commands/cloudflare.ts b/src/commands/cloudflare.ts index 35bc18d..f912733 100644 --- a/src/commands/cloudflare.ts +++ b/src/commands/cloudflare.ts @@ -18,13 +18,13 @@ export default class Cloudflare extends Command { public async run(message: Message, args: string[]) { try { if (!args.length) return this.client.commands.get('help').run(message, ['cloudflare']); - const msg = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Locating entry...***`); + const msg = await this.loading(message.channel, 'Locating entry...'); const { data } = await axios({ method: 'get', url: `https://api.cloudflare.com/client/v4/zones/5e82fc3111ed4fbf9f58caa34f7553a7/dns_records?name=${args[0]}`, headers: { Authorization: `Bearer ${this.client.config.cloudflare}` }, }); - if (!data.result.length) return msg.edit(`${this.client.stores.emojis.error} ***Entry not found***`); + if (!data.result.length) return msg.edit(`${this.client.stores.emojis.error} ***Entry not found.***`); msg.edit(`${this.client.stores.emojis.success} ***Located entry***\n${this.client.stores.emojis.loading} ***Deleting entry...***`); const { id }: { id: string } = data.result[0]; await axios({ diff --git a/src/commands/createaccount.ts b/src/commands/createaccount.ts index d9047b0..d3bba47 100644 --- a/src/commands/createaccount.ts +++ b/src/commands/createaccount.ts @@ -23,19 +23,19 @@ export default class CreateAccount extends Command { try { if (message.channel instanceof PrivateChannel || message.channel instanceof GroupChannel) return message; // Stop TS being gay if (!args[2]) return this.client.commands.get('help').run(message, [this.name]); - if (!message.channel.guild.members.has(args[0])) return message.channel.createMessage(`${this.client.stores.emojis.error} ***User not found.***`); - if (message.channel.guild.members.get(args[0]).bot) return message.channel.createMessage(`${this.client.stores.emojis.error} ***I cannot create accounts for bots.***`); + if (!message.channel.guild.members.has(args[0])) return this.error(message.channel, 'User not found.'); + if (message.channel.guild.members.get(args[0]).bot) return this.error(message.channel, 'I cannot create accounts for bots.'); const checkUser = await this.client.db.Account.findOne({ userID: args[0] }); - if (checkUser) return message.channel.createMessage(`${this.client.stores.emojis.error} ***<@${args[0]}> already has an account.***`); + if (checkUser) return this.error(message.channel, `<@${args[0]}> already has an account.`); const checkEmail = await this.client.db.Account.findOne({ emailAddress: args[1] }); - if (checkEmail) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Account already exists with this email address.***`); + if (checkEmail) return this.error(message.channel, 'Account already exists with this email address.'); const checkAccount = await this.client.db.Account.findOne({ username: args[2] }); - if (checkAccount) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Account already exists with this username.***`); + if (checkAccount) return this.error(message.channel, 'Account already exists with this username.'); - if (!this.client.util.isValidEmail(args[1])) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Invalid email address supplied.***`); - if (!/^[a-z][-a-z0-9]*$/.test(args[2])) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Invalid username supplied.***`); + if (!this.client.util.isValidEmail(args[1])) return this.error(message.channel, 'Invalid email address supplied.'); + if (!/^[a-z][-a-z0-9]*$/.test(args[2])) return this.error(message.channel, 'Invalid username supplied.'); - const confirmation = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Creating account...***`); + const confirmation = await this.loading(message.channel, 'Creating account...'); const data = await this.client.util.accounts.createAccount({ userID: args[0], username: args[2], emailAddress: args[1] }, message.author.id); message.delete(); diff --git a/src/commands/cwg_create.ts b/src/commands/cwg_create.ts index 10fff12..ffaa006 100644 --- a/src/commands/cwg_create.ts +++ b/src/commands/cwg_create.ts @@ -32,14 +32,14 @@ export default class CWG_Create extends Command { try { if (!args[2]) return this.client.commands.get('help').run(message, ['cwg', this.name]); - if (!this.urlRegex.test(args[1])) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Invalid URL***`); - if (Number(args[2]) <= 1024 || Number(args[2]) >= 65535) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Port must be greater than 1024 and less than 65535***`); - if (!args[1].endsWith('.cloud.libraryofcode.org') && !args[4]) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Certificate Chain and Private Key are required for custom domains***`); + if (!this.urlRegex.test(args[1])) return this.error(message.channel, 'Invalid URL supplied.'); + if (Number(args[2]) <= 1024 || Number(args[2]) >= 65535) return this.error(message.channel, 'Port must be greater than 1024 and less than 65535.'); + if (!args[1].endsWith('.cloud.libraryofcode.org') && !args[4]) return this.error(message.channel, 'Certificate Chain and Private Key are required for custom domains.'); const 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} Cannot locate account, please try again.`); + if (!account) return this.error(message.channel, 'Cannot locate account.'); - if (await this.client.db.Domain.exists({ domain: args[1] })) return message.channel.createMessage(`${this.client.stores.emojis.error} ***This domain already exists***`); + if (await this.client.db.Domain.exists({ domain: args[1] })) return this.error(message.channel, 'This domain already exists.'); if (await this.client.db.Domain.exists({ port: Number(args[2]) })) { let answer: Message; @@ -50,24 +50,24 @@ export default class CWG_Create extends Command { 30000, true, ['y', 'n'], (msg) => msg.author.id === message.author.id && msg.channel.id === message.channel.id, ); } catch (error) { - return message.channel.createMessage(`${this.client.stores.emojis.error} ***Bind request cancelled***`); + return this.error(message.channel, 'Bind request cancelled.'); } - if (answer.content === 'n') return message.channel.createMessage(`${this.client.stores.emojis.error} ***Bind request cancelled***`); + if (answer.content === 'n') return this.error(message.channel, 'Bind request cancelled.'); } - const edit = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Binding domain...***`); + const edit = await this.loading(message.channel, 'Binding domain...'); let certs: { cert?: string, key?: string } = {}; if (!args[1].endsWith('.cloud.libraryofcode.org')) { const urls = args.slice(3, 5); - if (urls.some((l) => !l.includes('snippets.cloud.libraryofcode.org/raw/'))) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Invalid snippets URL***`); + if (urls.some((l) => !l.includes('snippets.cloud.libraryofcode.org/raw/'))) return this.error(message.channel, 'Invalid snippets URL. Make sure to use https://snippets.libraryofcode.org/raw/*.'); const tasks = urls.map((l) => axios({ method: 'GET', url: l })); const response = await Promise.all(tasks); const certAndPrivateKey: string[] = response.map((r) => r.data); - if (!this.isValidCertificateChain(certAndPrivateKey[0])) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Invalid Certificate Chain***`); - if (!this.isValidPrivateKey(certAndPrivateKey[1])) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Invalid Private Key***`); + if (!this.isValidCertificateChain(certAndPrivateKey[0])) return this.error(message.channel, 'The certificate chain provided is invalid.'); + if (!this.isValidPrivateKey(certAndPrivateKey[1])) return this.error(message.channel, 'The private key provided is invalid.'); certs = { cert: certAndPrivateKey[0], key: certAndPrivateKey[1] }; } else { diff --git a/src/commands/cwg_data.ts b/src/commands/cwg_data.ts index 19e9d35..cfae12c 100644 --- a/src/commands/cwg_data.ts +++ b/src/commands/cwg_data.ts @@ -24,12 +24,12 @@ export default class CWG_Data extends Command { 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.***`); + return this.error(message.channel, '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 this.error(message.channel, '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.***`); + return this.error(message.channel, 'The domain or port you provided could not be found.'); } const embeds = dom.map((domain) => { const cert = fs.readFileSync(domain.x509.cert, { encoding: 'utf8' }); diff --git a/src/commands/cwg_delete.ts b/src/commands/cwg_delete.ts index 38dd1e2..ab6af76 100644 --- a/src/commands/cwg_delete.ts +++ b/src/commands/cwg_delete.ts @@ -19,8 +19,8 @@ export default class CWG_Delete extends Command { 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]) || 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...***`); + if (!domain) return this.error(message.channel, 'The domain or port you provided could not be found.'); + const edit = await this.loading(message.channel, 'Deleting domain...'); const embed = new RichEmbed(); embed.setTitle('Domain Deletion'); embed.addField('Account Username', domain.account.username, true); diff --git a/src/commands/cwg_updatecert.ts b/src/commands/cwg_updatecert.ts index 5e837e8..2fbc230 100644 --- a/src/commands/cwg_updatecert.ts +++ b/src/commands/cwg_updatecert.ts @@ -19,18 +19,18 @@ export default class CWG_UpdateCert extends Command { try { if (!args[2]) return this.client.commands.get('help').run(message, ['cwg', this.name]); const dom = await this.client.db.Domain.findOne({ $or: [{ domain: args[0] }, { port: Number(args[0]) || 0 }] }); - if (!dom) return message.channel.createMessage(`***${this.client.stores.emojis.error} The domain or port you provided could not be found.***`); + if (!dom) return this.error(message.channel, 'The domain or port you provided could not be found.'); const { domain, port, x509, account } = dom; const { cert, key } = x509; const urls = args.slice(1, 3); // eslint-disable-line - if (urls.some((l) => !l.includes('snippets.cloud.libraryofcode.org/raw/'))) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Invalid snippets URL***`); + if (urls.some((l) => !l.includes('snippets.cloud.libraryofcode.org/raw/'))) return this.error(message.channel, 'Invalid snippets URL.'); const tasks = urls.map((l) => axios({ method: 'GET', url: l })); const response = await Promise.all(tasks); const certAndPrivateKey: string[] = response.map((r) => r.data); - if (!this.isValidCertificateChain(certAndPrivateKey[0])) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Invalid Certificate Chain***`); - if (!this.isValidPrivateKey(certAndPrivateKey[1])) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Invalid Private Key***`); + if (!this.isValidCertificateChain(certAndPrivateKey[0])) return this.error(message.channel, 'The certificate chain provided is invalid.'); + if (!this.isValidPrivateKey(certAndPrivateKey[1])) return this.error(message.channel, 'The private key provided is invalid.'); const path = `/opt/CloudServices/temp/${account.id}`; const temp = [writeFile(`${path}.chain.crt`, certAndPrivateKey[0]), writeFile(`${path}.key.pem`, certAndPrivateKey[1])]; @@ -38,13 +38,13 @@ export default class CWG_UpdateCert extends Command { await Promise.all(temp); if (!this.isMatchingPair(`${path}.chain.crt`, `${path}.key.pem`)) { await Promise.all(removeFiles); - return message.channel.createMessage(`${this.client.stores.emojis.error} ***Certificate and private key do not match***`); + return this.error(message.channel, 'The certificate and private key provided do not match'); } const writeTasks = [writeFile(cert, certAndPrivateKey[0], { encoding: 'utf8' }), writeFile(key, certAndPrivateKey[1], { encoding: 'utf8' }), ...removeFiles]; await Promise.all(writeTasks); - return message.channel.createMessage(`${this.client.stores.emojis.success} ***Updated certificate for ${domain} on port ${port}***`); + return this.success(message.channel, `Updated certificate for ${domain} on port ${port}`); } catch (error) { return this.client.util.handleError(error, message, this); } diff --git a/src/commands/disk.ts b/src/commands/disk.ts deleted file mode 100644 index 867a3e9..0000000 --- a/src/commands/disk.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Message } from 'eris'; -import moment from 'moment'; -import { Client } from '..'; -import { RichEmbed, Command } from '../class'; -import { dataConversion } from '../functions'; -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: ['662163685439045632', '701454780828221450'] }; - 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/emailcode.ts b/src/commands/emailcode.ts index 19e4ff8..6ba4475 100644 --- a/src/commands/emailcode.ts +++ b/src/commands/emailcode.ts @@ -18,7 +18,7 @@ export default class EmailCode extends Command { try { if (!args.length) return this.client.commands.get('help').run(message, [this.name]); const code = randomBytes(5).toString('hex'); - if (!this.client.util.isValidEmail(args[0])) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Invalid email address supplied.***`); + if (!this.client.util.isValidEmail(args[0])) return this.error(message.channel, 'The email address provided is invalid.'); this.client.util.transport.sendMail({ to: args[0], from: 'Library of Code sp-us | Cloud Services ', @@ -35,7 +35,7 @@ export default class EmailCode extends Command { `, }); - return message.channel.createMessage(`***${this.client.stores.emojis.success} Code: \`${code}\` | Email Address: ${args[0]}***`); + return this.success(message.channel, `Code: \`${code}\` | Email Address: ${args[0]}`); } catch (error) { return this.client.util.handleError(error, message, this); } diff --git a/src/commands/exec.ts b/src/commands/exec.ts index c4363c6..2dc185a 100644 --- a/src/commands/exec.ts +++ b/src/commands/exec.ts @@ -18,7 +18,7 @@ export default class Exec extends Command { try { if (!args.length) return this.client.commands.get('help').run(message, [this.name]); - const response = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Executing \`${args.join(' ')}\`***`); + const response = await this.loading(message.channel, `***Executing \`${args.join(' ')}\``); let result: string; try { result = await this.client.util.exec(args.join(' '), { cwd: '/opt/CloudServices' }); diff --git a/src/commands/limits_setramnotification.ts b/src/commands/limits_setramnotification.ts index 4ecf5be..bc8e18f 100644 --- a/src/commands/limits_setramnotification.ts +++ b/src/commands/limits_setramnotification.ts @@ -14,16 +14,16 @@ export default class Limits_SetRAMNotification extends Command { 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} You do not have a Cloud Account.***`); + if (!account) return this.error(message.channel, 'You do not appear to have an account.'); const tier = await this.client.db.Tier.findOne({ id: account.tier }); - if (Number(args[0]) >= tier.resourceLimits.ram) return message.channel.createMessage(`***${this.client.stores.emojis.error} You cannot set your notification limit to be set to or above your hard limit.***`); - if (Number(args[0]) < 0) return message.channel.createMessage(`***${this.client.stores.emojis.error} You cannot set your notification limit to a negative number.***`); + if (Number(args[0]) >= tier.resourceLimits.ram) return this.error(message.channel, 'You cannot set your notification limit to be set to or above your hard limit.'); + if (Number(args[0]) < 0) return this.error(message.channel, 'You cannot set your notification limit to a negative number.'); if (Number(args[0]) === 0) { await account.updateOne({ $set: { ramLimitNotification: 0 } }); - return message.channel.createMessage(`***${this.client.stores.emojis.success} You have disabled notifications.***`); + return this.success(message.channel, 'You have disabled notifications.'); } await account.updateOne({ $set: { ramLimitNotification: Number(args[0]) } }); - return message.channel.createMessage(`***${this.client.stores.emojis.success} You will now receive notifications when you go above ${Number(args[0])} MB.***`); + return this.success(message.channel, `You will now receive notifications when you go above ${Number(args[0])} MB.`); } catch (error) { return this.client.util.handleError(error, message, this); } diff --git a/src/commands/load.ts b/src/commands/load.ts index 2e44703..98d4d0f 100644 --- a/src/commands/load.ts +++ b/src/commands/load.ts @@ -17,7 +17,7 @@ export default class Load extends Command { if (!args[0]) return this.client.commands.get('help').run(message, [this.name]); const allowed = ['config', 'util', 'command']; const type = args[0].toLowerCase(); - if (!allowed.includes(type)) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Invalid type to (re)load***`); + if (!allowed.includes(type)) return this.error(message.channel, 'Invalid type provided to (re)load.'); const corepath = '/opt/CloudServices/dist'; if (type === 'config') { @@ -33,20 +33,20 @@ export default class Load extends Command { delete require.cache[`${corepath}/commands/${args[1]}.js`]; Object.keys(require.cache).filter((path) => path.includes(`${args[1]}_`)).forEach((path) => delete require.cache[path]); const cmdIndex = require('.'); - let Cmd = cmdIndex[args[1]]; - if (!Cmd) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Could not find file***`); - Cmd = require(`${corepath}/commands/${args[1]}`).default; + let cmd = cmdIndex[args[1]]; + if (!cmd) return this.error(message.channel, 'Could not find file.'); + cmd = require(`${corepath}/commands/${args[1]}`).default; this.client.commands.remove(args[1]); - this.client.loadCommand(Cmd); + this.client.loadCommand(cmd); delete require.cache[`${corepath}/commands/index.js`]; delete require.cache[`${corepath}/commands/${args[1]}.js`]; Object.keys(require.cache).filter((path) => path.includes(`${args[1]}_`)).forEach((path) => delete require.cache[path]); } catch (error) { - if (error.message.includes('Cannot find module')) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Could not find file***`); + if (error.message.includes('Cannot find module')) return this.error(message.channel, 'Could not find file.'); throw error; } } - return message.channel.createMessage(`${this.client.stores.emojis.success} Reloaded ${type}`); + return this.success(message.channel, `Reloaded ${type}.`); } catch (error) { return this.client.util.handleError(error, message, this); } diff --git a/src/commands/lock.ts b/src/commands/lock.ts index 7769fab..10ccc77 100644 --- a/src/commands/lock.ts +++ b/src/commands/lock.ts @@ -16,9 +16,9 @@ export default class Lock extends Command { 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) return this.error(message.channel, 'Cannot find user.'); + if (account.locked) return this.error(message.channel, 'This account is already locked.'); + const edit = await this.loading(message.channel, 'Locking account...'); if (account.username === 'matthew' || account.root) return edit.edit(`***${this.client.stores.emojis.error} Permission denied.***`); await this.client.util.sendMessageToUserTerminal(account.username, 'ACCOUNT LOCKED BY TECHNICIAN | PREPARING TO LOGOUT').catch(() => { }); await this.client.util.exec(`lock ${account.username}`); diff --git a/src/commands/modlogs.ts b/src/commands/modlogs.ts index 73ab718..bc13075 100644 --- a/src/commands/modlogs.ts +++ b/src/commands/modlogs.ts @@ -16,7 +16,7 @@ export default class Modlogs extends Command { 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 msg: Message = await this.loading(message.channel, '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(' ')}***`); diff --git a/src/commands/notify.ts b/src/commands/notify.ts index 2a365e3..002c86b 100644 --- a/src/commands/notify.ts +++ b/src/commands/notify.ts @@ -15,7 +15,7 @@ export default class Notify extends Command { 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 edit = await this.loading(message.channel, '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() diff --git a/src/commands/parse.ts b/src/commands/parse.ts deleted file mode 100644 index b7df469..0000000 --- a/src/commands/parse.ts +++ /dev/null @@ -1,70 +0,0 @@ -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'] }; - this.enabled = false; - } - - 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 deleted file mode 100644 index d4ecc18..0000000 --- a/src/commands/parseall.ts +++ /dev/null @@ -1,73 +0,0 @@ -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']; - this.enabled = false; - } - - 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/pull.ts b/src/commands/pull.ts index a0991b1..557b54f 100644 --- a/src/commands/pull.ts +++ b/src/commands/pull.ts @@ -15,9 +15,9 @@ export default class Pull extends Command { public async run(message: Message, args: string[]) { try { - if (this.client.updating) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Update in progress***`); + if (this.client.updating) return this.error(message.channel, 'An update is already in progress.'); this.client.updating = true; - const updateMessage = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Fetching latest commit...***`); + const updateMessage = await this.loading(message.channel, 'Fetching latest commit...'); let pull: string; try { diff --git a/src/commands/reload.ts b/src/commands/reload.ts deleted file mode 100644 index 84a4ba4..0000000 --- a/src/commands/reload.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* eslint-disable consistent-return */ -import { Message } from 'eris'; -import { Command } from '../class'; -import { Client } from '..'; - -export default class Reload extends Command { - constructor(client: Client) { - super(client); - this.name = 'reload'; - this.description = 'Reloads a command.'; - this.usage = `${this.client.config.prefix}reload [command name]`; - this.permissions = { roles: ['525441307037007902'] }; - this.enabled = true; - } - - public run(message: Message, args: string[]) { - if (!args.length) return this.client.commands.get('help').run(message, [this.name]); - } -} diff --git a/src/commands/resetpassword.ts b/src/commands/resetpassword.ts index e122f42..ea48bfc 100644 --- a/src/commands/resetpassword.ts +++ b/src/commands/resetpassword.ts @@ -18,10 +18,10 @@ export default class ResetPassword extends Command { 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) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Permission denied***`); + if (!account) return this.error(message.channel, 'Account not found.'); + if (account.root) return this.error(message.channel, 'Permission denied.'); - const msg = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Resetting password for ${account.username}...***`); + const msg = await this.loading(message.channel, `Resetting password for ${account.username}...`); const tempPass = this.client.util.randomPassword(); await this.client.util.exec(`echo '${account.username}:${tempPass}' | chpasswd && chage -d0 ${account.username}`); diff --git a/src/commands/restart.ts b/src/commands/restart.ts index b110322..31847d1 100644 --- a/src/commands/restart.ts +++ b/src/commands/restart.ts @@ -13,9 +13,9 @@ export default class Restart extends Command { public async run(message: Message, args: string[]) { try { - if (this.client.updating && args[0] !== '-f') return message.channel.createMessage(`${this.client.stores.emojis.error} ***Update in progress***`); - if (this.client.buildError) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Build error, resolve before restarting. See CI job on Gitlab***`); - await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Restarting...***`); + if (this.client.updating && args[0] !== '-f') return this.error(message.channel, 'Update in process.'); + if (this.client.buildError) return this.error(message.channel, 'Build error detected, please resolve before continuing. See CI job on GitLab.'); + await this.loading(message.channel, 'Restarting...'); return process.exit(1); } catch (error) { return this.client.util.handleError(error, message, this); diff --git a/src/commands/setlimit_ram.ts b/src/commands/setlimit_ram.ts index 10f47c2..537f193 100644 --- a/src/commands/setlimit_ram.ts +++ b/src/commands/setlimit_ram.ts @@ -16,10 +16,10 @@ export default class SetLimit_RAM extends Command { try { if (!args[0]) return this.client.commands.get('help').run(message, ['setlimit', this.name]); const tier = await this.client.db.Tier.findOne({ id: Number(args[0]) }); - if (!tier) return message.channel.createMessage(`***${this.client.stores.emojis.error} Cannot locate that tier.***`); - if (Number.isNaN(Number(args[1]))) return message.channel.createMessage(`***${this.client.stores.emojis.error} This is not a valid number.***`); + if (!tier) return this.error(message.channel, 'The tier you provided doesn\'t appear to exist.'); + if (Number.isNaN(Number(args[1]))) return this.error(message.channel, 'Invalid number in limit argument.'); await tier.updateOne({ 'resourceLimits.ram': Number(args[1]) }); - return message.channel.createMessage(`***${this.client.stores.emojis.success} Tier ${tier.id} RAM resource limit set to ${args[1]} MB.***`); + return this.success(message.channel, `Tier ${tier.id} RAM resource limit set to ${args[1]} MB.`); } catch (error) { return this.client.util.handleError(error, message, this); } diff --git a/src/commands/sysinfo.ts b/src/commands/sysinfo.ts index 6dc5ce9..bf79eee 100644 --- a/src/commands/sysinfo.ts +++ b/src/commands/sysinfo.ts @@ -27,7 +27,7 @@ export default class SysInfo extends Command { 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().enp0s3.filter((r) => r.family === 'IPv4')[0].address, true); - embed.addField('Network Interfaces (IPv6)', os.networkInterfaces().enp0s3.filter((r) => r.family === 'IPv6')[0].address.replace(/:/gi, '\:'), true); // eslint-disable-line + // embed.addField('Network Interfaces (IPv6)', os.networkInterfaces().enp0s3.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/tier.ts b/src/commands/tier.ts index 6c14d6d..c6cf932 100644 --- a/src/commands/tier.ts +++ b/src/commands/tier.ts @@ -15,7 +15,7 @@ export default class Tier extends Command { 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} Editing tier...***`); + const edit = await this.loading(message.channel, 'Editing tier...'); 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.***`); if (account.root) return edit.edit(`***${this.client.stores.emojis.error} Permission denied.***`); diff --git a/src/commands/unlock.ts b/src/commands/unlock.ts index 66cb00f..e068f92 100644 --- a/src/commands/unlock.ts +++ b/src/commands/unlock.ts @@ -15,9 +15,9 @@ export default class Unlock extends Command { 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 unlocked.***`); - const edit = await message.channel.createMessage(`***${this.client.stores.emojis.loading} Unlocking account...***`); + if (!account) return this.error(message.channel, 'Cannot find user.'); + if (!account.locked) return this.error(message.channel, 'This account is already unlocked.'); + const edit = await this.loading(message.channel, 'Unlocking account...'); if (account.username === 'matthew' || account.root) return edit.edit(`***${this.client.stores.emojis.error} Permission denied.***`); await this.client.util.exec(`unlock ${account.username}`); await account.updateOne({ locked: false }); diff --git a/src/commands/warn.ts b/src/commands/warn.ts index 3e2c9b4..7e421f9 100644 --- a/src/commands/warn.ts +++ b/src/commands/warn.ts @@ -15,7 +15,7 @@ export default class Warn extends Command { 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 warning...***`); + const edit = await this.loading(message.channel, 'Processing warning...'); 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.***`); if (account.root) return edit.edit(`***${this.client.stores.emojis.error} Permission denied.***`); diff --git a/src/commands/whois.ts b/src/commands/whois.ts index 4a589a6..25a1de7 100644 --- a/src/commands/whois.ts +++ b/src/commands/whois.ts @@ -28,7 +28,7 @@ export default class Whois extends Command { const user = args[0] || message.author.id; if (full) account = await this.client.db.Account.findOne({ $or: [{ username: user }, { userID: user }, { emailAddress: user }, { supportKey: user.toUpperCase() }] }); else account = await this.client.db.Account.findOne({ $or: [{ username: user }, { userID: user }] }); - if (!account) return message.channel.createMessage(`***${this.client.stores.emojis.error} Account not found.***`); + if (!account) return this.error(message.channel, 'Account not found.'); const thumbnail = this.client.users.get(account.userID)?.avatarURL || message.channel.guild.iconURL;