1
0
Fork 0

Merge branch 'eirk/cloudservices-master'

master
Hiroyuki 2021-09-10 19:05:44 -04:00
commit b40b39ca12
No known key found for this signature in database
GPG Key ID: AF65958B7B7362E6
71 changed files with 2231 additions and 2042 deletions

61
.gitignore vendored
View File

@ -1,10 +1,57 @@
node_modules
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Serverless directories
.serverless/
# macOS files
.DS_Store
*.DS_Store
# Database files
*.sqlite
# IDE and text editor configuration files
.vscode/
.idea/
# other files
dist/
src/config.json
package-lock.json
htmlEmail_templates
yarn-error.log
src/keys.json
dist
htmlEmail_templates
securesign_genrsa.ts
.idea
.vscode

10
.vscode/settings.json vendored
View File

@ -1,10 +0,0 @@
{
"eslint.enable": true,
"eslint.validate": [
{
"language": "typescript",
"autoFix": true
}
],
"editor.tabSize": 2
}

View File

@ -629,7 +629,7 @@ to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Cloud Services
Cloud Services
Copyright (C) 2019 Library of Code sp-us Engineering Team
This program is free software: you can redistribute it and/or modify

View File

@ -7,7 +7,7 @@ get_user_by_uid_files := $(wildcard src/go/getUserByUid/*.go)
all: check_certificate check_cert_signatures storage getUserByUid typescript
check_certificate:
check_certificate:
HOME=/root go build -ldflags="-s -w" -o dist/bin/checkCertificate ${check_certificate_files}
@chmod 740 dist/bin/checkCertificate
@file dist/bin/checkCertificate

View File

@ -1,7 +1,7 @@
{
"name": "cloudservices-rewrite",
"version": "1.2.0",
"description": "The official LOC Cloud Services system, this is a rewrite of the original version. ",
"version": "2.0",
"description": "The official LOC Cloud Services system, this is a rewrite the original version, using discord.js.",
"main": "dist/Client.js",
"scripts": {
"lint": "eslint ./ --ext ts --fix",
@ -12,40 +12,37 @@
"license": "AGPL-3.0-only",
"private": false,
"dependencies": {
"axios": "^0.19.0",
"axios": "^0.21.1",
"body-parser": "^1.19.0",
"eris": "bsian03/eris#dev",
"eris-pagination": "git+https://github.com/bsian03/eris-pagination#c0f77b118e98309e89e6522ef545f9d121601f21",
"discord.js": "^13.0.0",
"express": "^4.17.1",
"fs-extra": "^8.1.0",
"fs-extra": "^10.0.0",
"hastebin-gen": "^2.0.5",
"helmet": "^3.21.2",
"ioredis": "^4.14.1",
"helmet": "^4.6.0",
"ioredis": "^4.27.7",
"jsonwebtoken": "^8.5.1",
"moment": "^2.27.0",
"moment-precise-range-plugin": "^1.3.0",
"mongoose": "^5.7.4",
"nodemailer": "^6.3.1",
"moment": "^2.29.1",
"mongoose": "^5.13.5",
"nodemailer": "^6.6.3",
"signale": "^1.4.0",
"uuid": "^3.3.3"
"uuid": "^8.3.2"
},
"devDependencies": {
"@types/express": "^4.17.6",
"@types/express-serve-static-core": "^4.17.5",
"@types/fs-extra": "^8.0.0",
"@types/helmet": "^0.0.45",
"@types/ioredis": "^4.0.18",
"@types/jsonwebtoken": "^8.5.0",
"@types/mongoose": "^5.7.14",
"@types/nodemailer": "^6.2.1",
"@types/signale": "^1.2.1",
"@types/uuid": "^3.4.5",
"@typescript-eslint/eslint-plugin": "2.31.0",
"@typescript-eslint/parser": "2.31.0",
"eslint": "^6.5.1",
"eslint-config-airbnb-base": "^14.0.0",
"eslint-plugin-import": "^2.18.2",
"madge": "^3.9.2",
"typescript": "^3.6.4"
"@types/express": "^4.17.13",
"@types/express-serve-static-core": "^4.17.24",
"@types/fs-extra": "^9.0.12",
"@types/ioredis": "^4.26.7",
"@types/jsonwebtoken": "^8.5.4",
"@types/node": "^16.4.13",
"@types/nodemailer": "^6.4.4",
"@types/signale": "^1.4.2",
"@types/uuid": "^8.3.1",
"@typescript-eslint/eslint-plugin": "^4.29.0",
"@typescript-eslint/parser": "^4.29.0",
"eslint": "^7.32.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-plugin-import": "^2.23.4",
"madge": "^5.0.1",
"typescript": "^4.3.5"
}
}

View File

@ -1,8 +1,8 @@
import os from 'os';
import jwt from 'jsonwebtoken';
import { TextChannel } from 'eris';
import { TextChannel, MessageEmbed } from 'discord.js';
import { Server } from '..';
import { RichEmbed, Route } from '../../class';
import { Route } from '../../class';
export default class Root extends Route {
constructor(server: Server) {
@ -59,18 +59,18 @@ export default class Root extends Route {
const token = <any> jwt.verify(req.query.t.toString(), this.server.client.config.keyPair.privateKey);
const check = await this.server.storage.get<boolean>(req.query.t.toString());
if (check) return res.sendStatus(401);
const embed = new RichEmbed();
const embed = new MessageEmbed();
embed.setTitle('Referral Authorization');
embed.setDescription(req.query.t.toString());
embed.addField('Referred User', `${token.referredUserAndDiscrim} | ${token.referredUserID}`, true);
embed.addField('Referrer User', token.referrerUsername, true);
embed.addField('Referral Code', token.referralCode, true);
embed.setTimestamp();
embed.setFooter(this.server.client.user.username, this.server.client.user.avatarURL);
const channel = <TextChannel> this.server.client.guilds.get('446067825673633794').channels.get('580950455581147146');
embed.setFooter(this.server.client.user.username, this.server.client.user.avatarURL());
const channel = this.server.client.guilds.cache.get('446067825673633794').channels.cache.get('580950455581147146') as TextChannel;
res.sendStatus(200);
await this.server.storage.set(req.query.t.toString(), true);
return channel.createMessage({ content: `<@${token.staffUserID}>`, embed });
return channel.send({ content: `<@${token.staffUserID}>`, embeds: [embed] });
} catch {
return res.sendStatus(401);
}

View File

@ -1,7 +1,7 @@
/* eslint-disable no-continue */
import { TextChannel } from 'eris';
import { TextChannel, MessageEmbed, TextBasedChannel } from 'discord.js';
import { Server } from '..';
import { Route, RichEmbed } from '../../class';
import { Route } from '../../class';
export default class Webhook extends Route {
constructor(server: Server) {
@ -12,7 +12,7 @@ export default class Webhook extends Route {
this.router.post('/s1', async (req, res) => {
try {
if (req.headers.authorization !== this.server.security.keys.iv.toString('base64')) return res.status(401).json({ code: this.constants.codes.UNAUTHORIZED, message: this.constants.codes.UNAUTHORIZED });
const embed = new RichEmbed();
const embed = new MessageEmbed();
embed.setTitle('Service Request');
embed.setDescription(`https://staff.libraryofcode.org/browse/${req.body.key}\n${req.body.url}`);
embed.setColor('#FF00FF');
@ -20,10 +20,10 @@ export default class Webhook extends Route {
embed.addField('Reporter', req.body.reporter, true);
embed.addField('Status', req.body.status, true);
embed.addField('Summary', req.body.summary);
embed.setFooter(this.server.client.user.username, this.server.client.user.avatarURL);
embed.setFooter(this.server.client.user.username, this.server.client.user.avatarURL());
embed.setTimestamp();
const chan = <TextChannel> this.server.client.getChannel('780513128240382002');
chan.createMessage({ content: '<@&741797822940315650>', embed });
const channel = this.server.client.channels.cache.get('780513128240382002') as TextChannel;
channel.send({ content: '<@&741797822940315650>', embeds: [embed] });
return res.status(200).json({ code: this.constants.codes.SUCCESS, message: this.constants.codes.SUCCESS });
} catch (err) {
return this.handleError(err, res);
@ -47,17 +47,18 @@ export default class Webhook extends Route {
} else {
await account.updateOne({ $set: { tier: 3 } });
}
const embed = new RichEmbed();
const embed = new MessageEmbed();
embed.setTitle('Cloud Account | Tier Change');
embed.setColor('#0099ff');
embed.addField('User', `${account.username} | <@${account.userID}>`, true);
embed.addField('Technician', 'SYSTEM', true);
embed.addField('Old Tier -> New Tier', `${account.tier} -> 3`, true);
embed.setFooter(this.server.client.user.username, this.server.client.user.avatarURL);
embed.setFooter(this.server.client.user.username, this.server.client.user.avatarURL());
embed.setTimestamp();
await this.server.client.util.sendMessageToUserTerminal(account.username, 'A technician has changed your tier to 3').catch(() => { });
this.server.client.createMessage('580950455581147146', { embed });
this.server.client.getDMChannel(account.userID).then((channel) => channel.createMessage({ embed })).catch();
const channel = this.server.client.channels.cache.get('580950455581147146') as TextChannel;
channel.send({ embeds: [embed] });
this.server.client.users.cache.get(account.userID).send({ embeds: [embed] });
return res.sendStatus(200);
});
@ -74,17 +75,18 @@ export default class Webhook extends Route {
} else {
await account.updateOne({ $set: { tier: 1 } });
}
const embed = new RichEmbed();
const embed = new MessageEmbed();
embed.setTitle('Cloud Account | Tier Change');
embed.setColor('#0099ff');
embed.addField('User', `${account.username} | <@${account.userID}>`, true);
embed.addField('Technician', 'SYSTEM', true);
embed.addField('Old Tier -> New Tier', `${account.tier} -> 1`, true);
embed.setFooter(this.server.client.user.username, this.server.client.user.avatarURL);
embed.setFooter(this.server.client.user.username, this.server.client.user.avatarURL());
embed.setTimestamp();
await this.server.client.util.sendMessageToUserTerminal(account.username, 'A technician has changed your tier.').catch(() => { });
this.server.client.createMessage('580950455581147146', { embed });
this.server.client.getDMChannel(account.userID).then((channel) => channel.createMessage({ embed })).catch();
const ch = this.server.client.channels.cache.get('580950455581147146') as TextChannel;
ch.send({ embeds: [embed] });
this.server.client.users.cache.get(account.userID).send({ embeds: [embed] });
return res.sendStatus(200);
});
@ -105,17 +107,18 @@ export default class Webhook extends Route {
} else {
await account.updateOne({ $set: { tier: 2 } });
}
const embed = new RichEmbed();
const embed = new MessageEmbed();
embed.setTitle('Cloud Account | Tier Change');
embed.setColor('#0099ff');
embed.addField('User', `${account.username} | <@${account.userID}>`, true);
embed.addField('Technician', 'SYSTEM', true);
embed.addField('Old Tier -> New Tier', `${account.tier} -> 2`, true);
embed.setFooter(this.server.client.user.username, this.server.client.user.avatarURL);
embed.setFooter(this.server.client.user.username, this.server.client.user.avatarURL());
embed.setTimestamp();
await this.server.client.util.sendMessageToUserTerminal(account.username, 'A technician has changed your tier to 2').catch(() => { });
this.server.client.createMessage('580950455581147146', { embed });
this.server.client.getDMChannel(account.userID).then((channel) => channel.createMessage({ embed })).catch();
const ch = this.server.client.channels.cache.get('580950455581147146') as TextChannel;
ch.send({ embeds: [embed] });
this.server.client.users.cache.get(account.userID).send({ embeds: [embed] });
return res.sendStatus(200);
});
@ -132,17 +135,18 @@ export default class Webhook extends Route {
} else {
await account.updateOne({ $set: { tier: 1 } });
}
const embed = new RichEmbed();
const embed = new MessageEmbed();
embed.setTitle('Cloud Account | Tier Change');
embed.setColor('#0099ff');
embed.addField('User', `${account.username} | <@${account.userID}>`, true);
embed.addField('Technician', 'SYSTEM', true);
embed.addField('Old Tier -> New Tier', `${account.tier} -> 1`, true);
embed.setFooter(this.server.client.user.username, this.server.client.user.avatarURL);
embed.setFooter(this.server.client.user.username, this.server.client.user.avatarURL());
embed.setTimestamp();
await this.server.client.util.sendMessageToUserTerminal(account.username, 'A technician has changed your tier.').catch(() => { });
this.server.client.createMessage('580950455581147146', { embed });
this.server.client.getDMChannel(account.userID).then((channel) => channel.createMessage({ embed })).catch();
const ch = this.server.client.channels.cache.get('580950455581147146') as TextChannel;
ch.send({ embeds: [embed] });
this.server.client.users.cache.get(account.userID).send({ embeds: [embed] });
return res.sendStatus(200);
});

View File

