merge-requests/1/merge
Bsian 2019-10-28 20:21:04 +00:00
parent 3942e96524
commit 246944c3ce
No known key found for this signature in database
GPG Key ID: 097FB9A291026091
22 changed files with 300 additions and 60 deletions

3
.gitignore vendored
View File

@ -1,4 +1,5 @@
node_modules node_modules
yarn.lock yarn.lock
src/config.json src/config.json
package-lock.json package-lock.json
htmlEmail_templates

View File

@ -3,10 +3,10 @@ import mongoose from 'mongoose';
import signale from 'signale'; import signale from 'signale';
import fs from 'fs-extra'; import fs from 'fs-extra';
import path from 'path'; import path from 'path';
import { config, Util } from '.'; import { config } from '.';
import { Account, AccountInterface, Moderation, ModerationInterface, Domain, DomainInterface } from './models'; import { Account, AccountInterface, Moderation, ModerationInterface, Domain, DomainInterface } from './models';
import { emojis } from './stores'; import { emojis } from './stores';
import { Command } from './class'; import { Command, Util } from './class';
export default class Client extends Eris.Client { export default class Client extends Eris.Client {
@ -27,6 +27,7 @@ export default class Client extends Eris.Client {
constructor() { constructor() {
super(config.token, { getAllUsers: true, restMode: true, defaultImageFormat: 'png' }); super(config.token, { getAllUsers: true, restMode: true, defaultImageFormat: 'png' });
process.title = 'cloudservices';
this.config = config; this.config = config;
this.util = new Util(this); this.util = new Util(this);
this.commands = new Map(); this.commands = new Map();
@ -39,13 +40,21 @@ export default class Client extends Eris.Client {
displayTimestamp: true, displayTimestamp: true,
displayFilename: true, displayFilename: true,
}); });
this.events();
this.loadFunctions(); this.loadFunctions();
this.init(); this.init();
} }
private async events() {
process.on('unhandledRejection', (error) => {
this.signale.error(error);
});
}
private async loadFunctions() { private async loadFunctions() {
const functions = await fs.readdir('./functions'); const functions = await fs.readdir('./functions');
functions.forEach(async (func) => { functions.forEach(async (func) => {
if (func === 'index.ts') return;
try { try {
require(`./functions/${func}`).default(this); require(`./functions/${func}`).default(this);
} catch (error) { } catch (error) {
@ -57,7 +66,8 @@ export default class Client extends Eris.Client {
public loadCommand(commandPath: string) { public loadCommand(commandPath: string) {
// eslint-disable-next-line no-useless-catch // eslint-disable-next-line no-useless-catch
try { try {
const command = new (require(commandPath))(this); // eslint-disable-next-line
const command = new (require(commandPath).default)(this);
this.commands.set(command.name, command); this.commands.set(command.name, command);
this.signale.complete(`Loaded command ${command.name}`); this.signale.complete(`Loaded command ${command.name}`);
} catch (err) { throw err; } } catch (err) { throw err; }
@ -67,17 +77,24 @@ export default class Client extends Eris.Client {
const evtFiles = await fs.readdir('./events/'); const evtFiles = await fs.readdir('./events/');
const commands = await fs.readdir(path.join(__dirname, './commands/')); const commands = await fs.readdir(path.join(__dirname, './commands/'));
commands.forEach((command) => { commands.forEach((command) => {
if (command === 'index.js') return;
this.loadCommand(`./commands/${command}`); this.loadCommand(`./commands/${command}`);
}); });
evtFiles.forEach((file) => { evtFiles.forEach((file) => {
const eventName = file.split('.')[0]; const eventName = file.split('.')[0];
const event = new (require(`./events/${file}`))(this); if (file === 'index.js') return;
// eslint-disable-next-line
const event = new (require(`./events/${file}`).default)(this);
this.signale.complete(`Loaded event ${eventName}`); this.signale.complete(`Loaded event ${eventName}`);
this.on(eventName, (...args) => event.run(...args)); this.on(eventName, (...args) => event.run(...args));
delete require.cache[require.resolve(`./events/${file}`)]; delete require.cache[require.resolve(`./events/${file}`)];
}); });
this.connect(); await mongoose.connect(config.mongoURL, { useNewUrlParser: true, useUnifiedTopology: true });
await this.connect();
} }
} }
// eslint-disable-next-line
new Client();

View File

@ -28,5 +28,6 @@ export default class Command {
this.aliases = []; this.aliases = [];
this.guildOnly = true; this.guildOnly = true;
this.client = client; this.client = client;
this.permissions = {};
} }
} }

View File

@ -33,16 +33,24 @@ export default class RichEmbed {
thumbnail?: { url?: string, proxy_url?: string, height?: number, width?: number }, video?: { url?: string, height?: number, width?: number }, thumbnail?: { url?: string, proxy_url?: string, height?: number, width?: number }, video?: { url?: string, height?: number, width?: number },
provider?: { name?: string, url?: string}, author?: { name?: string, url?: string, proxy_icon_url?: string, icon_url?: string}, provider?: { name?: string, url?: string}, author?: { name?: string, url?: string, proxy_icon_url?: string, icon_url?: string},
} = {}) { } = {}) {
/*
let types: { let types: {
title?: string, type?: string, description?: string, url?: string, timestamp?: Date, color?: number, fields?: {name: string, value: string, inline?: boolean}[] title?: string, type?: string, description?: string, url?: string, timestamp?: Date, color?: number, fields?: {name: string, value: string, inline?: boolean}[]
footer?: { text: string, icon_url?: string, proxy_icon_url?: string}, image?: { url?: string, proxy_url?: string, height?: number, width?: number }, footer?: { text: string, icon_url?: string, proxy_icon_url?: string}, image?: { url?: string, proxy_url?: string, height?: number, width?: number },
thumbnail?: { url?: string, proxy_url?: string, height?: number, width?: number }, video?: { url?: string, height?: number, width?: number }, thumbnail?: { url?: string, proxy_url?: string, height?: number, width?: number }, video?: { url?: string, height?: number, width?: number },
provider?: { name?: string, url?: string}, author?: { name?: string, url?: string, proxy_icon_url?: string, icon_url?: string} provider?: { name?: string, url?: string}, author?: { name?: string, url?: string, proxy_icon_url?: string, icon_url?: string}
}; };
this.fields = []; */
for (const key of Object.keys(types)) { this.title = data.title;
if (data[key]) this[key] = data[key]; this.description = data.description;
} this.url = data.url;
this.color = data.color;
this.author = data.author;
this.timestamp = data.timestamp;
this.fields = data.fields || [];
this.thumbnail = data.thumbnail;
this.image = data.image;
this.footer = data.footer;
} }
/** /**
@ -97,10 +105,8 @@ export default class RichEmbed {
*/ */
setAuthor(name: string, icon_url?: string, url?: string) { setAuthor(name: string, icon_url?: string, url?: string) {
if (typeof name !== 'string') throw new TypeError('RichEmbed Author names must be a string.'); if (typeof name !== 'string') throw new TypeError('RichEmbed Author names must be a string.');
if (typeof url !== 'string') throw new TypeError('RichEmbed Author URLs must be a string.'); if (url && typeof url !== 'string') throw new TypeError('RichEmbed Author URLs must be a string.');
if (typeof icon_url !== 'string') throw new TypeError('RichEmbed Author icons must be a string.'); if (icon_url && typeof icon_url !== 'string') throw new TypeError('RichEmbed Author icons must be a string.');
if (!url.startsWith('http://') || !url.startsWith('https://')) url = `https://${url}`;
if (!icon_url.startsWith('http://') || !icon_url.startsWith('https://')) icon_url = `https://${icon_url}`;
this.author = { name, icon_url, url }; this.author = { name, icon_url, url };
return this; return this;
} }
@ -164,7 +170,6 @@ export default class RichEmbed {
setFooter(text: string, icon_url?: string) { setFooter(text: string, icon_url?: string) {
if (typeof text !== 'string') throw new TypeError('RichEmbed Footers must be a string.'); if (typeof text !== 'string') throw new TypeError('RichEmbed Footers must be a string.');
if (icon_url && typeof icon_url !== 'string') throw new TypeError('RichEmbed Footer icon URLs must be a string.'); if (icon_url && typeof icon_url !== 'string') throw new TypeError('RichEmbed Footer icon URLs must be a string.');
if (!icon_url.startsWith('http://') || !icon_url.startsWith('https://')) icon_url = `https://${icon_url}`;
if (text.length > 2048) throw new RangeError('RichEmbed footer text may not exceed 2048 characters.'); if (text.length > 2048) throw new RangeError('RichEmbed footer text may not exceed 2048 characters.');
this.footer = { text, icon_url }; this.footer = { text, icon_url };
return this; return this;

View File

@ -2,10 +2,10 @@
import { promisify, isArray } from 'util'; import { promisify, isArray } from 'util';
import childProcess from 'child_process'; import childProcess from 'child_process';
import nodemailer from 'nodemailer'; import nodemailer from 'nodemailer';
import { Message, TextChannel, PrivateChannel } from 'eris'; import { Message, PrivateChannel } from 'eris';
import { outputFile } from 'fs-extra'; import { outputFile } from 'fs-extra';
import { Client } from '.'; import { Client } from '..';
import { Command, RichEmbed } from './class'; import { Command, RichEmbed } from '.';
export default class Util { export default class Util {
public client: Client; public client: Client;
@ -45,6 +45,8 @@ export default class Util {
} }
public async handleError(error: Error, message?: Message, command?: Command): Promise<void> { public async handleError(error: Error, message?: Message, command?: Command): Promise<void> {
this.client.signale.error(error);
/*
const info = { content: `\`\`\`js\n${error.stack}\n\`\`\``, embed: null }; const info = { content: `\`\`\`js\n${error.stack}\n\`\`\``, embed: null };
if (message) { if (message) {
const embed = new RichEmbed(); const embed = new RichEmbed();
@ -65,6 +67,7 @@ export default class Util {
if (message) this.client.createMessage('595788220764127272', 'Message content for above error'); if (message) this.client.createMessage('595788220764127272', 'Message content for above error');
if (command) this.client.commands.get(command.name).enabled = false; if (command) this.client.commands.get(command.name).enabled = false;
if (message) message.channel.createMessage(`***${this.client.stores.emojis.error} An unexpected error has occured - please contact a member of the Engineering Team.${command ? ' This command has been disabled.' : ''}***`); if (message) message.channel.createMessage(`***${this.client.stores.emojis.error} An unexpected error has occured - please contact a member of the Engineering Team.${command ? ' This command has been disabled.' : ''}***`);
*/
} }
public splitFields(fields: {name: string, value: string, inline?: boolean}[]): {name: string, value: string, inline?: boolean}[][] { public splitFields(fields: {name: string, value: string, inline?: boolean}[]): {name: string, value: string, inline?: boolean}[][] {
@ -94,6 +97,7 @@ export default class Util {
return arrayString; return arrayString;
} }
public async createHash(password: string) { public async createHash(password: string) {
const hashed = await this.exec(`mkpasswd -m sha-512 "${password}"`); const hashed = await this.exec(`mkpasswd -m sha-512 "${password}"`);
return hashed; return hashed;
@ -128,14 +132,14 @@ export default class Util {
await this.client.db.Account.deleteOne({ username }); await this.client.db.Account.deleteOne({ username });
} }
public async messageCollector(message: Message, question: string, timeout: number, shouldDelete = false, choices: string[] = null, filter = (msg: Message): boolean|void => {}): Promise<string> { public async messageCollector(message: Message, question: string, timeout: number, shouldDelete = false, choices: string[] = null, filter = (msg: Message): boolean|void => {}): Promise<Message> {
const msg = await message.channel.createMessage(question); const msg = await message.channel.createMessage(question);
return new Promise((res, rej) => { return new Promise((res, rej) => {
setTimeout(() => { if (shouldDelete) msg.delete(); rej(new Error('Did not supply a valid input in time')); }, timeout); setTimeout(() => { if (shouldDelete) msg.delete(); rej(new Error('Did not supply a valid input in time')); }, timeout);
this.client.on('messageCreate', (Msg) => { this.client.on('messageCreate', (Msg) => {
if (filter(Msg) === false) return; if (filter(Msg) === false) return;
const verif = choices ? choices.includes(Msg.content) : Msg.content; const verif = choices ? choices.includes(Msg.content) : Msg.content;
if (verif) { if (shouldDelete) msg.delete(); res(Msg.content); } if (verif) { if (shouldDelete) msg.delete(); res(Msg); }
}); });
}); });
} }

View File

@ -1,5 +1,5 @@
import { Message } from 'eris'; import { Message } from 'eris';
import { Client, config } from '..'; import { Client } from '..';
import { Command } from '../class'; import { Command } from '../class';
export default class Announce extends Command { export default class Announce extends Command {
@ -7,7 +7,7 @@ export default class Announce extends Command {
super(client); super(client);
this.name = 'announce'; this.name = 'announce';
this.description = 'Sends an announcement to all active terminals'; this.description = 'Sends an announcement to all active terminals';
this.usage = `${config.prefix}announce Hi there! | ${config.prefix}announce -e EMERGENCY!`; this.usage = `${this.client.config.prefix}announce Hi there! | ${this.client.config.prefix}announce -e EMERGENCY!`;
this.aliases = ['ann']; this.aliases = ['ann'];
this.permissions = { roles: ['608095934399643649', '521312697896271873'] }; this.permissions = { roles: ['608095934399643649', '521312697896271873'] };
this.enabled = true; this.enabled = true;
@ -17,8 +17,8 @@ export default class Announce extends Command {
try { try {
if (!args.length) return this.client.commands.get('help').run(message, [this.name]); 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 message.channel.createMessage(`${this.client.stores.emojis.loading} ***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(' ')}\n\n\n\n\n\n\n\n\n\n\n\n\n" | wall -n`); 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(' ')}" | 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(); message.delete();
return notification.edit(`${this.client.stores.emojis.success} ***Sent${args[0] === '-e' ? ' emergency' : ''} announcement to all active terminals***`); return notification.edit(`${this.client.stores.emojis.success} ***Sent${args[0] === '-e' ? ' emergency' : ''} announcement to all active terminals***`);
} catch (error) { } catch (error) {

View File

@ -59,7 +59,64 @@ export default class CreateAccount extends Command {
embed.setTimestamp(); embed.setTimestamp();
// @ts-ignore // @ts-ignore
this.client.createMessage('580950455581147146', { embed }); this.client.createMessage('580950455581147146', { embed });
return confirmation.edit(`${this.client.stores.emojis.success} ***Account successfully created***\n**Username:** \`${args[2]}\`\n**Temporary Password:** \`${tempPass}\``);
this.client.util.transport.sendMail({
to: args[1],
from: 'Library of Code sp-us | Cloud Services <support@libraryofcode.org>',
subject: 'Your account has been created',
html: `
<body>
<style>* {font-family: 'Calibri';}</style>
<h1>Library of Code | Cloud Services</h1>
<h2>Your Cloud Account has been created, welcome! Please see below for some details regarding your account and our services</h2>
<p><b>Username:</b> ${args[2]}</p>
<p><b>SSH Login:</b> <pre><code style="font-family: Courier;">ssh ${args[2]}@cloud.libraryofcode.org</code></pre>
<p><b>Email address (see below for further information):</b> ${args[2]}@cloud.libraryofcode.org</p>
<h2>Useful information</h2>
<h3>How to log in:</h3>
<ol>
<li>Open your desired terminal application - we recommend using <a target="_blank" href="https://www.gnu.org/software/bash/">Bash</a>, but you can use your computer's default</li>
<li>Type in your SSH Login as above</li>
<li>When prompted, enter your password <em>Please note that inputs will be blank, so be careful not to type in your password incorrectly</em></li>
</ol>
<p>If you fail to authenticate yourself too many times, you will be IP banned and will fail to connect. If this is the case, feel free to DM Ramirez with your <a target="_blank" href="https://whatismyip.com">public IPv4 address</a>.
<h3>Setting up your cloud email</h3>
<p>All email applications are different, so here are some information you can use to connect your email</p>
<ul>
<li><b>Server:</b> cloud.libraryofcode.org</li>
<li><b>Account username/password:</b> Normal login</li>
<li><b>Account type (incoming):</b> IMAP</li>
<li><b>Incoming port:</b> 143 (993 if you're using TLS security type)</li>
<li><b>Incoming Security Type:</b> STARTTLS (TLS if you're using port 993)</li>
<li><b>Outgoing port:</b> 587 (If that doesn't work, try 25)</li>
<li><b>Outgoing Security Type:</b> STARTTLS</li>
</ul>
<h3>Channels and Links</h3>
<ul>
<li><a target="_blank" href="https://discordapp.com/channels/446067825673633794/622856541325885453">#status</a> - You can find the status of all our services, including the cloud machine, here</li>
<li><a target="_blank" href="https://discordapp.com/channels/446067825673633794/620355063088414769">#cloud-announcements</a> - Announcements regarding the cloud machine will be here. These include planned maintenance, updates to preinstalled services etc.</li>
<li><a target="_blank" href="https://discordapp.com/channels/446067825673633794/620349128295186472">#cloud-info</a> - Important information you will need to, or should, know to a certain extent. These include our infractions system and Tier limits</li>
<li><a target="_blank" href="https://discordapp.com/channels/446067825673633794/546457788184789013">#cloud-support</a> - A support channel specifically for the cloud machine, you can use this to ask how to renew your certificates, for example</li>
<li><a target="_blank" href="https://support.libraryofcode.us">Library of Code Support Desk</a> - Our Support desk, you will find some handy info there</li>
<li><a target="_blank" href="https://www.securesign.org">SecureSign</a> - our certificates manager</li>
</ul>
<h3>Want to support us?</h3>
<p>You can support us on Patreon! Head to <a target="_blank" href="https://www.patreon.com/libraryofcode">our Patreon page</a> and feel free to donate as much or as little as you want!<br>Donating $5 or more will grant you Tier 3, which means we will manage your account at your request, longer certificates, increased Tier limits as well as some roles in the server!</p>
<b><i>Library of Code sp-us | Support Team</i></b>
</body>
`,
});
const dmChannel = await this.client.getDMChannel(args[0]).catch();
dmChannel.createMessage('<:loc:607695848612167700> **Thank you for creating an account with us!** <:loc:607695848612167700>\n'
+ `Please log into your account by running \`ssh ${args[2]}@cloud.libraryofcode.us\` in your terminal, then use the password \`${tempPass}\` to log in.\n`
+ `You will be asked to change your password, \`(current) UNIX password\` is \`${tempPass}\`, then create a password that is at least 12 characters long, with at least one number, special character, and an uppercase letter\n`
+ 'Bear in mind that when you enter your password, it will be blank, so be careful not to type in your password incorrectly.\n'
+ 'You may now return to Modmail, and continue setting up your account from there.\n\n'
+ 'An email containing some useful information has also been sent').catch();
return confirmation.edit(`${this.client.stores.emojis.success} ***Account successfully created***\n**Username:** \`${args[3]}\`\n**Temporary Password:** \`${tempPass}\``);
} catch (error) { } catch (error) {
return this.client.util.handleError(error, message, this); return this.client.util.handleError(error, message, this);
} }

View File

@ -16,6 +16,7 @@ export default class CWG extends Command {
} }
public async run(message: Message, args?: string[]) { public async run(message: Message, args?: string[]) {
if (!args.length) return this.client.commands.get('help').run(message, [this.name]);
/* /*
args[1] should be the user's ID OR account username; required args[1] should be the user's ID OR account username; required
args[2] should be the domain; required args[2] should be the domain; required

View File

@ -0,0 +1,79 @@
import { Message, PrivateChannel } from 'eris';
import uuid from 'uuid/v4';
import { Command, RichEmbed } from '../class';
import { Client, config } from '..';
export default class DeleteAccount extends Command {
constructor(client: Client) {
super(client);
this.name = 'deleteaccount';
this.description = 'Delete an account on the Cloud VM';
this.usage = `${config.prefix}deleteaccount [User ID] [Reason] | ${config.prefix}deleteaccount [Username] [Reason] | ${config.prefix}deleteaccount [Email] [Reason]`;
this.aliases = ['deleteacc', 'dacc', 'daccount', 'delete'];
this.permissions = { roles: ['475817826251440128', '525441307037007902'] };
this.enabled = true;
}
public async run(message: Message, args: string[]) {
try {
if (!args[1]) 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 { root, username, userID, emailAddress } = account;
if (root) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Permission denied***`);
const pad = (number: number, amount: number): string => '0'.repeat(amount - number.toString().length) + number;
const randomNumber = Math.floor(Math.random() * 9999);
const verify = pad(randomNumber, 4);
try {
await this.client.util.messageCollector(message,
`***Please confirm that you are permanently deleting ${username}'s account by entering ${verify}. This action cannot be reversed.***`,
15000, true, [verify], (msg) => !(message.channel instanceof PrivateChannel && msg.author.id === message.author.id));
} catch (error) {
if (error.message.includes('Did not supply')) return message;
throw error;
}
const deleting = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Deleteing account, please wait...***`);
await this.client.util.deleteAccount(username);
const reason = args.slice(1).join(' ');
const logInput = { username, userID, logID: uuid(), moderatorID: message.author.id, type: 4, date: new Date(), reason: null };
if (reason) logInput.reason = reason;
const log = await new this.client.db.Moderation(logInput);
await log.save();
const embed = new RichEmbed();
embed.setTitle('Cloud Account | Delete');
embed.setColor('ff0000');
embed.addField('User', `${username} | <@${userID}>`);
embed.addField('Engineer', `<@${message.author.id}>`, true);
if (reason) embed.addField('Reason', reason);
embed.setFooter(this.client.user.username, this.client.user.avatarURL);
embed.setTimestamp();
// @ts-ignore
this.client.createMessage('580950455581147146', { embed });
this.client.getDMChannel(userID).then((user) => {
// @ts-ignore
user.createMessage({ embed }).catch();
});
this.client.util.transport.sendMail({
to: account.emailAddress,
from: 'Library of Code sp-us | Cloud Services <support@libraryofcode.org>',
subject: 'Your account has been deleted',
html: `
<h1>Library of Code | Cloud Services</h1>
<p>Your Cloud Account has been deleted by our Engineers. There is no way to recover your files and this desicion cannot be appealed. We're sorry to see you go.</p>
<p><b>Reason:</b> ${reason}</p>
<p><b>Engineer:</b> ${message.author.username}</p>
<b><i>Library of Code sp-us | Support Team</i></b>
`,
});
return deleting.edit(`${this.client.stores.emojis.success} ***Account ${username} has been deleted by Engineer ${message.author.username}#${message.author.discriminator}***`);
} catch (error) {
return this.client.util.handleError(error, message, this);
}
}
}

View File

@ -2,7 +2,7 @@
import { Message } from 'eris'; import { Message } from 'eris';
import { inspect } from 'util'; import { inspect } from 'util';
import axios from 'axios'; import axios from 'axios';
import { Client, config } from '..'; import { Client } from '..';
import { Command } from '../class'; import { Command } from '../class';
export default class Eval extends Command { export default class Eval extends Command {
@ -15,26 +15,32 @@ export default class Eval extends Command {
this.permissions = { users: ['253600545972027394', '278620217221971968'] }; this.permissions = { users: ['253600545972027394', '278620217221971968'] };
} }
public async run(message: Message) { public async run(message: Message, args: string[]) {
try { try {
const evalMessage = message.content.slice(config.prefix.length).split(' ').slice(1).join(' '); // const evalMessage = message.content.slice(this.client.config.prefix.length).split(' ').slice(1).join(' ');
let evaled: any; let evaled: any;
let output: string;
try { try {
evaled = await eval(evalMessage); evaled = await eval(args.join(' ').trim());
if (typeof evaled !== 'string') output = output && inspect(evaled, { depth: 1 }); if (typeof evaled !== 'string') {
evaled = inspect(evaled, { depth: 0 });
}
if (evaled === undefined) {
evaled = 'undefined';
}
} catch (error) { } catch (error) {
output = error.stack; evaled = error.stack;
} }
/*
if (output) { if (output) {
output = output.replace(RegExp(config.prefix, 'gi'), 'juul'); output = output.replace(RegExp(this.client.config.prefix, 'gi'), 'juul');
output = output.replace(RegExp(config.emailPass, 'gi'), 'juul'); output = output.replace(RegExp(this.client.config.emailPass, 'gi'), 'juul');
output = output.replace(RegExp(config.cloudflare, 'gi'), 'juul'); output = output.replace(RegExp(this.client.config.cloudflare, 'gi'), 'juul');
} }
*/
const display = this.client.util.splitString(output, 1975); const display = this.client.util.splitString(evaled, 1975);
if (display[5]) { if (display[5]) {
try { try {
const { data } = await axios.post('https://snippets.cloud.libraryofcode.org/documents', display.join('')); const { data } = await axios.post('https://snippets.cloud.libraryofcode.org/documents', display.join(''));

View File

@ -28,7 +28,7 @@ export default class Help extends Command {
let allowedUsers = c.permissions && c.permissions.users && c.permissions.users.map((u) => `<@${u}>`).join(', '); let allowedUsers = c.permissions && c.permissions.users && c.permissions.users.map((u) => `<@${u}>`).join(', ');
if (allowedUsers) { allowedUsers = `**Users:** ${allowedUsers}`; perms.push(allowedUsers); } if (allowedUsers) { allowedUsers = `**Users:** ${allowedUsers}`; perms.push(allowedUsers); }
const displayedPerms = perms.length ? `**Permissions:**\n${perms.join('\n')}` : ''; 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: true }; 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 splitCommands = this.client.util.splitFields(commands);

View File

@ -16,22 +16,26 @@ export default class Lock extends Command {
public async run(message: Message, args: string[]) { // eslint-disable-line public async run(message: Message, args: string[]) { // eslint-disable-line
try { try {
const account = await this.client.db.Account.findOne({ $or: [{ account: args[0] }, { userID: args[0].replace(/[<@!>]/gi, '') }] }); 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) 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.***`); 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...***`); 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.***`); 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 this.client.util.exec(`lock ${account.username}`);
await account.update({ locked: true }); await account.updateOne({ locked: true });
const expiry = new Date(); const expiry = new Date();
const lockLength = args[1].match(/[a-z]+|[^a-z]+/gi); const lockLength = args[1].match(/[a-z]+|[^a-z]+/gi);
// @ts-ignore // @ts-ignore
const momentMilliseconds = moment.duration(Number(lockLength[0]), lockLength[1]).asMilliseconds; const momentMilliseconds = moment.duration(Number(lockLength[0]), lockLength[1]).asMilliseconds();
expiry.setMilliseconds(momentMilliseconds); expiry.setMilliseconds(momentMilliseconds);
let processed: boolean = false; let processed: boolean = false;
if (!momentMilliseconds) processed = true; if (!momentMilliseconds) processed = true;
this.client.signale.debug(lockLength);
this.client.signale.debug(expiry);
this.client.signale.debug(momentMilliseconds);
const moderation = new this.client.db.Moderation({ const moderation = new this.client.db.Moderation({
username: account.username, username: account.username,
userID: account.userID, userID: account.userID,
@ -41,7 +45,7 @@ export default class Lock extends Command {
type: 2, type: 2,
date: new Date(), date: new Date(),
expiration: { expiration: {
expirationDate: momentMilliseconds ? expiry : null, date: momentMilliseconds ? expiry : null,
processed, processed,
}, },
}); });
@ -53,8 +57,10 @@ export default class Lock extends Command {
embed.addField('User', `${account.username} | <@${account.userID}>`, true); embed.addField('User', `${account.username} | <@${account.userID}>`, true);
embed.addField('Supervisor', `<@${message.author.id}>`, true); embed.addField('Supervisor', `<@${message.author.id}>`, true);
if ((momentMilliseconds ? args.slice(2).join(' ') : args.slice(1).join(' ')).length > 0) embed.addField('Reason', momentMilliseconds ? args.slice(2).join(' ') : args.slice(1).join(' '), true); if ((momentMilliseconds ? args.slice(2).join(' ') : args.slice(1).join(' ')).length > 0) embed.addField('Reason', momentMilliseconds ? args.slice(2).join(' ') : args.slice(1).join(' '), true);
embed.addField('Lock Expiration', `${momentMilliseconds ? moment(expiry).format('dddd, MMMM Do YYYY, h:mm:ss A') : 'Indefinitely'}`, true);
embed.setFooter(this.client.user.username, this.client.user.avatarURL); embed.setFooter(this.client.user.username, this.client.user.avatarURL);
embed.setTimestamp(); embed.setTimestamp();
message.delete();
this.client.getDMChannel(account.userID).then((user) => { this.client.getDMChannel(account.userID).then((user) => {
// @ts-ignore // @ts-ignore
user.createMessage({ embed }).catch(); user.createMessage({ embed }).catch();

View File

@ -16,6 +16,7 @@ export default class Modlogs extends Command {
public async run(message: Message, args: string[]) { public async run(message: Message, args: string[]) {
try { 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 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, '') }] }); 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(' ')}***`); if (!query.length) return msg.edit(`***${this.client.stores.emojis.error} Cannot locate modlogs for ${args.join(' ')}***`);
@ -45,7 +46,7 @@ export default class Modlogs extends Command {
const embeds = logs.map((l) => { const embeds = logs.map((l) => {
const embed = new RichEmbed(); const embed = new RichEmbed();
embed.setDescription(`List of Cloud moderation logs for ${users.join(', ')}`); embed.setDescription(`List of Cloud moderation logs for ${users.join(', ')}`);
embed.setAuthor('Library of Code | Cloud Services', this.client.user.avatarURL, 'https://libraryofcode.us'); embed.setAuthor('Library of Code | Cloud Services', this.client.user.avatarURL, 'https://libraryofcode.org/');
embed.setTitle('Cloud Modlogs/Infractions'); embed.setTitle('Cloud Modlogs/Infractions');
embed.setFooter(`Requested by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL); embed.setFooter(`Requested by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL);
l.forEach((f) => embed.addField(f.name, f.value, f.inline)); l.forEach((f) => embed.addField(f.name, f.value, f.inline));
@ -54,7 +55,12 @@ export default class Modlogs extends Command {
return embed; return embed;
}); });
createPaginationEmbed(message, this.client, embeds, {}, msg); if (embeds.length === 1) {
// @ts-ignore
message.channel.createMessage({ embed: embeds[0] });
} else {
createPaginationEmbed(message, this.client, embeds, {}, msg);
}
return msg; return msg;
} catch (error) { } catch (error) {
return this.client.util.handleError(error, message, this); return this.client.util.handleError(error, message, this);

36
src/commands/sysinfo.ts Normal file
View File

@ -0,0 +1,36 @@
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 });
}
}

View File

@ -14,12 +14,14 @@ export default class Unlock extends Command {
public async run(message: Message, args: string[]) { // eslint-disable-line public async run(message: Message, args: string[]) { // eslint-disable-line
try { try {
const account = await this.client.db.Account.findOne({ $or: [{ account: args[0] }, { userID: args[0].replace(/[<@!>]/gi, '') }] }); 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) 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.***`); 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...***`); const edit = await message.channel.createMessage(`***${this.client.stores.emojis.loading} Unlocking account...***`);
if (account.username === 'matthew' || account.root) return edit.edit(`***${this.client.stores.emojis.error} Permission denied.***`); 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 this.client.util.exec(`unlock ${account.username}`);
await account.updateOne({ locked: false });
const moderation = new this.client.db.Moderation({ const moderation = new this.client.db.Moderation({
username: account.username, username: account.username,
@ -33,13 +35,14 @@ export default class Unlock extends Command {
await moderation.save(); await moderation.save();
edit.edit(`***${this.client.stores.emojis.success} Account ${account.username} has been unlocked by Supervisor ${message.author.username}#${message.author.discriminator}.***`); edit.edit(`***${this.client.stores.emojis.success} Account ${account.username} has been unlocked by Supervisor ${message.author.username}#${message.author.discriminator}.***`);
const embed = new RichEmbed(); const embed = new RichEmbed();
embed.setTitle('Account Infraction | UnLock'); embed.setTitle('Account Infraction | Unlock');
embed.setColor(15158332); embed.setColor(15158332);
embed.addField('User', `${account.username} | <@${account.userID}>`, true); embed.addField('User', `${account.username} | <@${account.userID}>`, true);
embed.addField('Supervisor', `<@${message.author.id}>`, true); embed.addField('Supervisor', `<@${message.author.id}>`, true);
if (args.slice(1).join(' ').length > 0) embed.addField('Reason', args.slice(1).join(' '), true); if (args.slice(1).join(' ').length > 0) embed.addField('Reason', args.slice(1).join(' '), true);
embed.setFooter(this.client.user.username, this.client.user.avatarURL); embed.setFooter(this.client.user.username, this.client.user.avatarURL);
embed.setTimestamp(); embed.setTimestamp();
message.delete();
this.client.getDMChannel(account.userID).then((user) => { this.client.getDMChannel(account.userID).then((user) => {
// @ts-ignore // @ts-ignore
user.createMessage({ embed }).catch(); user.createMessage({ embed }).catch();

View File

@ -1,9 +1,7 @@
import { Message, TextChannel } from 'eris'; import { Message, TextChannel } from 'eris';
import { Client, config } from '..'; import { Client } from '..';
import Command from '../class/Command'; import Command from '../class/Command';
const { prefix } = config;
export default class { export default class {
public client: Client public client: Client
@ -13,18 +11,30 @@ export default class {
public async run(message: Message) { public async run(message: Message) {
try { try {
const noPrefix: string[] = message.content.slice(prefix.length).trim().split(/ +/g); if (message.author.bot) return;
if (message.content.indexOf(this.client.config.prefix) !== 0) return;
const noPrefix: string[] = message.content.slice(this.client.config.prefix.length).trim().split(/ +/g);
const command: string = noPrefix[0].toLowerCase(); const command: string = noPrefix[0].toLowerCase();
const resolved: Command = this.client.util.resolveCommand(command); const resolved: Command = this.client.util.resolveCommand(command);
if (!resolved) return; if (!resolved) return;
if (resolved.guildOnly && !(message.channel instanceof TextChannel)) return; if (resolved.guildOnly && !(message.channel instanceof TextChannel)) return;
const hasUserPerms: boolean = resolved.permissions.users.includes(message.author.id); let hasUserPerms: boolean;
if (resolved.permissions.users) {
hasUserPerms = resolved.permissions.users.includes(message.author.id);
}
let hasRolePerms: boolean = false; let hasRolePerms: boolean = false;
for (const role of resolved.permissions.roles) { if (resolved.permissions.roles) {
if (message.member && message.member.roles.includes(role)) { for (const role of resolved.permissions.roles) {
hasRolePerms = true; break; if (message.member && message.member.roles.includes(role)) {
// this.client.signale.debug(message.member.roles.includes(role));
hasRolePerms = true; break;
}
} }
} }
if (!resolved.permissions.users && !resolved.permissions.roles) {
hasUserPerms = true;
hasRolePerms = true;
}
if (!hasRolePerms && !hasUserPerms) return; if (!hasRolePerms && !hasUserPerms) return;
if (!resolved.enabled) { message.channel.createMessage(`***${this.client.stores.emojis.error} This command has been disabled***`); return; } if (!resolved.enabled) { message.channel.createMessage(`***${this.client.stores.emojis.error} This command has been disabled***`); return; }
const args: string[] = noPrefix.slice(1); const args: string[] = noPrefix.slice(1);

View File

@ -3,7 +3,7 @@ import { Client } from '..';
import { RichEmbed } from '../class'; import { RichEmbed } from '../class';
export default function checkLock(client: Client) { export default function checkLock(client: Client) {
setTimeout(async () => { setInterval(async () => {
try { try {
const moderations = await client.db.Moderation.find(); const moderations = await client.db.Moderation.find();
moderations.forEach(async (moderation) => { moderations.forEach(async (moderation) => {
@ -13,7 +13,8 @@ export default function checkLock(client: Client) {
const account = await client.db.Account.findOne({ username: moderation.username }); const account = await client.db.Account.findOne({ username: moderation.username });
if (!account) return; if (!account) return;
await client.util.exec(`unlock ${account.username}`); await client.util.exec(`unlock ${account.username}`);
await moderation.update({ 'expiration.processed': true }); await moderation.updateOne({ 'expiration.processed': true });
await account.updateOne({ locked: false });
const mod = new client.db.Moderation({ const mod = new client.db.Moderation({
username: account.username, username: account.username,
userID: account.userID, userID: account.userID,
@ -38,10 +39,11 @@ export default function checkLock(client: Client) {
}); });
// @ts-ignore // @ts-ignore
client.createMessage('580950455581147146', { embed }); client.createMessage('580950455581147146', { embed });
client.signale.complete(`Unlocked account ${account.username} | Queue date at ${moderation.expiration.date.toLocaleString('en-us')}`);
} }
}); });
} catch (error) { } catch (error) {
await client.util.handleError(error); await client.util.handleError(error);
} }
}); }, 10000);
} }

View File

@ -0,0 +1,5 @@
export default function dataConversion(bytes: number) {
const i = Math.floor(Math.log(bytes) / Math.log(1024));
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
return `${(bytes / 1024 ** i).toFixed(2)} ${sizes[i]}`;
}

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

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

View File

@ -5,4 +5,3 @@ export { default as Commands } from './commands';
export { default as Events } from './events'; export { default as Events } from './events';
export { default as Models } from './models'; export { default as Models } from './models';
export { default as Stores } from './stores'; export { default as Stores } from './stores';
export { default as Util } from './Util';

View File

@ -25,10 +25,10 @@ export interface ModerationInterface extends Document {
const Moderation: Schema = new Schema({ const Moderation: Schema = new Schema({
username: String, username: String,
userID: String, userID: String,
logID: Number, logID: String,
moderatorID: String, moderatorID: String,
reason: String, reason: String,
type: String, type: Number,
date: Date, date: Date,
expiration: { expiration: {
date: Date, date: Date,

View File

@ -1,5 +1,5 @@
export default { export default {
success: '<:modSuccess:578750988907970567>', success: '<:modSuccess:578750988907970567>',
loading: '<a:modloading:588607353935364106>', loading: '<a:modloading:588607353935364106>',
error: '<:modError:578750737920688128', error: '<:modError:578750737920688128>',
}; };