@ -20,15 +20,15 @@ export default class AccountUtil {
* @param moderator The Discord user ID for the Staff member that created the account.
*/
public async createAccount(data: { userID: string, username: string, emailAddress: string }, moderator: string): Promise<{ account: AccountInterface, tempPass: string }> {
const moderatorMember = this.client.guilds.get('446067825673633794').members.get(moderator);
const moderatorMember = this.client.guilds.cache.get('446067825673633794').members.cache.get(moderator);
const tempPass = this.client.util.randomPassword();
let passHash = await this.client.util.createHash(tempPass); passHash = passHash.replace(/[$]/g, '\\$').replace('\n', '');
const acctName = this.client.users.get(data.userID).username.replace(/[!@#$%^&*(),.?":{}|<>]/g, '-').replace(/\s/g, '-');
const passHash = (await this.client.util.createHash(tempPass)).replace(/[$]/g, '\\$').replace('\n', '');
const acctName = this.client.users.cache.get(data.userID).username.replace(/[!@#$%^&*(),.?":{}|<>]/g, '-').replace(/\s/g, '-');
const etcPasswd = `${acctName},${data.userID},,`;
const code = randomBytes(3).toString('hex').toUpperCase();
const accountInterface = await this.client.util.createAccount(passHash, etcPasswd, data.username, data.userID, data.emailAddress, moderator, code);
await this.client.util.createModerationLog(data.userID, moderatorMember, 0);
await this.client.util.createModerationLog(data.userID, moderatorMember.user, 0);
const req = await axios.get('https://loc.sh/int/directory');
const find = req.data.find((mem) => mem.userID === moderator);
@ -67,13 +67,14 @@ export default class AccountUtil {
<h3>Want to support us?</h3>
<p>You can support us by purchasing a Tier 3 subscription! <a target="_blank" href="https://canary.discord.com/channels/446067825673633794/620355063088414769/774938174001774592">this site</a> for more information.</p>
<b><i>Library of Code sp-us | Support Team</i></b>
</body>
</body>
`,
});
this.client.guilds.get('446067825673633794').members.get(data.userID).addRole('546457886440685578');
const dmChannel = await this.client.getDMChannel(data.userID).catch();
dmChannel.createMessage('<:loc:607695848612167700> **Thank you for creating an account with us!** <:loc:607695848612167700>\n'
const guild = this.client.guilds.cache.get('446067825673633794');
const member = guild.members.cache.get(data.userID);
await member.roles.add('546457886440685578');
const user = this.client.users.cache.get(data.userID);
user.send('<:loc:607695848612167700> **Thank you for creating an account with us!** <:loc:607695848612167700>\n'
+ `Please log into your account by running \`ssh ${data.username}@cloud.libraryofcode.org\` 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\n'
@ -90,7 +91,7 @@ export default class AccountUtil {
await this.client.util.exec(`lock ${account.username}`);
await account.updateOne({ locked: true });
await this.client.util.createModerationLog(account.userID, this.client.users.get(moderatorID), 2, data?.reason, data?.time);
await this.client.util.createModerationLog(account.userID, this.client.users.cache.get(moderatorID), 2, data?.reason, data?.time);
this.client.util.transport.sendMail({
to: account.emailAddress,
@ -101,9 +102,9 @@ export default class AccountUtil {
<h1>Library of Code | Cloud Services</h1>
<p>Your Cloud Account has been locked until ${data?.time ? moment(data?.time).calendar() : 'indefinitely'} under the EULA.</p>
<p><b>Reason:</b> ${data?.reason ? data.reason : 'none provided'}</p>
<p><b>Technician:</b> ${moderatorID !== this.client.user.id ? this.client.users.get(moderatorID).username : 'SYSTEM'}</p>
<p><b>Technician:</b> ${moderatorID !== this.client.user.id ? (this.client.users.cache.get(moderatorID).username) : 'SYSTEM'}</p>
<p><b>Expiration:</b> ${data?.time ? moment(data?.time).format('dddd, MMMM Do YYYY, h:mm:ss A') : 'N/A'}</p>
<b><i>Library of Code sp-us | Support Team</i></b>
`,
});

View File

@ -1,4 +1,4 @@
import Eris from 'eris';
import { Client as DiscordClient, Intents } from 'discord.js';
import Redis from 'ioredis';
import mongoose from 'mongoose';
import signale from 'signale';
@ -8,8 +8,7 @@ import { Account, AccountInterface, Moderation, ModerationInterface, Domain, Dom
import { emojis } from '../stores';
import { Command, CSCLI, Util, Collection, Server, Event } from '.';
export default class Client extends Eris.Client {
export default class Client extends DiscordClient {
public config: { 'token': string; 'cloudflare': string; 'prefix': string; 'emailPass': string; 'mongoURL': string; 'port': number; 'keyPair': { 'publicKey': string; 'privateKey': string; }; vendorKey: string; internalKey: string; };
public util: Util;
@ -35,7 +34,36 @@ export default class Client extends Eris.Client {
public buildError: boolean
constructor() {
super(config.token, { getAllUsers: true, restMode: true, defaultImageFormat: 'png', intents: ['guildBans', 'guildEmojis', 'guildInvites', 'guildMembers', 'guildMessageReactions', 'guildMessages', 'guildPresences', 'guildWebhooks', 'guilds', 'directMessages'] });
super({
shards: 'auto',
intents: [
Intents.FLAGS.GUILDS,
Intents.FLAGS.GUILD_MEMBERS,
Intents.FLAGS.GUILD_BANS,
Intents.FLAGS.GUILD_EMOJIS_AND_STICKERS,
Intents.FLAGS.GUILD_WEBHOOKS,
Intents.FLAGS.GUILD_INVITES,
Intents.FLAGS.GUILD_INTEGRATIONS,
Intents.FLAGS.GUILD_PRESENCES,
Intents.FLAGS.GUILD_MESSAGES,
Intents.FLAGS.GUILD_MESSAGE_REACTIONS,
Intents.FLAGS.DIRECT_MESSAGES,
],
partials: [
'USER',
'CHANNEL',
'GUILD_MEMBER',
'MESSAGE',
'REACTION',
],
allowedMentions: {
parse: [
'users',
'roles',
],
repliedUser: false,
},
});
process.title = 'cloudservices';
this.config = config;
@ -74,7 +102,7 @@ export default class Client extends Eris.Client {
const funcRequire: Function = require(`${__dirname}/../functions/${func}`).default;
this.functions.set(func.split('.')[0], funcRequire);
} catch (error) {
this.signale.error(`Error occured loading ${func}`);
this.signale.error(`Error occurred loading ${func}`);
await this.util.handleError(error);
}
});
@ -83,7 +111,6 @@ export default class Client extends Eris.Client {
public loadCommand(CommandFile: any) {
// eslint-disable-next-line no-useless-catch
try {
// eslint-disable-next-line
const command: Command = new CommandFile(this);
if (command.subcmds.length) {
command.subcmds.forEach((C) => {
@ -126,19 +153,17 @@ export default class Client extends Eris.Client {
public async init() {
await mongoose.connect(config.mongoURL, { useNewUrlParser: true, useUnifiedTopology: true });
await this.connect();
await this.login(config.token);
this.on('ready', () => {
this.signale.info(`Connected to Discord as ${this.user.username}#${this.user.discriminator}`);
});
const intervals = await fs.readdir(`${__dirname}/../intervals`);
intervals.forEach((interval) => {
// eslint-disable-next-line
if (interval === 'index.js') return;
require(`${__dirname}/../intervals/${interval}`).default(this);
this.signale.complete(`Loaded interval ${interval.split('.')[0]}`);
});
this.server = new Server(this, { port: this.config.port });
// eslint-disable-next-line no-new
const corepath = '/opt/CloudServices/dist';
const cmdFiles = await fs.readdir('/opt/CloudServices/dist/commands');

View File

@ -1,4 +1,4 @@
import { Message, TextableChannel } from 'eris';
import { Message, TextBasedChannels } from 'discord.js';
import { Client, Collection } from '.';
export default class Command {
@ -39,15 +39,15 @@ export default class Command {
this.permissions = {};
}
public success(channel: TextableChannel, txt: string) {
return channel.createMessage(`***${this.client.stores.emojis.success} ${txt}***`);
public success(channel: TextBasedChannels, txt: string) {
return channel.send(`***${this.client.stores.emojis.success} ${txt}***`);
}
public loading(channel: TextableChannel, txt: string) {
return channel.createMessage(`***${this.client.stores.emojis.loading} ${txt}***`);
public loading(channel: TextBasedChannels, txt: string) {
return channel.send(`***${this.client.stores.emojis.loading} ${txt}***`);
}
public error(channel: TextableChannel, txt: string) {
return channel.createMessage(`***${this.client.stores.emojis.error} ${txt}***`);
public error(channel: TextBasedChannels, txt: string) {
return channel.send(`***${this.client.stores.emojis.error} ${txt}***`);
}
}

View File

@ -5,9 +5,8 @@ import { gzip, gzipSync, unzip } from 'zlib';
type JSONData = [{key: string, value: any}?];
/**
* Persistant local JSON-based storage.
* Persistent local JSON-based storage.
* - auto-locking system to prevent corrupted data
* - uses gzip compression to keep DB storage space utilization low
* @author Matthew <matthew@staff.libraryofcode.org>

View File

@ -0,0 +1,56 @@
import { Message, MessageEmbed, EmojiResolvable, User, MessageReaction } from 'discord.js';
export default async function PaginationEmbed(message: Message, pages: MessageEmbed[], options?: {leftArrow?: EmojiResolvable, rightArrow?: EmojiResolvable, timeout?: number }) {
// eslint-disable-next-line no-param-reassign
options = {
leftArrow: options?.leftArrow ?? '⬅️',
rightArrow: options?.rightArrow ?? '➡️',
timeout: options?.timeout ?? 120000,
};
let pageNumber: number = 0;
const paginationMessage = await message.channel.send({ content: `Page ${pageNumber + 1} of ${pages.length}`, embeds: [pages[pageNumber]] });
await paginationMessage.react(options.leftArrow);
await paginationMessage.react(options.rightArrow);
const filter = (reaction: MessageReaction, user: User) => {
if ([options.leftArrow, options.rightArrow].includes(reaction.emoji.name)
&& !user.bot
&& user.id === message.author.id) {
return true;
}
return false;
};
const reactionCollector = paginationMessage.createReactionCollector({
filter,
time: options.timeout,
dispose: true,
});
reactionCollector.on('collect', (reaction, user) => {
reaction.users.remove(user);
if (reaction.emoji.name === options.leftArrow) {
if (pageNumber > 0) {
pageNumber -= 1;
} else {
pageNumber = pages.length - 1;
}
} else if (reaction.emoji.name === options.rightArrow) {
if (pageNumber + 1 < pages.length) {
pageNumber += 1;
} else {
pageNumber = 0;
}
}
paginationMessage.edit({ content: `Page ${pageNumber + 1} / ${pages.length}`, embeds: [pages[pageNumber]] });
});
reactionCollector.on('end', () => {
if (!paginationMessage.deleted) {
paginationMessage.reactions.removeAll();
}
});
return paginationMessage;
}

View File

@ -104,7 +104,7 @@ export default class Report {
* @param pin The last 4 digits of the member's PIN number.
* @param reason A reason for the hard inquiry.
* ```ts
* Report.hard('253600545972027394', 1102, 'Verification and Elibility for Personal Account');
* Report.hard('253600545972027394', 1102, 'Verification and Eligibility for Personal Account');
* ```
*/
public static async hard(userID: string, pin: number, reason: string, auth: string): Promise<HardReport> {

View File

@ -1,176 +0,0 @@
/* eslint-disable no-param-reassign */
export default class RichEmbed {
title?: string
type?: string
description?: string
url?: string
timestamp?: Date
color?: number
footer?: { text: string, icon_url?: string, proxy_icon_url?: string}
image?: { url: string, proxy_url?: string, height?: number, width?: number }
thumbnail?: { url?: string, proxy_url?: string, height?: number, width?: number }
video?: { url: string, height?: number, width?: number }
provider?: { name: string, url?: string}
author?: { name: string, url?: string, proxy_icon_url?: string, icon_url?: string}
fields?: {name: string, value: string, inline?: boolean}[]
constructor(data: {
title?: string, type?: string, description?: string, url?: string, timestamp?: Date, color?: number, fields?: {name: string, value: string, inline?: boolean}[]
footer?: { text: string, icon_url?: string, proxy_icon_url?: string}, image?: { url: string, proxy_url?: string, height?: number, width?: number },
thumbnail?: { url: string, proxy_url?: string, height?: number, width?: number }, video?: { url: string, height?: number, width?: number },
provider?: { name: string, url?: string}, author?: { name: string, url?: string, proxy_icon_url?: string, icon_url?: string},
} = {}) {
/*
let types: {
title?: string, type?: string, description?: string, url?: string, timestamp?: Date, color?: number, fields?: {name: string, value: string, inline?: boolean}[]
footer?: { text: string, icon_url?: string, proxy_icon_url?: string}, image?: { url?: string, proxy_url?: string, height?: number, width?: number },
thumbnail?: { url?: string, proxy_url?: string, height?: number, width?: number }, video?: { url?: string, height?: number, width?: number },
provider?: { name?: string, url?: string}, author?: { name?: string, url?: string, proxy_icon_url?: string, icon_url?: string}
};
*/
this.title = data.title;
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;
}
/**
* Sets the title of this embed.
*/
setTitle(title: string) {
if (typeof title !== 'string') throw new TypeError('RichEmbed titles must be a string.');
if (title.length > 256) throw new RangeError('RichEmbed titles may not exceed 256 characters.');
this.title = title;
return this;
}
/**
* Sets the description of this embed.
*/
setDescription(description: string) {
if (typeof description !== 'string') throw new TypeError('RichEmbed descriptions must be a string.');
if (description.length > 2048) throw new RangeError('RichEmbed descriptions may not exceed 2048 characters.');
this.description = description;
return this;
}
/**
* Sets the URL of this embed.
*/
setURL(url: string) {
if (typeof url !== 'string') throw new TypeError('RichEmbed URLs must be a string.');
if (!url.startsWith('http://') || !url.startsWith('https://')) url = `https://${url}`;
this.url = url;
return this;
}
/**
* Sets the color of this embed.
*/
setColor(color: string | number) {
if (typeof color === 'string' || typeof color === 'number') {
if (typeof color === 'string') {
const regex = /[^a-f0-9]/gi;
color = color.replace(/#/g, '');
if (regex.test(color)) throw new RangeError('Hexadecimal colours must not contain characters other than 0-9 and a-f.');
color = parseInt(color, 16);
} else if (color < 0 || color > 16777215) throw new RangeError('Base 10 colours must not be less than 0 or greater than 16777215.');
this.color = color;
return this;
}
throw new TypeError('RichEmbed colours must be hexadecimal as string or number.');
}
/**
* Sets the author of this embed.
*/
setAuthor(name: string, icon_url?: string, url?: string) {
if (typeof name !== 'string') throw new TypeError('RichEmbed Author names must be a string.');
if (url && typeof url !== 'string') throw new TypeError('RichEmbed Author URLs must be a string.');
if (icon_url && typeof icon_url !== 'string') throw new TypeError('RichEmbed Author icons must be a string.');
this.author = { name, icon_url, url };
return this;
}
/**
* Sets the timestamp of this embed.
*/
setTimestamp(timestamp = new Date()) {
// eslint-disable-next-line no-restricted-globals
if (isNaN(timestamp.getTime())) throw new TypeError('Expecting ISO8601 (Date constructor)');
this.timestamp = timestamp;
return this;
}
/**
* Adds a field to the embed (max 25).
*/
addField(name: string, value: string, inline = false) {
if (typeof name !== 'string') throw new TypeError('RichEmbed Field names must be a string.');
if (typeof value !== 'string') throw new TypeError('RichEmbed Field values must be a string.');
if (typeof inline !== 'boolean') throw new TypeError('RichEmbed Field inlines must be a boolean.');
if (this.fields.length >= 25) throw new RangeError('RichEmbeds may not exceed 25 fields.');
if (name.length > 256) throw new RangeError('RichEmbed field names may not exceed 256 characters.');
if (!/\S/.test(name)) throw new RangeError('RichEmbed field names may not be empty.');
if (value.length > 1024) throw new RangeError('RichEmbed field values may not exceed 1024 characters.');
if (!/\S/.test(value)) throw new RangeError('RichEmbed field values may not be empty.');
this.fields.push({ name, value, inline });
return this;
}
/**
* Convenience function for `<RichEmbed>.addField('\u200B', '\u200B', inline)`.
*/
addBlankField(inline = false) {
return this.addField('\u200B', '\u200B', inline);
}
/**
* Set the thumbnail of this embed.
*/
setThumbnail(url: string) {
if (typeof url !== 'string') throw new TypeError('RichEmbed Thumbnail URLs must be a string.');
this.thumbnail = { url };
return this;
}
/**
* Set the image of this embed.
*/
setImage(url: string) {
if (typeof url !== 'string') throw new TypeError('RichEmbed Image URLs must be a string.');
if (!url.startsWith('http://') || !url.startsWith('https://')) url = `https://${url}`;
this.image = { url };
return this;
}
/**
* Sets the footer of this embed.
*/
setFooter(text: string, icon_url?: 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 (text.length > 2048) throw new RangeError('RichEmbed footer text may not exceed 2048 characters.');
this.footer = { text, icon_url };
return this;
}
}

View File

@ -1,152 +1,152 @@
/* eslint-disable consistent-return */
import { Request, Response, NextFunction, Router as router } from 'express';
import { Server } from '.';
export default class Route {
public server: Server;
public router: router;
public conf: { path: string, deprecated?: boolean, maintenance?: boolean };
protected constructor(server: Server, conf: { path: string, deprecated?: boolean, maintenance?: boolean }) {
this.conf = {
path: null,
deprecated: false,
maintenance: false,
};
this.server = server;
this.router = router();
this.conf = conf;
}
public bind() {}
public deprecated(): void {
this.router.all('*', (_req, res) => {
res.status(501).json({ code: this.constants.codes.DEPRECATED, message: this.constants.messages.DEPRECATED });
});
}
public maintenance(): void {
this.router.all('*', (_req, res) => {
res.status(503).json({ code: this.constants.codes.MAINTENANCE_OR_UNAVAILABLE, message: this.constants.messages.MAINTENANCE_OR_UNAVAILABLE });
});
}
public init(): void {
this.router.all('*', (req, res, next) => {
// this.server.client.signale.log(`'${req.method}' request from '${req.ip}' to '${req.hostname}${req.path}'.`);
if (this.conf.maintenance === true) res.status(503).json({ code: this.constants.codes.MAINTENANCE_OR_UNAVAILABLE, message: this.constants.messages.MAINTENANCE_OR_UNAVAILABLE });
else if (this.conf.deprecated === true) res.status(501).json({ code: this.constants.codes.DEPRECATED, message: this.constants.messages.DEPRECATED });
else next();
});
}
/**
* This function checks for the presense of a Bearer token with Security.extractBearer(),
* then it will attempt to validate it with Security.checkBearer().
* If it can authenticate the request, it'll add a custom property on Request called
* `account`, which will hold an the bearer token's account owner. The account is of the
* type `AccountInterface`.
* @param req The Request object from Express.
* @param res The Response object from Express.
* @param next The NextFunction from Express.
* @example Security.authorize(req, res, next);
*/
public async authorize(req: Request, res: Response, next: NextFunction) {
const account = await this.server.security.checkBearer(this.server.security.extractBearer(req));
if (!account) return res.status(401).json({ code: this.constants.codes.UNAUTHORIZED, message: this.constants.messages.UNAUTHORIZED });
Object.defineProperty(req, 'account', { value: account, writable: true, enumerable: true, configurable: true });
next();
}
/**
* This function calls Util.handleError() internally, however it also sends a generic
* response to the user.
* @param error The Error object.
* @param res The Response object from Express.
*/
public handleError(error: Error, res: Response): void {
this.server.client.util.handleError(error);
res.status(500).json({ code: this.constants.codes.SERVER_ERROR, message: this.constants.messages.SERVER_ERROR });
}
get constants() {
return {
codes: {
/**
* SUCCESS 100
* Used if the request was processed successfully.
*/
SUCCESS: 100,
/**
* UNAUTHORIZED 101
* Used if the client calling the request couldn't be correctly authenticated.
*/
UNAUTHORIZED: 101,
/**
* PERMISSION DENIED 103
* Used if the client calling the request doesn't have access to the resource specified.
*/
PERMISSION_DENIED: 103,
/**
* NOT FOUND 104
* Used if the resource the client requested doesn't exist.
*/
NOT_FOUND: 104,
/**
* ACCOUNT NOT FOUND 1041
* Used if the account specified by the client couldn't be found.
*/
ACCOUNT_NOT_FOUND: 1041,
/**
* CLIENT ERROR 1044
* Used in cases of user error. Examples are incorrect parameters, incorrect headers, or an invalid request.
*/
CLIENT_ERROR: 1044,
/**
* SERVER ERROR 105
* Used in cases of an internal error that caused the bind() function to throw.
*/
SERVER_ERROR: 105,
/**
* DEPRECATED 1051
* Returned back to the user if the resource requested is deprecated.
*/
DEPRECATED: 1051,
/**
* MAINTENANCE OR UNAVAILABLE 1053
* Used if the resource requested is currently in maintenance, not finished, or temporarily disabled.
*/
MAINTENANCE_OR_UNAVAILABLE: 1053,
},
messages: {
/**
* The credentials you supplied are invalid.
*/
UNAUTHORIZED: ['CREDENTIALS_INVALID', 'The credentials you supplied are invalid.'],
/**
* You do not have valid credentials to access this resource.
*/
PERMISSION_DENIED: ['PERMISSION_DENIED', 'You do not have valid credentials to access this resource.'],
/**
* The resource you requested cannot be located.
*/
NOT_FOUND: ['NOT_FOUND', 'The resource you requested cannot be located.'],
/**
* An internal error has occurred, Engineers have been notified.
*/
SERVER_ERROR: ['INTERNAL_ERROR', 'An internal error has occurred, Engineers have been notified.'],
/**
* The endpoint or resource you\'re trying to access has been deprecated.
*/
DEPRECATED: ['ENDPOINT_OR_RESOURCE_DEPRECATED', 'The endpoint or resource you\'re trying to access has been deprecated.'],
/**
* The endpoint or resource you\'re trying to access is either in maintenance or is not available.
*/
MAINTENANCE_OR_UNAVAILABLE: ['SERVICE_UNAVAILABLE', 'The endpoint or resource you\'re trying to access is either in maintenance or is not available.'],
},
};
}
}
/* eslint-disable consistent-return */
import { Request, Response, NextFunction, Router as router } from 'express';
import { Server } from '.';
export default class Route {
public server: Server;
public router: router;
public conf: { path: string, deprecated?: boolean, maintenance?: boolean };
protected constructor(server: Server, conf: { path: string, deprecated?: boolean, maintenance?: boolean }) {
this.conf = {
path: null,
deprecated: false,
maintenance: false,
};
this.server = server;
this.router = router();
this.conf = conf;
}
public bind() {}
public deprecated(): void {
this.router.all('*', (_req, res) => {
res.status(501).json({ code: this.constants.codes.DEPRECATED, message: this.constants.messages.DEPRECATED });
});
}
public maintenance(): void {
this.router.all('*', (_req, res) => {
res.status(503).json({ code: this.constants.codes.MAINTENANCE_OR_UNAVAILABLE, message: this.constants.messages.MAINTENANCE_OR_UNAVAILABLE });
});
}
public init(): void {
this.router.all('*', (req, res, next) => {
this.server.client.signale.log(`'${req.method}' request from '${req.ip}' to '${req.hostname}${req.path}'.`);
if (this.conf.maintenance === true) res.status(503).json({ code: this.constants.codes.MAINTENANCE_OR_UNAVAILABLE, message: this.constants.messages.MAINTENANCE_OR_UNAVAILABLE });
else if (this.conf.deprecated === true) res.status(501).json({ code: this.constants.codes.DEPRECATED, message: this.constants.messages.DEPRECATED });
else next();
});
}
/**
* This function checks for the presence of a Bearer token with Security.extractBearer(),
* then it will attempt to validate it with Security.checkBearer().
* If it can authenticate the request, it'll add a custom property on Request called
* `account`, which will hold an the bearer token's account owner. The account is of the
* type `AccountInterface`.
* @param req The Request object from Express.
* @param res The Response object from Express.
* @param next The NextFunction from Express.
* @example Security.authorize(req, res, next);
*/
public async authorize(req: Request, res: Response, next: NextFunction) {
const account = await this.server.security.checkBearer(this.server.security.extractBearer(req));
if (!account) return res.status(401).json({ code: this.constants.codes.UNAUTHORIZED, message: this.constants.messages.UNAUTHORIZED });
Object.defineProperty(req, 'account', { value: account, writable: true, enumerable: true, configurable: true });
next();
}
/**
* This function calls Util.handleError() internally, however it also sends a generic
* response to the user.
* @param error The Error object.
* @param res The Response object from Express.
*/
public handleError(error: Error, res: Response): void {
this.server.client.util.handleError(error);
res.status(500).json({ code: this.constants.codes.SERVER_ERROR, message: this.constants.messages.SERVER_ERROR });
}
get constants() {
return {
codes: {
/**
* SUCCESS 100
* Used if the request was processed successfully.
*/
SUCCESS: 100,
/**
* UNAUTHORIZED 101
* Used if the client calling the request couldn't be correctly authenticated.
*/
UNAUTHORIZED: 101,
/**
* PERMISSION DENIED 103
* Used if the client calling the request doesn't have access to the resource specified.
*/
PERMISSION_DENIED: 103,
/**
* NOT FOUND 104
* Used if the resource the client requested doesn't exist.
*/
NOT_FOUND: 104,
/**
* ACCOUNT NOT FOUND 1041
* Used if the account specified by the client couldn't be found.
*/
ACCOUNT_NOT_FOUND: 1041,
/**
* CLIENT ERROR 1044
* Used in cases of user error. Examples are incorrect parameters, incorrect headers, or an invalid request.
*/
CLIENT_ERROR: 1044,
/**
* SERVER ERROR 105
* Used in cases of an internal error that caused the bind() function to throw.
*/
SERVER_ERROR: 105,
/**
* DEPRECATED 1051
* Returned back to the user if the resource requested is deprecated.
*/
DEPRECATED: 1051,
/**
* MAINTENANCE OR UNAVAILABLE 1053
* Used if the resource requested is currently in maintenance, not finished, or temporarily disabled.
*/
MAINTENANCE_OR_UNAVAILABLE: 1053,
},
messages: {
/**
* The credentials you supplied are invalid.
*/
UNAUTHORIZED: ['CREDENTIALS_INVALID', 'The credentials you supplied are invalid.'],
/**
* You do not have valid credentials to access this resource.
*/
PERMISSION_DENIED: ['PERMISSION_DENIED', 'You do not have valid credentials to access this resource.'],
/**
* The resource you requested cannot be located.
*/
NOT_FOUND: ['NOT_FOUND', 'The resource you requested cannot be located.'],
/**
* An internal error has occurred, Engineers have been notified.
*/
SERVER_ERROR: ['INTERNAL_ERROR', 'An internal error has occurred, Engineers have been notified.'],
/**
* The endpoint or resource you\'re trying to access has been deprecated.
*/
DEPRECATED: ['ENDPOINT_OR_RESOURCE_DEPRECATED', 'The endpoint or resource you\'re trying to access has been deprecated.'],
/**
* The endpoint or resource you\'re trying to access is either in maintenance or is not available.
*/
MAINTENANCE_OR_UNAVAILABLE: ['SERVICE_UNAVAILABLE', 'The endpoint or resource you\'re trying to access is either in maintenance or is not available.'],
},
};
}
}

View File

@ -6,7 +6,6 @@ import fs from 'fs-extra';
import { Client, Collection, LocalStorage, Route } from '.';
import { Security } from '../api';
export default class Server {
public routes: Collection<Route>

View File

@ -5,14 +5,14 @@ import axios from 'axios';
import { randomBytes } from 'crypto';
import childProcess from 'child_process';
import nodemailer from 'nodemailer';
import { Message, PrivateChannel, GroupChannel, Member, User } from 'eris';
import uuid from 'uuid/v4';
import { Message, DMChannel, User, MessageEmbed, ColorResolvable, TextChannel } from 'discord.js';
import { v4 as uuid } from 'uuid';
import moment from 'moment';
import fs from 'fs';
import hastebin from 'hastebin-gen';
import { getUserByUid } from '../functions';
import { AccountUtil, Client, Command, RichEmbed } from '.';
import { ModerationInterface, AccountInterface, Account } from '../models';
import { AccountUtil, Client, Command, PaginationEmbed } from '.';
import { ModerationInterface, AccountInterface } from '../models';
import { Certificate } from '../../types/x509';
export default class Util {
@ -96,7 +96,6 @@ export default class Util {
return returnArray;
}
/**
* Resolves a command
* @param query Command input
@ -131,29 +130,32 @@ export default class Util {
this.client.signale.error(error);
const info = { content: `\`\`\`js\n${error.stack}\n\`\`\``, embed: null };
if (message) {
const embed = new RichEmbed();
embed.setColor('FF0000');
embed.setAuthor(`Error caused by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL);
const embed = new MessageEmbed();
embed.setColor('#FF0000');
embed.setAuthor(`Error caused by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL());
embed.setTitle('Message content');
embed.setDescription(message.content);
embed.addField('User', `${message.author.mention} (\`${message.author.id}\`)`, true);
embed.addField('Channel', message.channel.mention, true);
embed.addField('User', `${message.author.toString()} (\`${message.author.id}\`)`, true);
embed.addField('Channel', `<#${message.channel.id}>`, true);
let guild: string;
if (message.channel instanceof PrivateChannel || message.channel instanceof GroupChannel) guild = '@me';
else guild = message.channel.guild.id;
if (message.channel instanceof DMChannel) guild = '@me';
else guild = message.guild.id;
embed.addField('Message link', `[Click here](https://discordapp.com/channels/${guild}/${message.channel.id}/${message.id})`, true);
embed.setTimestamp(new Date(message.timestamp));
embed.setTimestamp(new Date(message.createdTimestamp));
info.embed = embed;
}
await this.client.createMessage('595788220764127272', info);
const ch = this.client.channels.cache.get('595788220764127272') as TextChannel;
await ch.send(info);
const msg = message.content.slice(this.client.config.prefix.length).trim().split(/ +/g);
if (command) this.resolveCommand(msg).then((c) => { c.cmd.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.send(`***${this.client.stores.emojis.error} An unexpected error has occurred - please contact a member of the Engineering Team.${command ? ' This command has been disabled.' : ''}***`);
} catch (err) {
this.client.signale.error(err);
}
}
public createPaginationEmbed = PaginationEmbed;
public splitFields(fields: { name: string, value: string, inline?: boolean }[]): { name: string, value: string, inline?: boolean }[][] {
let index = 0;
const array: {name: string, value: string, inline?: boolean}[][] = [[]];
@ -181,7 +183,6 @@ export default class Util {
return arrayString;
}
public async createHash(password: string): Promise<string> {
const hashed = await this.exec(`mkpasswd -m sha-512 "${password}"`);
return hashed;
@ -220,13 +221,15 @@ export default class Util {
this.exec(`deluser ${username} --remove-home --backup-to /management/Archives && rm -rf -R ${account.homepath}`),
this.client.db.Account.deleteOne({ username }),
];
this.client.removeGuildMemberRole('446067825673633794', account.userID, '546457886440685578', 'Cloud Account Deleted').catch();
const guild = this.client.guilds.cache.get('446067825673633794');
const member = await guild.members.fetch(account.userID);
await member.roles.remove('546457886440685578', 'Cloud Account Deleted');
// @ts-ignore
await Promise.all(tasks);
}
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.send(question);
return new Promise((res, rej) => {
const func = (Msg: Message) => {
if (filter(Msg) === false) return;
@ -253,7 +256,7 @@ export default class Util {
*
* `4` - Delete
*/
public async createModerationLog(user: string, moderator: Member|User, type: number, reason?: string, duration?: number): Promise<ModerationInterface> {
public async createModerationLog(user: string, moderator: User, type: number, reason?: string, duration?: number): Promise<ModerationInterface> {
const moderatorID = moderator.id;
const account = await this.client.db.Account.findOne({ $or: [{ username: user }, { userID: user }] });
if (!account) return Promise.reject(new Error(`Account ${user} not found`));
@ -280,28 +283,30 @@ export default class Util {
await log.save();
let embedTitle: string;
let color: string;
let color: ColorResolvable;
let archType: string;
switch (type) {
default: archType = 'Staff'; embedTitle = 'Cloud Account | Generic'; color = '0892e1'; break;
case 0: archType = 'Technician'; embedTitle = 'Cloud Account | Create'; color = '00ff00'; break;
case 1: archType = 'Technician'; embedTitle = 'Account Warning | Warn'; color = 'ffff00'; break;
case 2: archType = 'Technician'; embedTitle = 'Account Infraction | Lock'; color = 'ff6600'; break;
case 3: archType = 'Technician'; embedTitle = 'Account Reclaim | Unlock'; color = '0099ff'; break;
case 4: archType = 'Director'; embedTitle = 'Cloud Account | Delete'; color = 'ff0000'; break;
default: archType = 'Staff'; embedTitle = 'Cloud Account | Generic'; color = '#0892e1'; break;
case 0: archType = 'Technician'; embedTitle = 'Cloud Account | Create'; color = '#00ff00'; break;
case 1: archType = 'Technician'; embedTitle = 'Account Warning | Warn'; color = '#ffff00'; break;
case 2: archType = 'Technician'; embedTitle = 'Account Infraction | Lock'; color = '#ff6600'; break;
case 3: archType = 'Technician'; embedTitle = 'Account Reclaim | Unlock'; color = '#0099ff'; break;
case 4: archType = 'Director'; embedTitle = 'Cloud Account | Delete'; color = '#ff0000'; break;
}
const req = await axios.get('https://loc.sh/int/directory');
const find = req.data.find((mem) => mem.userID === moderator.id);
const embed = new RichEmbed()
const embed = new MessageEmbed()
.setTitle(embedTitle)
.setColor(color)
.addField('User', `${username} | <@${userID}>`, true)
.addField(archType, moderatorID === this.client.user.id ? 'SYSTEM' : `${moderator.username}, ${find.pn.join(', ')} (<@${moderatorID}>)`, true)
.setFooter(this.client.user.username, this.client.user.avatarURL)
.setFooter(this.client.user.username, this.client.user.avatarURL())
.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'}`);
this.client.createMessage('580950455581147146', { embed }); this.client.getDMChannel(userID).then((channel) => channel.createMessage({ embed })).catch();
const ch = this.client.channels.cache.get('580950455581147146') as TextChannel;
ch.send({ embeds: [embed] });
this.client.users.fetch(userID).then((channel) => channel.send({ embeds: [embed] })).catch();
return Promise.resolve(log);
}

View File

@ -8,8 +8,8 @@ export { default as Event } from './Event';
export { default as Handler } from './Handler';
export { default as LocalStorage } from './LocalStorage';
export { default as Report } from './Report';
export { default as RichEmbed } from './RichEmbed';
export { default as Route } from './Route';
export { default as Security } from './Security';
export { default as Server } from './Server';
export { default as Util } from './Util';
export { default as PaginationEmbed } from './PaginationEmbed';

View File

@ -1,4 +1,4 @@
import { Message } from 'eris';
import { Message, TextChannel } from 'discord.js';
import { Client, Command } from '../class';
export default class AddReferral extends Command {
@ -19,10 +19,10 @@ export default class AddReferral extends Command {
if (!account) return this.error(message.channel, 'Cannot find user.');
await account.updateOne({ $inc: { totalReferrals: 1 } });
this.client.getDMChannel(account.userID).then((chan) => {
chan.createMessage('__**Referral - Application Approval**__\nAn applicant who used your referral code during the application process has been approved. Your referral count has been increased.');
this.client.users.fetch(account.userID).then((chan) => {
chan.send('__**Referral - Application Approval**__\nAn applicant who used your referral code during the application process has been approved. Your referral count has been increased.');
}).catch(() => {});
return this.success(message.channel, `Added referral value to account.\nReferrer: \`${account.username}\` | <@${account.userID}>`);
return this.success(message.channel as TextChannel, `Added referral value to account.\nReferrer: \`${account.username}\` | <@${account.userID}>`);
} catch (error) {
return this.client.util.handleError(error, message, this);
}

View File

@ -1,4 +1,4 @@
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
export default class Announce extends Command {

View File

@ -1,6 +1,6 @@
/* eslint-disable no-useless-escape */
import { Message } from 'eris';
import { Client, Command, Report, RichEmbed } from '../class';
import { Message, MessageEmbed, TextChannel } from 'discord.js';
import { Client, Command, Report } from '../class';
export default class ApplyT2 extends Command {
constructor(client: Client) {
@ -24,7 +24,7 @@ export default class ApplyT2 extends Command {
const decision = await Report.tier2(account.userID, this.client.config.internalKey);
if (decision.status === 'SUCCESS') {
await loading.delete();
message.channel.createMessage(`__**Decision**__\n\n**Status:** ${decision.decision}\n**Processed by:** EDS (A\*01)`);
message.channel.send(`__**Decision**__\n\n**Status:** ${decision.decision}\n**Processed by:** EDS (A\*01)`);
if (decision.decision === 'APPROVED') {
const tier = await this.client.db.Tier.findOne({ id: 2 });
@ -33,21 +33,23 @@ export default class ApplyT2 extends Command {
} else {
await account.updateOne({ $set: { tier: 2 } });
}
const embed = new RichEmbed();
const embed = new MessageEmbed();
embed.setTitle('Cloud Account | Tier Change');
embed.setColor('#0099ff');
embed.addField('User', `${account.username} | <@${account.userID}>`, true);
embed.addField('Technician', 'SYSTEM', true);
embed.addField('Old Tier -> New Tier', `${account.tier} -> 2`, true);
embed.setFooter(this.client.user.username, this.client.user.avatarURL);
embed.setFooter(this.client.user.username, this.client.user.avatarURL());
embed.setTimestamp();
await this.client.util.sendMessageToUserTerminal(account.username, 'A technician has changed your tier to 2').catch(() => { });
this.client.createMessage('580950455581147146', { embed }); return this.client.getDMChannel(account.userID).then((channel) => channel.createMessage({ embed })).catch();
const ch = this.client.channels.cache.get('580950455581147146') as TextChannel;
ch.send({ embeds: [embed] });
return this.client.users.fetch(account.userID).then((channel) => channel.send({ embeds: [embed] })).catch();
}
return null;
}
await loading.delete();
return message.channel.createMessage(`__**Decision**__\n\n**Status:** ${decision.decision}\n**Processed by:** EDS (A*01)\n\n\n*Pre-Declines will not result in a hard pull, and they may be due to a server issue or insufficient information. You may want to contact a Staff member for further information.*`);
return message.channel.send(`__**Decision**__\n\n**Status:** ${decision.decision}\n**Processed by:** EDS (A*01)\n\n\n*Pre-Declines will not result in a hard pull, and they may be due to a server issue or insufficient information. You may want to contact a Staff member for further information.*`);
} catch (error) {
return this.client.util.handleError(error, message, this);
}

View File

@ -1,5 +1,5 @@
import jwt from 'jsonwebtoken';
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
export default class AuthReferral extends Command {
@ -18,13 +18,23 @@ export default class AuthReferral extends Command {
if (!args.length) return this.client.commands.get('help').run(message, [this.name]);
const referrer = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { referralCode: args[0] }, { userID: args[0].replace(/[<@!>]/gi, '') }] });
if (!referrer) return this.error(message.channel, 'Cannot find referrer.');
const referred = await this.client.getRESTGuildMember('446067825673633794', args[1]);
const referred = await message.guild.members.fetch(args[1]);
if (!referred) return this.error(message.channel, 'Cannot find referred member.');
const token = jwt.sign({ staffUserID: message.author.id, referralCode: referrer.referralCode, referrerUserID: referrer.userID, referrerUsername: referrer.username, referredUserID: referred.id, referredUserAndDiscrim: `${referred.username}#${referred.discriminator}` }, this.client.config.keyPair.privateKey, { expiresIn: '24 hours', issuer: 'Library of Code sp-us | Cloud Services Daemon' });
this.client.getDMChannel(referrer.userID).then(async (chan) => {
await chan.createMessage(`__**Referral Request Authorization**__\nYour referral code has been used in an application recently submitted to us. We need to authorize this request, please visit https://loc.sh/rv and enter the authorization token below. This token expires in 24 hours. If you did not authorize this request, please contact us immediately by DMing Ramirez or opening a ticket at https://loc.sh/cs-help.\n**Referred User:** ${referred.username}#${referred.discriminator} | <@${referred.user.id}>`);
await chan.createMessage(`\`${token}\``);
const token = jwt.sign(
{ staffUserID: message.author.id,
referralCode: referrer.referralCode,
referrerUserID: referrer.userID,
referrerUsername: referrer.username,
referredUserID: referred.id,
referredUserAndDiscrim: `${referred.user.username}#${referred.user.discriminator}` },
this.client.config.keyPair.privateKey, { expiresIn: '24 hours', issuer: 'Library of Code sp-us | Cloud Services Daemon' },
);
this.client.users.fetch(referrer.userID).then(async (user) => {
await user.send('__**Referral Request Authorization**__\n'
+ 'Your referral code has been used in an application recently submitted to us. We need to authorize this request, please visit https://loc.sh/rv and enter the authorization token below. This token expires in 24 hours. If you did not authorize this request, please contact us immediately by DMing Ramirez or opening a ticket at https://loc.sh/cs-help.\n'
+ `**Referred User:** ${referred.user.username}#${referred.user.discriminator} | ${referred.user.toString()}`);
await user.send(`\`${token}\``);
}).catch(() => {
this.error(message.channel, 'Could not DM referrer.');
});

View File

@ -1,4 +1,4 @@
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
import Bearer_Revoke from './bearer_revoke';
@ -19,8 +19,8 @@ export default class Bearer extends Command {
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}`);
const dm = await this.client.users.fetch(message.author.id);
const msg = await dm.send(`__**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}`);
this.success(message.channel, 'Bearer token sent to direct messages.');
return setTimeout(() => {
msg.delete();

View File

@ -1,4 +1,4 @@
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
export default class Bearer_Revoke extends Command {

View File

@ -1,5 +1,5 @@
import axios from 'axios';
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
export default class Cloudflare extends Command {

View File

@ -1,4 +1,4 @@
import { Message, PrivateChannel, GroupChannel } from 'eris';
import { GuildMember, Message } from 'discord.js';
import { Client, Command } from '../class';
export default class CreateAccount extends Command {
@ -20,10 +20,10 @@ export default class CreateAccount extends Command {
public async run(message: Message, args: string[]) {
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 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 member: GuildMember = await message.guild.members.fetch(args[0]);
if (!member) return this.error(message.channel, 'User not found.');
if (member.user.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 this.error(message.channel, `<@${args[0]}> already has an account.`);
const checkEmail = await this.client.db.Account.findOne({ emailAddress: args[1] });

View File

@ -1,4 +1,4 @@
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
import Create from './cwg_create';
import Data from './cwg_data';

View File

@ -1,9 +1,9 @@
import fs, { writeFile, unlink } from 'fs-extra';
import axios from 'axios';
import { randomBytes } from 'crypto';
import { Message } from 'eris';
import { Message, MessageEmbed, TextChannel } from 'discord.js';
import { AccountInterface } from '../models';
import { Client, Command, RichEmbed } from '../class';
import { Client, Command } from '../class';
import { parseCertificate } from '../functions';
export default class CWG_Create extends Command {
@ -45,7 +45,7 @@ export default class CWG_Create extends Command {
try {
answer = await this.client.util.messageCollector(
message,
`***${this.client.stores.emojis.error} This port is already binded to a domain. Do you wish to continue? (y/n)***`,
`***${this.client.stores.emojis.error} This port is already bound to a domain. Do you wish to continue? (y/n)***`,
30000, true, ['y', 'n'], (msg) => msg.author.id === message.author.id && msg.channel.id === message.channel.id,
);
} catch (error) {
@ -80,12 +80,12 @@ export default class CWG_Create extends Command {
// @ts-ignore
await Promise.all(tasks);
const embed = new RichEmbed()
const embed = new MessageEmbed()
.setTitle('Domain Creation')
.setColor(3066993)
.addField('Account Username', `${account.username} | <@${account.userID}>`, true)
.addField('Account ID', account.id, true)
.addField('Technician', `<@${message.author.id}>`, true)
.addField('Technician', message.author.toString(), true)
.addField('Domain', domain.domain, true)
.addField('Port', String(domain.port), true);
@ -95,37 +95,37 @@ export default class CWG_Create extends Command {
embed.addField('Certificate Issuer', cert.issuer.organizationName, true)
.addField('Certificate Subject', cert.subject.commonName, true)
.setFooter(this.client.user.username, this.client.user.avatarURL)
.setTimestamp(new Date(message.timestamp));
.setFooter(this.client.user.username, this.client.user.avatarURL())
.setTimestamp(new Date(message.createdTimestamp));
const completed = [
edit.edit(`***${this.client.stores.emojis.success} Successfully binded ${domain.domain} to port ${domain.port} for ${account.username}.***`),
this.client.createMessage('580950455581147146', { embed }),
this.client.getDMChannel(account.userID).then((r) => r.createMessage({ embed })),
edit.edit(`***${this.client.stores.emojis.success} Successfully bound ${domain.domain} to port ${domain.port} for ${account.username}.***`),
(this.client.channels.cache.get('580950455581147146') as TextChannel).send({ embeds: [embed] }),
this.client.users.fetch(account.userID).then((r) => r.send({ embeds: [embed] })),
this.client.util.transport.sendMail({
to: account.emailAddress,
from: 'Library of Code sp-us | Support Team <help@libraryofcode.org>',
subject: 'Your domain has been binded',
subject: 'Your domain has been bound',
html: `
<h1>Library of Code sp-us | Cloud Services</h1>
<p>Hello, this is an email informing you that a new domain under your account has been binded.
<p>Hello, this is an email informing you that a new domain under your account has been bound.
Information is below.</p>
<b>Domain:</b> ${domain.domain}<br>
<b>Port:</b> ${domain.port}<br>
<b>Certificate Issuer:</b> ${cert.issuer.organizationName}<br>
<b>Certificate Subject:</b> ${cert.subject.commonName}<br>
<b>Responsible Engineer:</b> ${message.author.username}#${message.author.discriminator}<br><br>
If you have any questions about additional setup, you can reply to this email or send a message in #cloud-support in our Discord server.<br>
<b><i>Library of Code sp-us | Support Team</i></b>
`,
}),
];
if (!domain.domain.includes('cloud.libraryofcode.org')) {
const content = `__**DNS Record Setup**__\nYou recently a binded a custom domain to your Library of Code sp-us Account. You'll have to update your DNS records. We've provided the records below.\n\n\`${domain.domain} IN CNAME cloud.libraryofcode.org AUTO/500\`\nThis basically means you need to make a CNAME record with the key/host of ${domain.domain} and the value/point to cloud.libraryofcode.org. If you have any questions, don't hesitate to ask us.`;
completed.push(this.client.getDMChannel(account.userID).then((r) => r.createMessage(content)));
const content = `__**DNS Record Setup**__\nYou recently a bound a custom domain to your Library of Code sp-us Account. You'll have to update your DNS records. We've provided the records below.\n\n\`${domain.domain} IN CNAME cloud.libraryofcode.org AUTO/500\`\nThis basically means you need to make a CNAME record with the key/host of ${domain.domain} and the value/point to cloud.libraryofcode.org. If you have any questions, don't hesitate to ask us.`;
completed.push(this.client.users.fetch(account.userID).then((r) => r.send(content)));
}
return Promise.all(completed);
@ -194,23 +194,23 @@ export default class CWG_Create extends Command {
return { cert: `/etc/ssl/certs/cwg/${domain}.chain.crt`, key: `/etc/ssl/private/cwg/${domain}.key.pem` };
}
public checkOccurance(text: string, query: string) {
public checkOccurrence(text: string, query: string) {
return (text.match(new RegExp(query, 'g')) || []).length;
}
public isValidCertificateChain(cert: string) {
if (!cert.replace(/^\s+|\s+$/g, '').startsWith('-----BEGIN CERTIFICATE-----')) return false;
if (!cert.replace(/^\s+|\s+$/g, '').endsWith('-----END CERTIFICATE-----')) return false;
if (this.checkOccurance(cert.replace(/^\s+|\s+$/g, ''), '-----BEGIN CERTIFICATE-----') !== 2) return false;
if (this.checkOccurance(cert.replace(/^\s+|\s+$/g, ''), '-----END CERTIFICATE-----') !== 2) return false;
if (this.checkOccurrence(cert.replace(/^\s+|\s+$/g, ''), '-----BEGIN CERTIFICATE-----') !== 2) return false;
if (this.checkOccurrence(cert.replace(/^\s+|\s+$/g, ''), '-----END CERTIFICATE-----') !== 2) return false;
return true;
}
public isValidPrivateKey(key: string) {
if (!key.replace(/^\s+|\s+$/g, '').startsWith('-----BEGIN PRIVATE KEY-----') && !key.replace(/^\s+|\s+$/g, '').startsWith('-----BEGIN RSA PRIVATE KEY-----') && !key.replace(/^\s+|\s+$/g, '').startsWith('-----BEGIN ECC PRIVATE KEY-----')) return false;
if (!key.replace(/^\s+|\s+$/g, '').endsWith('-----END PRIVATE KEY-----') && !key.replace(/^\s+|\s+$/g, '').endsWith('-----END RSA PRIVATE KEY-----') && !key.replace(/^\s+|\s+$/g, '').endsWith('-----END ECC PRIVATE KEY-----')) return false;
if ((this.checkOccurance(key.replace(/^\s+|\s+$/g, ''), '-----BEGIN PRIVATE KEY-----') !== 1) && (this.checkOccurance(key.replace(/^\s+|\s+$/g, ''), '-----BEGIN RSA PRIVATE KEY-----') !== 1) && (this.checkOccurance(key.replace(/^\s+|\s+$/g, ''), '-----BEGIN ECC PRIVATE KEY-----') !== 1)) return false;
if ((this.checkOccurance(key.replace(/^\s+|\s+$/g, ''), '-----END PRIVATE KEY-----') !== 1) && (this.checkOccurance(key.replace(/^\s+|\s+$/g, ''), '-----END RSA PRIVATE KEY-----') !== 1) && (this.checkOccurance(key.replace(/^\s+|\s+$/g, ''), '-----END ECC PRIVATE KEY-----') !== 1)) return false;
if ((this.checkOccurrence(key.replace(/^\s+|\s+$/g, ''), '-----BEGIN PRIVATE KEY-----') !== 1) && (this.checkOccurrence(key.replace(/^\s+|\s+$/g, ''), '-----BEGIN RSA PRIVATE KEY-----') !== 1) && (this.checkOccurrence(key.replace(/^\s+|\s+$/g, ''), '-----BEGIN ECC PRIVATE KEY-----') !== 1)) return false;
if ((this.checkOccurrence(key.replace(/^\s+|\s+$/g, ''), '-----END PRIVATE KEY-----') !== 1) && (this.checkOccurrence(key.replace(/^\s+|\s+$/g, ''), '-----END RSA PRIVATE KEY-----') !== 1) && (this.checkOccurrence(key.replace(/^\s+|\s+$/g, ''), '-----END ECC PRIVATE KEY-----') !== 1)) return false;
return true;
}

View File

@ -1,8 +1,7 @@
import fs from 'fs';
import moment from 'moment';
import { createPaginationEmbed } from 'eris-pagination';
import { Message } from 'eris';
import { Client, Command, RichEmbed } from '../class';
import { Message, MessageEmbed } from 'discord.js';
import { Client, Command, PaginationEmbed } from '../class';
export default class CWG_Data extends Command {
constructor(client: Client) {
@ -32,7 +31,7 @@ export default class CWG_Data extends Command {
const embeds = await Promise.all(dom.map(async (domain) => {
const pem = fs.readFileSync(domain.x509.cert, { encoding: 'utf8' });
const cert = await this.client.util.parseCertificate(pem);
const embed = new RichEmbed();
const embed = new MessageEmbed();
embed.setTitle('Domain Information');
embed.addField('Account Username', domain.account.username, true);
embed.addField('Account ID', domain.account.userID, true);
@ -41,13 +40,13 @@ export default class CWG_Data extends Command {
embed.addField('Certificate Issuer', cert.data.issuer.organization[0], true);
embed.addField('Certificate Subject', cert.data.issuer.commonName, true);
embed.addField('Certificate Expiration Date', moment(cert.data.notAfter).format('dddd, MMMM Do YYYY, h:mm:ss A'), true);
embed.setFooter(this.client.user.username, this.client.user.avatarURL);
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, embeds);
if (embeds.length === 1) return message.channel.send({ embeds: [embeds[0]] });
return this.client.util.createPaginationEmbed(message, embeds);
} catch (error) {
return this.client.util.handleError(error, message, this);
}

View File

@ -1,7 +1,7 @@
import fs from 'fs-extra';
import axios from 'axios';
import { Message } from 'eris';
import { Client, Command, RichEmbed } from '../class';
import { Message, MessageEmbed, TextChannel } from 'discord.js';
import { Client, Command } from '../class';
export default class CWG_Delete extends Command {
constructor(client: Client) {
@ -20,13 +20,13 @@ export default class CWG_Delete extends Command {
const domain = await this.client.db.Domain.findOne({ $or: [{ domain: args[0] }, { port: Number(args[0]) || 0 }] });
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();
const embed = new MessageEmbed();
embed.setTitle('Domain Deletion');
embed.addField('Account Username', `${domain.account.username} | <@${domain.account.userID}>`, 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.setFooter(this.client.user.username, this.client.user.avatarURL());
embed.setTimestamp();
if (domain.domain.includes('cloud.libraryofcode.org')) {
const resultID = await axios({
@ -51,8 +51,9 @@ export default class CWG_Delete extends Command {
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(() => {});
const ch = this.client.channels.cache.get('580950455581147146') as TextChannel;
ch.send({ embeds: [embed] });
return this.client.users.fetch(domain.account.userID).then((u) => u.send({ embeds: [embed] })).catch(() => {});
} catch (error) {
return this.client.util.handleError(error, message, this);
}

View File

@ -1,6 +1,6 @@
import { writeFile, unlink } from 'fs-extra';
import axios from 'axios';
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
export default class CWG_UpdateCert extends Command {
@ -49,23 +49,23 @@ export default class CWG_UpdateCert extends Command {
}
}
public checkOccurance(text: string, query: string) {
public checkOccurrence(text: string, query: string) {
return (text.match(new RegExp(query, 'g')) || []).length;
}
public isValidCertificateChain(cert: string) {
if (!cert.replace(/^\s+|\s+$/g, '').startsWith('-----BEGIN CERTIFICATE-----')) return false;
if (!cert.replace(/^\s+|\s+$/g, '').endsWith('-----END CERTIFICATE-----')) return false;
if (this.checkOccurance(cert.replace(/^\s+|\s+$/g, ''), '-----BEGIN CERTIFICATE-----') !== 2) return false;
if (this.checkOccurance(cert.replace(/^\s+|\s+$/g, ''), '-----END CERTIFICATE-----') !== 2) return false;
if (this.checkOccurrence(cert.replace(/^\s+|\s+$/g, ''), '-----BEGIN CERTIFICATE-----') !== 2) return false;
if (this.checkOccurrence(cert.replace(/^\s+|\s+$/g, ''), '-----END CERTIFICATE-----') !== 2) return false;
return true;
}
public isValidPrivateKey(key: string) {
if (!key.replace(/^\s+|\s+$/g, '').startsWith('-----BEGIN PRIVATE KEY-----')) return false;
if (!key.replace(/^\s+|\s+$/g, '').endsWith('-----END PRIVATE KEY-----')) return false;
if (this.checkOccurance(key.replace(/^\s+|\s+$/g, ''), '-----BEGIN PRIVATE KEY-----') !== 1) return false;
if (this.checkOccurance(key.replace(/^\s+|\s+$/g, ''), '-----END PRIVATE KEY-----') !== 1) return false;
if (this.checkOccurrence(key.replace(/^\s+|\s+$/g, ''), '-----BEGIN PRIVATE KEY-----') !== 1) return false;
if (this.checkOccurrence(key.replace(/^\s+|\s+$/g, ''), '-----END PRIVATE KEY-----') !== 1) return false;
return true;
}

View File

@ -1,5 +1,5 @@
import { Message, PrivateChannel } from 'eris';
import uuid from 'uuid/v4';
import { Message } from 'discord.js';
import { v4 as uuid } from 'uuid';
import { Client, Command } from '../class';
export default class DeleteAccount extends Command {
@ -18,9 +18,9 @@ export default class DeleteAccount extends Command {
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.***`);
if (!account) return message.channel.send(`${this.client.stores.emojis.error} ***Account not found.***`);
const { root, username, userID, emailAddress, homepath } = account;
if (root) return message.channel.createMessage(`${this.client.stores.emojis.error} ***Permission denied.***`);
if (root) return message.channel.send(`${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);
@ -28,17 +28,17 @@ export default class DeleteAccount extends Command {
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));
15000, true, [verify], (msg) => !(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} ***Deleting account, please wait...***`);
const deleting = await message.channel.send(`${this.client.stores.emojis.loading} ***Deleting account, please wait...***`);
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;
await this.client.util.createModerationLog(args[0], message.member, 4, reason);
await this.client.util.createModerationLog(args[0], message.author, 4, reason);
await this.client.util.deleteAccount(username);
message.delete().catch(() => {});
@ -52,7 +52,7 @@ export default class DeleteAccount extends Command {
<p>Your Cloud Account has been deleted by a Director. If your account was deleted due to infractions, this will not be appealable. We're sorry to see you go.</p>
<p><b>Reason:</b> ${reason}</p>
<p><b>Director:</b> ${message.author.username}</p>
<b><i>Library of Code sp-us | Support Team</i></b>
`,
});

View File

@ -1,5 +1,5 @@
import { randomBytes } from 'crypto';
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
export default class EmailCode extends Command {
@ -31,7 +31,7 @@ export default class EmailCode extends Command {
<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>
</body>
`,
});
return this.success(message.channel, `Code: \`${code}\` | Email Address: ${args[0]}`);

View File

@ -1,6 +1,6 @@
/* eslint-disable no-eval */
import axios from 'axios';
import { Message } from 'eris';
import { Message } from 'discord.js';
import { inspect } from 'util';
import { Client, Command } from '../class';
@ -45,22 +45,21 @@ export default class Eval extends Command {
evaled = error.stack;
}
evaled = evaled.replace(new RegExp(this.client.config.token, 'gi'), 'juul');
evaled = evaled.replace(new RegExp(this.client.config.emailPass, 'gi'), 'juul');
evaled = evaled.replace(new RegExp(this.client.config.cloudflare, 'gi'), 'juul');
evaled = evaled.replace(new RegExp(this.client.config.token, 'gi'), 'token');
evaled = evaled.replace(new RegExp(this.client.config.emailPass, 'gi'), 'emailPass');
evaled = evaled.replace(new RegExp(this.client.config.cloudflare, 'gi'), 'cloudflare');
const display = this.client.util.splitString(evaled, 1975);
if (display[5]) {
try {
const { data } = await axios.post('https://snippets.cloud.libraryofcode.org/documents', display.join(''));
return message.channel.createMessage(`${this.client.stores.emojis.success} Your evaluation evaled can be found on https://snippets.cloud.libraryofcode.org/${data.key}`);
return message.channel.send(`${this.client.stores.emojis.success} Your evaluation evaled can be found on https://snippets.cloud.libraryofcode.org/${data.key}`);
} catch (error) {
return message.channel.createMessage(`${this.client.stores.emojis.error} ${error}`);
return message.channel.send(`${this.client.stores.emojis.error} ${error}`);
}
}
return display.forEach((m) => message.channel.createMessage(`\`\`\`js\n${m}\n\`\`\``));
return display.forEach((m) => message.channel.send(`\`\`\`js\n${m}\n\`\`\``));
} catch (error) {
return this.client.util.handleError(error, message, this);
}

View File

@ -1,4 +1,4 @@
import { Message } from 'eris';
import { Message } from 'discord.js';
import axios from 'axios';
import { Client, Command } from '../class';
@ -38,7 +38,7 @@ export default class Exec extends Command {
}
await response.delete();
return splitResult.forEach((m) => message.channel.createMessage(`\`\`\`bash\n${m}\n\`\`\``));
return splitResult.forEach((m) => message.channel.send(`\`\`\`bash\n${m}\n\`\`\``));
} catch (error) {
return this.client.util.handleError(error, message, this);
}

View File

@ -1,4 +1,4 @@
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
export default class GetReferral extends Command {
@ -16,8 +16,8 @@ export default class GetReferral extends Command {
const account = await this.client.db.Account.findOne({ userID: message.author.id });
if (!account) return this.error(message.channel, 'You do not have a Cloud Services account.');
return this.client.getDMChannel(message.author.id).then((chan) => {
chan.createMessage(`__**CS Account Referral Code**__\n*Referral codes are considered to be somewhat private information. Applicants with referral codes have a greater chance of approval, don't refer someone you don't trust :).*\nYour referral code: \`${account.referralCode}\`\nReferrals Granted: \`${account.totalReferrals ? account.totalReferrals : '0'}\``);
return this.client.users.fetch(message.author.id).then((u) => {
u.send(`__**CS Account Referral Code**__\n*Referral codes are considered to be somewhat private information. Applicants with referral codes have a greater chance of approval, don't refer someone you don't trust :).*\nYour referral code: \`${account.referralCode}\`\nReferrals Granted: \`${account.totalReferrals ? account.totalReferrals : '0'}\``);
}).catch(() => this.error(message.channel, 'Could not send you a DM.'));
} catch (err) {
return this.client.util.handleError(err, message, this);

View File

@ -1,6 +1,5 @@
import { Message } from 'eris';
import { createPaginationEmbed } from 'eris-pagination';
import { Client, Command, RichEmbed } from '../class';
import { Message, MessageEmbed } from 'discord.js';
import { Client, Command } from '../class';
export default class Help extends Command {
constructor(client: Client) {
@ -21,44 +20,45 @@ export default class Help extends Command {
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(', ');
let allowedRoles = c.permissions && c.permissions.roles && c.permissions.roles.map((r) => r.toString()).join(', ');
if (allowedRoles) { allowedRoles = `**Roles:** ${allowedRoles}`; perms.push(allowedRoles); }
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.toString()).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[] = [];
const cmdPages: MessageEmbed[] = [];
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);
const embed = new MessageEmbed();
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, cmdPages);
if (cmdPages.length === 1) return message.channel.send({ embeds: [cmdPages[0]] });
return this.client.util.createPaginationEmbed(message, cmdPages);
}
const resolved = await this.client.util.resolveCommand(args, message);
if (!resolved) return message.channel.createMessage(`${this.client.stores.emojis.error} **Command not found!**`);
if (!resolved) return message.channel.send(`${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(', ');
let allowedRoles = cmd.permissions && cmd.permissions.roles && cmd.permissions.roles.map((r) => r.toString()).join(', ');
if (allowedRoles) { allowedRoles = `**Roles:** ${allowedRoles}`; perms.push(allowedRoles); }
let allowedUsers = cmd.permissions && cmd.permissions.users && cmd.permissions.users.map((u) => `<@${u}>`).join(', ');
let allowedUsers = cmd.permissions && cmd.permissions.users && cmd.permissions.users.map((u) => u.toString()).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 embed = new MessageEmbed();
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 });
message.channel.send({ embeds: [embed] });
} catch (error) {
this.client.util.handleError(error, message, this);
}

View File

@ -1,7 +1,7 @@
import { Message } from 'eris';
import { Message, MessageEmbed } from 'discord.js';
import { totalmem } from 'os';
import { Client, Command, RichEmbed } from '../class';
import { version as erisVersion } from '../../node_modules/eris/package.json';
import { Client, Command } from '../class';
import { version as discordjsVersion } from '../../node_modules/discord.js/package.json';
import { version as expressVersion } from '../../node_modules/express/package.json';
import { version as mongooseVersion } from '../../node_modules/mongoose/package.json';
import { version as ioredisVersion } from '../../node_modules/ioredis/package.json';
@ -18,20 +18,20 @@ export default class Info extends Command {
public async run(message: Message) {
try {
const embed = new RichEmbed();
const embed = new MessageEmbed();
embed.setTitle('Information');
embed.setThumbnail(this.client.user.avatarURL);
embed.setThumbnail(this.client.user.avatarURL());
embed.addField('Language(s)', '<:ts:604565354554982401> TypeScript, <:Go:703449475405971466> Go', true);
embed.addField('Runtime', `Node (${process.version})`, true);
embed.addField('Compilers/Transpilers', `TypeScript [tsc] (${tscVersion}) | Go [gc] (${await this.client.util.exec('go version')})`, true);
embed.addField('Process Manager', `SystemD (${(await this.client.util.exec('systemd --version')).split('\n')[0]})`, true);
embed.addField('Discord Library', `Eris (${erisVersion})`, true);
embed.addField('Discord Library', `Discord.js (${discordjsVersion})`, true);
embed.addField('HTTP Server Library', `Express (${expressVersion})`, true);
embed.addField('Database Library', `MongoDB w/ Mongoose ODM (${mongooseVersion})`, true);
embed.addField('Cache Library', `Redis w/ IORedis (${ioredisVersion})`, true);
embed.addField('Memory Usage', `${Math.round(process.memoryUsage().rss / 1024 / 1024)} MB / ${Math.round(totalmem() / 1024 / 1024 / 1024)} GB`, true);
embed.addField('Repository', 'https://loc.sh/csdgit | Licensed under GNU Affero General Public License V3', true);
return message.channel.createMessage({ embed });
return message.channel.send({ embeds: [embed] });
} catch (err) {
return this.client.util.handleError(err, message, this);
}

View File

@ -1,5 +1,5 @@
import { Message } from 'eris';
import { Client, Command, RichEmbed } from '../class';
import { Message, MessageEmbed } from 'discord.js';
import { Client, Command } from '../class';
import { dataConversion } from '../functions';
import setRamNotification from './limits_setramnotification';
@ -16,7 +16,7 @@ export default class Limits extends Command {
public async run(message: Message) {
try {
const tiers = await this.client.db.Tier.find();
const embed = new RichEmbed();
const embed = new MessageEmbed();
embed.setTitle('Resource Limit Information');
const account = await this.client.db.Account.findOne({ userID: message.author.id });
if (account) {
@ -29,12 +29,12 @@ export default class Limits extends Command {
}
embed.setDescription(`Your resource limit is ${dataConversion(tier.resourceLimits?.ram * 1024 * 1024) ?? '0 B'}.\n${msg}`);
}
embed.setFooter(this.client.user.username, this.client.user.avatarURL);
embed.setFooter(this.client.user.username, this.client.user.avatarURL());
embed.setTimestamp();
for (const tier of tiers.sort((a, b) => a.id - b.id)) {
embed.addField(`Tier ${tier.id}`, `**RAM:** ${dataConversion(tier.resourceLimits?.ram * 1024 * 1024) ?? '0 B'}\n**Storage:** ${dataConversion(tier.resourceLimits?.storage * 1024 * 1024) ?? '0 B'}`);
}
return message.channel.createMessage({ embed });
return message.channel.send({ embeds: [embed] });
} catch (error) {
return this.client.util.handleError(error, message, this);
}

View File

@ -1,11 +1,11 @@
import { Message } from 'eris';
import { Message, TextChannel } from 'discord.js';
import { Client, Command } from '../class';
export default class Limits_SetRAMNotification extends Command {
constructor(client: Client) {
super(client);
this.name = 'set-ram-notification';
this.description = 'Sets your personal perference for receiving RAM resource limit notifications. Set the limit to "-1" to disable notifications.';
this.description = 'Sets your personal preference for receiving RAM resource limit notifications. Set the limit to "-1" to disable notifications.';
this.usage = `${this.client.config.prefix}limits set-ram-notification <limit in mb>`;
this.enabled = true;
}
@ -13,16 +13,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 this.error(message.channel, 'You do not appear to have an account.');
if (!account) return this.error(message.channel as TextChannel, '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 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]) >= tier.resourceLimits.ram) return this.error(message.channel as TextChannel, '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 as TextChannel, 'You cannot set your notification limit to a negative number.');
if (Number(args[0]) === 0) {
await account.updateOne({ $set: { ramLimitNotification: 0 } });
return this.success(message.channel, 'You have disabled notifications.');
return this.success(message.channel as TextChannel, 'You have disabled notifications.');
}
await account.updateOne({ $set: { ramLimitNotification: Number(args[0]) } });
return this.success(message.channel, `You will now receive notifications when you go above ${Number(args[0])} MB.`);
return this.success(message.channel as TextChannel, `You will now receive notifications when you go above ${Number(args[0])} MB.`);
} catch (error) {
return this.client.util.handleError(error, message, this);
}

View File

@ -1,4 +1,4 @@
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
export default class Load extends Command {

View File

@ -1,5 +1,5 @@
import moment, { unitOfTime } from 'moment';
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
export default class Lock extends Command {
@ -29,7 +29,7 @@ export default class Lock extends Command {
const momentMilliseconds = moment.duration(Number(length), unit as unitOfTime.Base).asMilliseconds();
const reason = momentMilliseconds ? args.slice(2).join(' ') : args.slice(1).join(' ');
await this.client.util.createModerationLog(account.userID, message.member, 2, reason, momentMilliseconds);
await this.client.util.createModerationLog(account.userID, message.author, 2, reason, momentMilliseconds);
edit.edit(`***${this.client.stores.emojis.success} Account ${account.username} has been locked by Technician ${message.author.username}#${message.author.discriminator}.***`);
message.delete();
@ -44,7 +44,7 @@ export default class Lock extends Command {
<p><b>Reason:</b> ${momentMilliseconds ? args.slice(2).join(' ') : args.slice(1).join(' ')}</p>
<p><b>Technician:</b> ${message.author.username}</p>
<p><b>Expiration:</b> ${momentMilliseconds ? moment(expiry).format('dddd, MMMM Do YYYY, h:mm:ss A') : 'N/A'}</p>
<b><i>Library of Code sp-us | Support Team</i></b>
`,
});

View File

@ -1,6 +1,5 @@
import { Message } from 'eris';
import { createPaginationEmbed } from 'eris-pagination';
import { Client, Command, RichEmbed } from '../class';
import { Message, MessageEmbed } from 'discord.js';
import { Client, Command } from '../class';
export default class Modlogs extends Command {
constructor(client: Client) {
@ -35,16 +34,16 @@ export default class Modlogs extends Command {
const inline = true;
return { name, value, inline };
}));
const users = [...new Set(query.map((log) => log.userID))].map((u) => `<@${u}>`);
const users = [...new Set(query.map((log) => log.userID))].map((u) => u.toString());
const logs = this.client.util.splitFields(formatted);
const embeds = logs.map((l) => {
const embed = new RichEmbed();
const embed = new MessageEmbed();
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.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);
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);
@ -52,9 +51,9 @@ export default class Modlogs extends Command {
});
if (embeds.length === 1) {
msg.edit({ content: '', embed: embeds[0] });
msg.edit({ content: '', embeds: [embeds[0]] });
} else {
createPaginationEmbed(message, embeds, {});
this.client.util.createPaginationEmbed(message, embeds);
}
return msg;
} catch (error) {

View File

@ -1,5 +1,5 @@
import { Message } from 'eris';
import { Client, Command, RichEmbed } from '../class';
import { Message, MessageEmbed, TextChannel } from 'discord.js';
import { Client, Command } from '../class';
export default class Notify extends Command {
constructor(client: Client) {
@ -17,17 +17,18 @@ export default class Notify extends Command {
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()
const embed = new MessageEmbed()
.setTitle('Cloud Account | Notification')
.setDescription(args.slice(1).join(' '))
.addField('Technician', `<@${message.author.id}>`, true)
.setFooter(this.client.user.username, this.client.user.avatarURL)
.addField('Technician', message.author.toString(), true)
.setFooter(this.client.user.username, this.client.user.avatarURL())
.setTimestamp();
this.client.getDMChannel(account.userID).then((channel) => {
channel.createMessage({ embed });
this.client.users.fetch(account.userID).then((u) => {
u.send({ embeds: [embed] });
});
embed.addField('User', `${account.username} | <@${account.userID}>`, true);
this.client.createMessage('580950455581147146', { embed });
const ch = this.client.channels.cache.get('580950455581147146') as TextChannel;
ch.send({ embeds: [embed] });
this.client.util.transport.sendMail({
to: account.emailAddress,
from: 'Library of Code sp-us | Cloud Services <help@libraryofcode.org>',

View File

@ -1,4 +1,4 @@
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
export default class Ping extends Command {
@ -12,8 +12,8 @@ export default class Ping extends Command {
public async run(message: Message) {
try {
const clientStart: number = Date.now();
const msg: Message = await message.channel.createMessage('🏓 Pong!');
msg.edit(`🏓 Pong!\nClient: \`${Date.now() - clientStart}ms\`\nResponse: \`${msg.createdAt - message.createdAt}ms\``);
const msg: Message = await message.channel.send('🏓 Pong!');
msg.edit(`🏓 Pong!\nClient: \`${Date.now() - clientStart}ms\`\nResponse: \`${msg.createdTimestamp - message.createdTimestamp}ms\``);
} catch (error) {
this.client.util.handleError(error, message, this);
}

View File

@ -1,4 +1,4 @@
import { Message } from 'eris';
import { Message } from 'discord.js';
import axios from 'axios';
import { Client, Command } from '../class';
@ -41,7 +41,6 @@ export default class Pull extends Command {
+ `***${this.client.stores.emojis.loading} Reinstalling dependencies...***`);
const passedPull = await updateMessage.edit(continueMessage);
let install: string;
try {
install = await this.client.util.exec('yarn install', { cwd: '/opt/CloudServices' });
@ -76,7 +75,7 @@ export default class Pull extends Command {
updatedMessage += `${this.client.stores.emojis.error} Could not upload error: ${e}`;
}
} else {
split.forEach((m) => message.channel.createMessage(`\`\`\`bash\n${m}\n\`\`\``));
split.forEach((m) => message.channel.send(`\`\`\`bash\n${m}\n\`\`\``));
}
}
this.client.buildError = true;

View File

@ -1,4 +1,4 @@
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
export default class ResetPassword extends Command {
@ -25,9 +25,9 @@ export default class ResetPassword extends Command {
await this.client.util.exec(`echo '${account.username}:${tempPass}@' | chpasswd && chage -d0 ${account.username}`);
let completeMessage = `${this.client.stores.emojis.success} ***Password for ${account.username} reset to \`${tempPass}@\`***`;
const dmChannel = await this.client.getDMChannel(account.userID);
const dmChannel = await this.client.users.fetch(account.userID);
try {
await dmChannel.createMessage(`We received a password reset request from you, your new password is \`${tempPass}@\`.\n`
await dmChannel.send(`We received a password reset request from you, your new password is \`${tempPass}@\`.\n`
+ `You will be asked to change your password when you log back in, \`(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.');
} catch (error) {

View File

@ -1,4 +1,4 @@
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
export default class Restart extends Command {

View File

@ -1,4 +1,4 @@
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
import SetLimit_RAM from './setlimit_ram';

View File

@ -1,4 +1,4 @@
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
export default class SetLimit_RAM extends Command {

View File

@ -1,7 +1,7 @@
import moment from 'moment';
import { Message } from 'eris';
import os, { totalmem } from 'os';
import { Client, Command, RichEmbed } from '../class';
import { Message, MessageEmbed } from 'discord.js';
import os from 'os';
import { Client, Command } from '../class';
import { dataConversion } from '../functions';
export default class SysInfo extends Command {
@ -14,21 +14,21 @@ export default class SysInfo extends Command {
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 usedMemory = dataConversion(os.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();
const embed = new MessageEmbed();
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('Memory/RAM', `${usedMemory} / ${dataConversion(os.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.setFooter(this.client.user.username, this.client.user.avatarURL);
embed.setFooter(this.client.user.username, this.client.user.avatarURL());
embed.setTimestamp();
message.channel.createMessage({ embed });
message.channel.send({ embeds: [embed] });
}
}

View File

@ -1,4 +1,4 @@
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
import SystemD_Linger from './systemd_linger';
import SystemD_Status from './systemd_status';

View File

@ -1,5 +1,5 @@
/* eslint-disable consistent-return */
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
export default class SystemdD_Linger extends Command {

View File

@ -1,4 +1,4 @@
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
export default class SystemD_Restart extends Command {

View File

@ -1,4 +1,4 @@
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
export default class SystemD_Start extends Command {

View File

@ -1,5 +1,5 @@
import { Message } from 'eris';
import { Client, Command, RichEmbed } from '../class';
import { Message, MessageEmbed } from 'discord.js';
import { Client, Command } from '../class';
export default class SystemD_Status extends Command {
constructor(client: Client) {
@ -22,12 +22,12 @@ export default class SystemD_Status extends Command {
if (err.toString().includes('could not be found')) return this.error(message.channel, 'The service name you provided doesn\'t exist.');
return this.error(message.channel, err.toString());
}
const embed = new RichEmbed();
const embed = new MessageEmbed();
embed.setTitle(`SystemD Status | ${args[0]}`);
embed.setDescription(`\`\`\`sh\n${cmd}\n\`\`\``);
embed.setFooter(this.client.user.username, this.client.user.avatarURL);
embed.setFooter(this.client.user.username, this.client.user.avatarURL());
embed.setTimestamp();
return message.channel.createMessage({ embed });
return message.channel.send({ embeds: [embed] });
} catch (err) {
return this.client.util.handleError(err, message, this);
}

View File

@ -1,4 +1,4 @@
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
export default class SystemD_Stop extends Command {

View File

@ -1,5 +1,5 @@
import { Message } from 'eris';
import { Client, Command, RichEmbed } from '../class';
import { Message, MessageEmbed, TextChannel } from 'discord.js';
import { Client, Command } from '../class';
export default class Tier extends Command {
constructor(client: Client) {
@ -28,16 +28,18 @@ export default class Tier extends Command {
await account.updateOne({ $set: { tier: Number(args[1]) } });
}
edit.edit(`***${this.client.stores.emojis.success} Tier for ${account.username} has been changed to ${args[1]}.***`);
const embed = new RichEmbed();
const embed = new MessageEmbed();
embed.setTitle('Cloud Account | Tier Change');
embed.setColor('#0099ff');
embed.addField('User', `${account.username} | <@${account.userID}>`, true);
embed.addField('Technician', `<@${message.author.id}>`, true);
embed.addField('Technician', message.author.toString(), true);
embed.addField('Old Tier -> New Tier', `${account.tier} -> ${args[1]}`, true);
embed.setFooter(this.client.user.username, this.client.user.avatarURL);
embed.setFooter(this.client.user.username, this.client.user.avatarURL());
embed.setTimestamp();
await this.client.util.sendMessageToUserTerminal(account.username, `A technician has changed your tier to ${args[1]}`).catch(() => { });
this.client.createMessage('580950455581147146', { embed }); return this.client.getDMChannel(account.userID).then((channel) => channel.createMessage({ embed })).catch();
const ch = this.client.channels.cache.get('580950455581147146') as TextChannel;
ch.send({ embeds: [embed] });
return this.client.users.fetch(account.userID).then((u) => u.send({ embeds: [embed] })).catch();
} catch (error) {
return this.client.util.handleError(error, message, this);
}

View File

@ -1,4 +1,4 @@
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
export default class Unban extends Command {
@ -16,7 +16,7 @@ export default class Unban extends Command {
public async run(message: Message, args: string[]) {
try {
if (!args[1]) return this.client.commands.get('help').run(message, [this.name]);
const msg = await message.channel.createMessage(`${this.client.stores.emojis.loading} ***Unbanning IP...***`);
const msg = await message.channel.send(`${this.client.stores.emojis.loading} ***Unbanning IP...***`);
try {
await this.client.util.exec(`sudo fail2ban-client set ${args[0]} unbanip ${args[1]}`);
} catch (error) {

View File

@ -1,4 +1,4 @@
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
export default class Unlock extends Command {
@ -21,7 +21,7 @@ export default class Unlock extends Command {
await this.client.util.exec(`unlock ${account.username}`);
await account.updateOne({ locked: false });
await this.client.util.createModerationLog(account.userID, message.member, 3, args.slice(1).join(' '));
await this.client.util.createModerationLog(account.userID, message.author, 3, args.slice(1).join(' '));
message.delete();
edit.edit(`***${this.client.stores.emojis.success} Account ${account.username} has been unlocked by Technician ${message.author.username}#${message.author.discriminator}.***`);
} catch (error) {

View File

@ -1,7 +1,6 @@
/* eslint-disable no-await-in-loop */
import { Message } from 'eris';
import { createPaginationEmbed } from 'eris-pagination';
import { Client, Command, RichEmbed } from '../class';
import { Message, MessageEmbed } from 'discord.js';
import { Client, Command } from '../class';
export default class Users extends Command {
constructor(client: Client) {
@ -47,10 +46,10 @@ export default class Users extends Command {
const data = this.client.util.splitFields(embedFields);
const embeds = data.map((l) => {
const embed = new RichEmbed();
embed.setAuthor('Library of Code | Cloud Services', this.client.user.avatarURL, 'https://libraryofcode.org/');
const embed = new MessageEmbed();
embed.setAuthor('Library of Code | Cloud Services', this.client.user.avatarURL(), 'https://libraryofcode.org/');
embed.setTitle('Cloud Accounts');
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, true));
embed.setTimestamp();
embed.setColor(3447003);
@ -58,10 +57,10 @@ export default class Users extends Command {
});
if (embeds.length === 1) {
msg.edit({ content: '', embed: embeds[0] });
msg.edit({ content: '', embeds: [embeds[0]] });
} else {
msg.delete();
createPaginationEmbed(message, embeds, {});
this.client.util.createPaginationEmbed(message, embeds);
}
return msg;
} catch (error) {

View File

@ -1,4 +1,4 @@
import { Message } from 'eris';
import { Message } from 'discord.js';
import { Client, Command } from '../class';
export default class Warn extends Command {
@ -18,7 +18,7 @@ export default class Warn extends Command {
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.***`);
await this.client.util.createModerationLog(account.userID, message.member, 1, args.slice(1).join(' '));
await this.client.util.createModerationLog(account.userID, message.author, 1, args.slice(1).join(' '));
message.delete();
edit.edit(`***${this.client.stores.emojis.success} Account ${account.username} has been warned by Technician ${message.author.username}#${message.author.discriminator}.***`);
await this.client.util.sendMessageToUserTerminal(account.username, `WARNING FROM TECHNICIAN: ${args.slice(1).join(' ')}`).catch(() => { });

View File

@ -1,6 +1,6 @@
import moment from 'moment';
import { Message, GuildTextableChannel, Member, Role } from 'eris';
import { Client, Command, Report, RichEmbed } from '../class';
import { Message, TextChannel, Role, MessageEmbed, GuildMember } from 'discord.js';
import { Client, Command } from '../class';
import { dataConversion } from '../functions';
import { AccountInterface } from '../models';
@ -16,22 +16,26 @@ export default class Whois extends Command {
public fullRoles = ['662163685439045632', '701454780828221450'];
public IP_REGEX = /((^\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
public IP_REGEX = /((^\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;
public async run(message: Message<GuildTextableChannel>, args: string[]) {
public IP_V4_REGEX = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/
public IP_V6_REGEX = /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/
public async run(message: Message, args: string[]) {
try {
let full = false;
let account: AccountInterface;
if (args[1] === '--full' && this.fullRoles.some((r) => message.member.roles.includes(r) || message.author.id === '554168666938277889')) full = true;
if (args[1] === '--full' && this.fullRoles.some((r) => message.member.roles.cache.has(r) || message.author.id === '554168666938277889')) full = true;
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() }, { referralCode: args[0] }] });
else account = await this.client.db.Account.findOne({ $or: [{ username: user }, { userID: user }] });
if (!account) return this.error(message.channel, 'Account not found.');
if (!account) return this.error(message.channel as TextChannel, 'Account not found.');
const thumbnail = this.client.users.get(account.userID)?.avatarURL || message.channel.guild.iconURL;
const thumbnail = (await this.client.users.fetch(account.userID))?.avatarURL() || message.guild.iconURL();
const embed = new RichEmbed();
const embed = new MessageEmbed();
embed.setTitle('Account Information');
embed.setThumbnail(thumbnail);
if (full) await this.full(account, embed, message.member);
@ -43,18 +47,18 @@ export default class Whois extends Command {
switch (true) {
case account.permissions.director:
details += 'This account belongs to a Director.\n';
role = message.member.guild.roles.get('662163685439045632');
role = await message.member.guild.roles.fetch('662163685439045632');
break;
case account.permissions.technician:
details += 'This account belongs to a Technician.\n';
role = message.member.guild.roles.get('701454780828221450');
role = await message.member.guild.roles.fetch('701454780828221450');
break;
case account.permissions.staff:
details += 'This account belongs to a Staff member.\n';
role = message.member.guild.roles.get('453689940140883988');
role = await message.member.guild.roles.fetch('453689940140883988');
break;
default:
role = message.member.guild.roles.get(message.member.guild.id);
role = await message.member.guild.roles.fetch(message.member.guild.id);
break;
}
if (account.root) details += '**This account has root/administrative privileges.**\n';
@ -62,13 +66,13 @@ export default class Whois extends Command {
if (details) embed.addField('Additional Details', details, true);
embed.setTimestamp();
return message.channel.createMessage({ embed });
return message.channel.send({ embeds: [embed] });
} catch (error) {
return this.client.util.handleError(error, message, this);
}
}
public async full(account: AccountInterface, embed: RichEmbed, member: Member) {
public async full(account: AccountInterface, embed: MessageEmbed, member: GuildMember) {
const [cpuUsage, data, fingerInformation, chage, memory] = await Promise.all([
this.client.util.exec(`top -b -n 1 -u ${account.username} | awk 'NR>7 { sum += $9; } END { print sum; }'`),
this.client.redis.get(`storage-${account.username}`),
@ -76,7 +80,7 @@ export default class Whois extends Command {
this.client.util.exec(`chage -l ${account.username}`),
this.client.util.exec(`memory ${account.username}`),
]);
const finger = !member.roles.includes('662163685439045632') ? fingerInformation.replace(this.IP_REGEX, '[MASKED IP ADDRRESS]') : fingerInformation;
const finger = !member.roles.cache.has('662163685439045632') ? fingerInformation.replace(this.IP_REGEX, '[MASKED IP ADDRESS]') : fingerInformation;
embed.setDescription(`${finger}\n${chage}`);
embed.addField('Username', `${account.username} | <@${account.userID}>`, true);
@ -85,14 +89,16 @@ export default class Whois extends Command {
embed.addField('Tier', String(account.tier), true);
embed.addField('Support Key', account.supportKey, true);
embed.addField('Referral Code & Total', `${account.referralCode} | ${account.totalReferrals}`, true);
embed.addField('Created By', this.client.users.get(account.createdBy) ? `<@${this.client.users.get(account.createdBy).id}>` : 'SYSTEM', true);
embed.addField('Created By', await this.client.users.fetch(account.createdBy)
? (await this.client.users.fetch(account.createdBy)).toString()
: 'SYSTEM', true);
embed.addField('Created At', moment(account.createdAt).format('dddd, MMMM Do YYYY, h:mm:ss A'), true);
embed.addField('CPU Usage', `${cpuUsage.split('\n')[0] || '0'}%`, true);
embed.addField('Memory', dataConversion(Number(memory) * 1000), true);
embed.addField('Storage', data ? dataConversion(Number(data)) : 'N/A', true);
}
public async default(account: AccountInterface, embed: RichEmbed) {
public async default(account: AccountInterface, embed: MessageEmbed) {
const [cpuUsage, data, memory] = await Promise.all([
this.client.util.exec(`top -b -n 1 -u ${account.username} | awk 'NR>7 { sum += $9; } END { print sum; }'`),
this.client.redis.get(`storage-${account.username}`),
@ -102,7 +108,9 @@ export default class Whois extends Command {
embed.addField('Username', `${account.username} | <@${account.userID}>`, true);
embed.addField('ID', account.userID, true);
embed.addField('Tier', String(account.tier), true);
embed.addField('Created By', this.client.users.get(account.createdBy) ? `<@${this.client.users.get(account.createdBy).id}>` : 'SYSTEM', true);
embed.addField('Created By', await this.client.users.fetch(account.createdBy)
? (await this.client.users.fetch(account.createdBy)).toString()
: 'SYSTEM', true);
embed.addField('Created At', moment(account.createdAt).format('dddd, MMMM Do YYYY, h:mm:ss A'), true);
embed.addField('CPU Usage', `${cpuUsage.split('\n')[0] || '0'}%`, true);
embed.addField('Memory', dataConversion(Number(memory) * 1000), true);

View File

@ -1,5 +1,5 @@
import { Message, TextChannel } from 'eris';
import { Client, Event, RichEmbed } from '../class';
import { Message, TextChannel, MessageEmbed } from 'discord.js';
import { Client, Event } from '../class';
export default class extends Event {
public client: Client
@ -10,9 +10,6 @@ export default class extends Event {
this.event = 'messageCreate';
}
public info(message: Message) {
}
public async run(message: Message) {
try {
if (message.author.bot && message.author.id !== '554168666938277889') return;
@ -28,8 +25,9 @@ export default class extends Event {
let hasRolePerms: boolean = false;
if (resolved.cmd.permissions.roles) {
for (const role of resolved.cmd.permissions.roles) {
if (message.member && message.member.roles.includes(role)) {
hasRolePerms = true; break;
if (message.member && message.member.roles.cache.has(role)) {
hasRolePerms = true;
break;
}
}
}
@ -37,9 +35,15 @@ export default class extends Event {
hasUserPerms = true;
hasRolePerms = true;
}
if (message.author.id === '554168666938277889') { hasUserPerms = true; hasRolePerms = true; }
if (message.author.id === '554168666938277889') {
hasUserPerms = true;
hasRolePerms = true;
}
if (!hasRolePerms && !hasUserPerms) return;
if (!resolved.cmd.enabled) { message.channel.createMessage(`***${this.client.stores.emojis.error} This command has been disabled***`); return; }
if (!resolved.cmd.enabled) {
message.channel.send(`***${this.client.stores.emojis.error} This command has been disabled***`);
return;
}
await resolved.cmd.run(message, resolved.args);
} catch (error) {
this.client.util.handleError(error, message);

View File

@ -74,7 +74,7 @@ func handler(config ConfigStruct) {
collection := mongoClient.Database("cloudservices").Collection("accounts")
cur, err := collection.Find(context.TODO(), bson.D{})
HandleError(err, 0)
for cur.Next(context.TODO()) {
checkAccountSizeAndUpdate(cur.Current.Lookup("username").String(), cur.Current.Lookup("id").String())
fmt.Printf("Checking account information for %s\n", cur.Current.Lookup("username").String())

View File

@ -1,60 +1,62 @@
/* eslint-disable no-await-in-loop */
import { Client, RichEmbed } from '../class';
import { MessageEmbed, TextChannel } from 'discord.js';
import { Client } from '../class';
export default function checkStaffStatus(client: Client) {
setInterval(async () => {
const accounts = await client.db.Account.find();
for (const acc of accounts) {
const tier3 = await client.db.Tier.findOne({ id: 3 });
let user = client.guilds.get('446067825673633794').members.get(acc.userID);
let user = client.guilds.cache.get('446067825673633794').members.cache.get(acc.userID);
try {
if (!user) user = await client.guilds.get('446067825673633794').getRESTMember(acc.userID);
if (!user) user = await client.guilds.cache.get('446067825673633794').members.fetch(acc.userID);
} catch (error) {
continue; // eslint-disable-line no-continue
}
if (!acc.permissions.director && user.roles.includes('662163685439045632')) {
if (!acc.permissions.director && user.roles.cache.has('662163685439045632')) {
await client.db.Account.updateOne({ username: acc.username }, { $set: { 'permissions.director': true } });
if (acc.ramLimitNotification !== -1) {
await client.db.Account.updateOne({ username: acc.username }, { $set: { ramLimitNotification: tier3.resourceLimits.ram - 20 } });
}
}
if (!acc.permissions.technician && user.roles.includes('701454780828221450')) {
if (!acc.permissions.technician && user.roles.cache.has('701454780828221450')) {
await client.db.Account.updateOne({ username: acc.username }, { $set: { 'permissions.technician': true } });
if (acc.ramLimitNotification !== -1) {
await client.db.Account.updateOne({ username: acc.username }, { $set: { ramLimitNotification: tier3.resourceLimits.ram - 20 } });
}
}
if (!acc.permissions.staff && user.roles.includes('446104438969466890')) {
if (!acc.permissions.staff && user.roles.cache.has('446104438969466890')) {
await client.db.Account.updateOne({ username: acc.username }, { $set: { 'permissions.staff': true } });
if (acc.ramLimitNotification !== -1) {
await client.db.Account.updateOne({ username: acc.username }, { $set: { ramLimitNotification: tier3.resourceLimits.ram - 20 } });
}
}
if (acc.permissions.director && !user.roles.includes('662163685439045632')) {
if (acc.permissions.director && !user.roles.cache.has('662163685439045632')) {
await client.db.Account.updateOne({ username: acc.username }, { $set: { 'permissions.director': false } });
}
if (acc.permissions.technician && !user.roles.includes('701454780828221450')) {
if (acc.permissions.technician && !user.roles.cache.has('701454780828221450')) {
await client.db.Account.updateOne({ username: acc.username }, { $set: { 'permissions.technician': false } });
}
if (acc.permissions.staff && !user.roles.includes('446104438969466890')) {
if (acc.permissions.staff && !user.roles.cache.has('446104438969466890')) {
await client.db.Account.updateOne({ username: acc.username }, { $set: { 'permissions.staff': false } });
}
if (acc.permissions.staff && acc.tier < 3) {
await client.db.Account.updateOne({ username: acc.username }, { $set: { tier: 3 } });
const embed = new RichEmbed();
const embed = new MessageEmbed();
embed.setTitle('Cloud Account | Tier Change');
embed.setColor('#0099ff');
embed.addField('User', `${acc.username} | <@${acc.userID}>`, true);
embed.addField('Moderator', `<@${client.user.id}>`, true);
embed.addField('Moderator', client.user.toString(), true);
embed.addField('Old Tier -> New Tier', `${acc.tier} -> 3`, true);
embed.setFooter(client.user.username, client.user.avatarURL);
embed.setFooter(client.user.username, client.user.avatarURL());
embed.setTimestamp();
client.createMessage('580950455581147146', { embed });
client.getDMChannel(acc.userID).then((chan) => {
chan.createMessage('***Your account has automatically been upgraded to Tier 3 since you are a Staff member.***');
const ch = await client.channels.fetch('580950455581147146') as TextChannel;
ch.send({ embeds: [embed] });
client.users.fetch(acc.userID).then((chan) => {
chan.send('***Your account has automatically been upgraded to Tier 3 since you are a Staff member.***');
});
}
}

View File

@ -1,7 +1,8 @@
/* eslint-disable no-useless-escape */
/* eslint-disable no-continue */
/* eslint-disable no-await-in-loop */
import { Client, RichEmbed } from '../class';
import { MessageEmbed, TextChannel } from 'discord.js';
import { Client } from '../class';
import { Tiers } from '../models';
const channelID = '691824484230889546';
@ -32,15 +33,16 @@ export default function memory(client: Client) {
client.signale.info(`RAM Hard Limit Reached | ${acc.username} | ${memoryConversion}/${userLimits.hard} MB`);
await client.util.sendMessageToUserTerminal(acc.username, 'REACHED RAM LIMIT; SENDING KILL SIGNAL');
client.util.exec(`killall -9 -u ${acc.username}`);
const embed = new RichEmbed();
const embed = new MessageEmbed();
embed.setTitle('Resource Enforcement Notification');
embed.setDescription('Someone has reached the (hard) resource limit for their tier on RAM. The system has automatically killed all of their processes.');
embed.addField('User', `${acc.username} | <@${acc.userID}> | ${acc.userID}`, true);
embed.addField('Tier', String(acc.tier), true);
embed.addField('Memory Usage', `${String(memoryConversion)} MB`, true);
embed.addField('Memory Limit', `${String(userLimits.hard)} MB`, true);
client.createMessage(channelID, { embed });
client.util.createModerationLog(acc.userID, client.guilds.get('446067825673633794').members.get(client.user.id), 1, `You have exceeded your resource limit of '${String(userLimits.hard)} MB'. Any process running on your user account has been sent a STOP/KILL signal. If you have any questions, please contact a Technician.`);
const ch = client.channels.cache.get(channelID) as TextChannel;
ch.send({ embeds: [embed] });
client.util.createModerationLog(acc.userID, (await (await client.guilds.fetch('446067825673633794')).members.fetch(client.user.id)).user, 1, `You have exceeded your resource limit of '${String(userLimits.hard)} MB'. Any process running on your user account has been sent a STOP/KILL signal. If you have any questions, please contact a Technician.`);
client.util.transport.sendMail({
to: acc.emailAddress,
from: 'Library of Code sp-us | Cloud Services <help@libraryofcode.org>',
@ -50,38 +52,40 @@ export default function memory(client: Client) {
<p>Your account has received an official warning from a Technician. Please get the underlying issue resolved to avoid <i>possible</i> moderative action.</p>
<p><strong>Reason:</strong> You have exceeded your resource limit of '${String(userLimits.hard)} MB'. Any process running on your user account has been sent a STOP/KILL signal. If you have any questions, please contact a Technician.</p>
<p><strong>Moderator:</strong> SYSTEM</p>
<b><i>Library of Code sp-us | Support Team</i></b>
`,
});
client.createMessage(channelID, { embed });
ch.send({ embeds: [embed] });
set.delete(acc.username);
} else if ((memoryConversion >= userLimits.soft) && !set.has(acc.username)) {
client.signale.info(`RAM Soft Limit Reached | ${acc.username} | ${memoryConversion}/${userLimits.soft} MB`);
const embed = new RichEmbed();
if (client.users.get(acc.userID)) embed.setThumbnail(client.users.get(acc.userID).avatarURL);
const embed = new MessageEmbed();
const user = await client.users.fetch(acc.userID);
if (user) embed.setThumbnail(user.avatarURL());
embed.setTitle('Resource Limit Notification');
embed.setDescription('Someone has reached the (soft) resource limit for their tier on RAM.');
embed.addField('User', `${acc.username} | <@${acc.userID}> | ${acc.userID}`, true);
embed.addField('Tier', String(acc.tier), true);
embed.addField('Memory Usage', `${String(memoryConversion)} MB`, true);
embed.addField('Memory Limit', `${String(userLimits.hard)} MB`, true);
embed.setFooter(client.user.username, client.user.avatarURL);
embed.setFooter(client.user.username, client.user.avatarURL());
embed.setTimestamp();
if (acc.ramLimitNotification !== 0) {
await client.createMessage(channelID, { embed });
const ch = client.channels.cache.get(channelID) as TextChannel;
await ch.send({ embeds: [embed] });
}
if ((memoryConversion >= acc.ramLimitNotification) && (acc.ramLimitNotification !== 0)) {
const notifyEmbed = new RichEmbed()
const notifyEmbed = new MessageEmbed()
.setTitle('Cloud Account | Notification')
.setDescription(`You are about to reach your RAM resource limits, you are currently using '${String(Math.round(memoryConversion))} MB' and your limit is '${String(userLimits.hard)} MB'. Please correct your usage to avoid further action.`)
.addField('User', `${acc.username} | <@${acc.userID}>`, true)
.addField('Technician', 'SYSTEM', true)
.addField('Additional Information', 'This notification was sent by the system. You can set your notification preferences by running \`=limits set-ram-notification <preferred ram threshold in MB>\`, you can disable these notifications by running \`=limits set-ram-notification -1\`.')
.setFooter(client.user.username, client.user.avatarURL)
.setFooter(client.user.username, client.user.avatarURL())
.setTimestamp();
client.getDMChannel(acc.userID).then((channel) => {
channel.createMessage({ embed: notifyEmbed });
client.users.fetch(acc.userID).then((u) => {
u.send({ embeds: [notifyEmbed] });
});
await client.util.sendMessageToUserTerminal(acc.username, `You are about to reach your RAM resource limits, you are currently using '${String(Math.round(memoryConversion))} MB' and your limit is '${String(userLimits.hard)} MB'. Please correct your usage to avoid further action.`).catch(() => { });
client.util.transport.sendMail({
@ -92,11 +96,12 @@ export default function memory(client: Client) {
<h1>Library of Code sp-us | Cloud Services</h1>
<p>You are about to reach your RAM resource limits, you are currently using '${String(memoryConversion)} MB' and your limit is '${String(userLimits.hard)} MB'. Please correct your usage to avoid further action.</p>
<p><strong>Technician:</strong> SYSTEM</p>
<b><i>Library of Code sp-us | Support Team</i></b>
`,
});
client.createMessage('580950455581147146', { embed: notifyEmbed });
const channel = client.channels.cache.get('580950455581147146') as TextChannel;
channel.send({ embeds: [notifyEmbed] });
}
set.add(acc.username);
}

View File

@ -1,4 +1,5 @@
/* eslint-disable no-await-in-loop */
/* eslint-disable no-unreachable */
// import fs from 'fs-extra';
import { spawn } from 'child_process';
import { Client } from '../class';

View File

@ -1,65 +1,61 @@
{
"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": ["ES2019.Object", "ES2020.Promise"], /* 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": ["./types"], /* 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'. */
"skipLibCheck": true, /* Skips type-checking the installed libraries in node_modules. */
// "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. */
// "incremental": true, /* Enable incremental compilation */
"target": "ES2019", /* 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": [
"ES2019.Object",
"ES2020.Promise"
], /* 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": true, /* Enable all strict type-checking options. */
"skipLibCheck": true,
"noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */
"strictNullChecks": false, /* Enable strict null checks. */
"strictFunctionTypes": true, /* Enable strict checking of function types. */
"strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
"strictPropertyInitialization": false, /* 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. */
// "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. */
}
}

8
types/eris.d.ts vendored
View File

@ -1,8 +0,0 @@
import { EmbedOptions } from 'eris';
import RichEmbed from '../src/class/RichEmbed';
declare global {
namespace Eris {
type MessageContent = string | { content?: string; tts?: boolean; disableEveryone?: boolean; embed?: EmbedOptions | RichEmbed; flags?: number };
}
}

2717
yarn.lock

File diff suppressed because it is too large Load Diff