Compare commits
99 Commits
refactor/m
...
master
Author | SHA1 | Date |
---|---|---|
Matthew | 309f080b37 | |
Matthew | 64f67c9a18 | |
Matthew | c283487b5a | |
Matthew | 9ecc9f1324 | |
Matthew | 2a9492ccbb | |
Matthew | 7337f77027 | |
Matthew | a2e9a87e69 | |
Matthew | e653484be4 | |
Matthew | 276467519f | |
Matthew | 77e50b61c5 | |
Matthew | 60683688ce | |
Matthew | 3d930792fe | |
Matthew | 9d9485700a | |
Matthew | 88e2273f5f | |
Matthew | f231cc4fdd | |
Matthew | b6da450c94 | |
Matthew | 81f58cab27 | |
Matthew | a36b579a9c | |
Matthew | 9ce84fb89b | |
Pax | 1e73890594 | |
Matthew | 84bdaae2d2 | |
Matthew | d7567ccb5a | |
Matthew | 87753431f8 | |
Matthew | 45e5496f24 | |
Matthew | c9b4fdd583 | |
Matthew | f91cc0221a | |
Matthew | 84a539c65a | |
Matthew | 5bf3425ee0 | |
Matthew | 48e5579a8c | |
Matthew | 6958d4d6b1 | |
Matthew | 33a9493a98 | |
Matthew | c812a2fe02 | |
Matthew | 1164034e55 | |
Matthew | cf42db6cc7 | |
Matthew | 436152ffef | |
Matthew | d62e8846a4 | |
Matthew | b3f9abf692 | |
Matthew | b9ce4ddce4 | |
Matthew | 4211689f73 | |
Matthew | 86fb5a7dee | |
Matthew | 374dd04e0a | |
Hiroyuki | 1b085c8740 | |
Matthew | a9a70080cb | |
Matthew | 8615d61815 | |
Matthew | 2880dc4eb7 | |
Matthew | ae272d7aee | |
Matthew | 8e9b260481 | |
Hiroyuki | 46b83daf78 | |
Matthew | 7e20b362c3 | |
Matthew | 3e1731ef89 | |
Matthew | 361fa4a5cc | |
Matthew | 5ca005cf90 | |
Hiroyuki | a075bfdc6e | |
Hiroyuki | 785baaa902 | |
Hiroyuki | 2bad7fb05a | |
Hiroyuki | 578aa0db8c | |
Hiroyuki | 669d9d0fef | |
Hiroyuki | e826e464c9 | |
Hiroyuki | 69fe6256fa | |
Hiroyuki | d4c740206d | |
Hiroyuki | ec343d541c | |
Hiroyuki | 502321ee2e | |
Hiroyuki | 248bd88469 | |
Hiroyuki | bef1f3e59f | |
Hiroyuki | 7fd95aa586 | |
eirk | cb550aa578 | |
Hiroyuki | f4f5fdf381 | |
Hiroyuki | 12aab5aa56 | |
Hiroyuki | 64a68e51a3 | |
Hiroyuki | b40b39ca12 | |
eirk | 3cfefda5ce | |
eirk | 87b320f2fb | |
eirk | 4c2690d7fd | |
eirk | 9bac31de61 | |
eirk | 2870b4a4ac | |
eirk | 9b412ae7df | |
eirk | 7629d3284b | |
eirk | 11b4828dc1 | |
eirk | 9ada0b1d4e | |
Matthew | f10a67ff58 | |
Matthew | 0da741f453 | |
Matthew | cc4cd3d06c | |
Matthew | 760337a181 | |
Matthew | 39d8760f5f | |
Matthew | 406ba69637 | |
Matthew | 96d47a040c | |
Matthew | 60f8c78180 | |
Matthew | fb3ec73898 | |
Matthew | f07d155d96 | |
Matthew | 647e3be766 | |
eirk | aebfb7a574 | |
eirk | ebbbc67943 | |
eirk | 2e84994610 | |
eirk | 1fb9bc2628 | |
eirk | f113fabc30 | |
Hiroyuki | 08be688daa | |
TheGreench | 9cc21aba44 | |
Hiroyuki | 8c9f70df10 | |
Hiroyuki | 0bf94819d5 |
|
@ -39,6 +39,8 @@
|
||||||
"import/prefer-default-export": "off",
|
"import/prefer-default-export": "off",
|
||||||
"no-useless-constructor": "off",
|
"no-useless-constructor": "off",
|
||||||
"@typescript-eslint/no-useless-constructor": 2,
|
"@typescript-eslint/no-useless-constructor": 2,
|
||||||
"import/extensions": "off"
|
"import/extensions": "off",
|
||||||
|
"consistent-return": "off",
|
||||||
|
"no-continue": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
src/config.json
|
||||||
package-lock.json
|
|
||||||
htmlEmail_templates
|
|
||||||
yarn-error.log
|
|
||||||
src/keys.json
|
src/keys.json
|
||||||
dist
|
htmlEmail_templates
|
||||||
securesign_genrsa.ts
|
securesign_genrsa.ts
|
||||||
.idea
|
|
||||||
.vscode
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ typescript_build:
|
||||||
script:
|
script:
|
||||||
- cp ../config.json ./src/config.json
|
- cp ../config.json ./src/config.json
|
||||||
- yarn install --ignore-engines
|
- yarn install --ignore-engines
|
||||||
- tsc -p ./tsconfig.json
|
- npx tsc -p ./tsconfig.json
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
stage: test
|
stage: test
|
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
"eslint.enable": true,
|
|
||||||
"eslint.validate": [
|
|
||||||
{
|
|
||||||
"language": "typescript",
|
|
||||||
"autoFix": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"editor.tabSize": 2
|
|
||||||
}
|
|
2
LICENSE
2
LICENSE
|
@ -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
|
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.
|
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
|
Copyright (C) 2019 Library of Code sp-us Engineering Team
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -7,7 +7,7 @@ get_user_by_uid_files := $(wildcard src/go/getUserByUid/*.go)
|
||||||
|
|
||||||
all: check_certificate check_cert_signatures storage getUserByUid typescript
|
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}
|
HOME=/root go build -ldflags="-s -w" -o dist/bin/checkCertificate ${check_certificate_files}
|
||||||
@chmod 740 dist/bin/checkCertificate
|
@chmod 740 dist/bin/checkCertificate
|
||||||
@file dist/bin/checkCertificate
|
@file dist/bin/checkCertificate
|
||||||
|
|
63
package.json
63
package.json
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "cloudservices-rewrite",
|
"name": "cloudservices-rewrite",
|
||||||
"version": "1.2.0",
|
"version": "2.0",
|
||||||
"description": "The official LOC Cloud Services system, this is a rewrite of the original version. ",
|
"description": "The official LOC Cloud Services system, this is a rewrite the original version, using discord.js.",
|
||||||
"main": "dist/Client.js",
|
"main": "dist/Client.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint ./ --ext ts --fix",
|
"lint": "eslint ./ --ext ts --fix",
|
||||||
|
@ -12,40 +12,41 @@
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"private": false,
|
"private": false,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.19.0",
|
"axios": "^0.21.1",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"eris": "bsian03/eris#dev",
|
"cron": "^1.8.2",
|
||||||
"eris-pagination": "git+https://github.com/bsian03/eris-pagination#c0f77b118e98309e89e6522ef545f9d121601f21",
|
"discord.js": "^13.0.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"fs-extra": "^8.1.0",
|
"fs-extra": "^10.0.0",
|
||||||
"helmet": "^3.21.2",
|
"hastebin-gen": "^2.0.5",
|
||||||
"ioredis": "^4.14.1",
|
"helmet": "^4.6.0",
|
||||||
|
"ioredis": "^4.27.7",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"moment": "^2.27.0",
|
"moment": "^2.29.1",
|
||||||
"moment-precise-range-plugin": "^1.3.0",
|
"mongoose": "^5.13.5",
|
||||||
"mongoose": "^5.7.4",
|
"nodemailer": "^6.6.3",
|
||||||
"nodemailer": "^6.3.1",
|
"showdown": "^1.9.1",
|
||||||
"signale": "^1.4.0",
|
"signale": "^1.4.0",
|
||||||
"uuid": "^3.3.3",
|
"uuid": "^8.3.2"
|
||||||
"x509": "bsian03/node-x509"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/express": "^4.17.6",
|
"@types/cron": "^1.7.3",
|
||||||
"@types/express-serve-static-core": "^4.17.5",
|
"@types/express": "^4.17.13",
|
||||||
"@types/fs-extra": "^8.0.0",
|
"@types/express-serve-static-core": "^4.17.24",
|
||||||
"@types/helmet": "^0.0.45",
|
"@types/fs-extra": "^9.0.12",
|
||||||
"@types/ioredis": "^4.0.18",
|
"@types/ioredis": "^4.26.7",
|
||||||
"@types/jsonwebtoken": "^8.5.0",
|
"@types/jsonwebtoken": "^8.5.4",
|
||||||
"@types/mongoose": "^5.7.14",
|
"@types/node": "^16.4.13",
|
||||||
"@types/nodemailer": "^6.2.1",
|
"@types/nodemailer": "^6.4.4",
|
||||||
"@types/signale": "^1.2.1",
|
"@types/showdown": "^1.9.4",
|
||||||
"@types/uuid": "^3.4.5",
|
"@types/signale": "^1.4.2",
|
||||||
"@typescript-eslint/eslint-plugin": "2.31.0",
|
"@types/uuid": "^8.3.1",
|
||||||
"@typescript-eslint/parser": "2.31.0",
|
"@typescript-eslint/eslint-plugin": "^4.29.0",
|
||||||
"eslint": "^6.5.1",
|
"@typescript-eslint/parser": "^4.29.0",
|
||||||
"eslint-config-airbnb-base": "^14.0.0",
|
"eslint": "^7.32.0",
|
||||||
"eslint-plugin-import": "^2.18.2",
|
"eslint-config-airbnb-base": "^14.2.1",
|
||||||
"madge": "^3.9.2",
|
"eslint-plugin-import": "^2.23.4",
|
||||||
"typescript": "^3.6.4"
|
"madge": "^5.0.1",
|
||||||
|
"typescript": "^4.3.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import { TextChannel } from 'eris';
|
import { TextChannel, MessageEmbed } from 'discord.js';
|
||||||
import { Server } from '..';
|
import { Server } from '..';
|
||||||
import { RichEmbed, Route } from '../../class';
|
import { Route } from '../../class';
|
||||||
|
|
||||||
export default class Root extends Route {
|
export default class Root extends Route {
|
||||||
constructor(server: Server) {
|
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 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());
|
const check = await this.server.storage.get<boolean>(req.query.t.toString());
|
||||||
if (check) return res.sendStatus(401);
|
if (check) return res.sendStatus(401);
|
||||||
const embed = new RichEmbed();
|
const embed = new MessageEmbed();
|
||||||
embed.setTitle('Referral Authorization');
|
embed.setTitle('Referral Authorization');
|
||||||
embed.setDescription(req.query.t.toString());
|
embed.setDescription(req.query.t.toString());
|
||||||
embed.addField('Referred User', `${token.referredUserAndDiscrim} | ${token.referredUserID}`, true);
|
embed.addField('Referred User', `${token.referredUserAndDiscrim} | ${token.referredUserID}`, true);
|
||||||
embed.addField('Referrer User', token.referrerUsername, true);
|
embed.addField('Referrer User', token.referrerUsername, true);
|
||||||
embed.addField('Referral Code', token.referralCode, true);
|
embed.addField('Referral Code', token.referralCode, true);
|
||||||
embed.setTimestamp();
|
embed.setTimestamp();
|
||||||
embed.setFooter(this.server.client.user.username, this.server.client.user.avatarURL);
|
embed.setFooter(this.server.client.user.username, this.server.client.user.avatarURL());
|
||||||
const channel = <TextChannel> this.server.client.guilds.get('446067825673633794').channels.get('580950455581147146');
|
const channel = this.server.client.guilds.cache.get('446067825673633794').channels.cache.get('580950455581147146') as TextChannel;
|
||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
await this.server.storage.set(req.query.t.toString(), true);
|
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 {
|
} catch {
|
||||||
return res.sendStatus(401);
|
return res.sendStatus(401);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/* eslint-disable no-continue */
|
/* eslint-disable no-continue */
|
||||||
import { TextChannel } from 'eris';
|
import { TextChannel, MessageEmbed, TextBasedChannel } from 'discord.js';
|
||||||
import { Server } from '..';
|
import { Server } from '..';
|
||||||
import { Route, RichEmbed } from '../../class';
|
import { Route } from '../../class';
|
||||||
|
|
||||||
export default class Webhook extends Route {
|
export default class Webhook extends Route {
|
||||||
constructor(server: Server) {
|
constructor(server: Server) {
|
||||||
|
@ -12,7 +12,7 @@ export default class Webhook extends Route {
|
||||||
this.router.post('/s1', async (req, res) => {
|
this.router.post('/s1', async (req, res) => {
|
||||||
try {
|
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 });
|
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.setTitle('Service Request');
|
||||||
embed.setDescription(`https://staff.libraryofcode.org/browse/${req.body.key}\n${req.body.url}`);
|
embed.setDescription(`https://staff.libraryofcode.org/browse/${req.body.key}\n${req.body.url}`);
|
||||||
embed.setColor('#FF00FF');
|
embed.setColor('#FF00FF');
|
||||||
|
@ -20,10 +20,10 @@ export default class Webhook extends Route {
|
||||||
embed.addField('Reporter', req.body.reporter, true);
|
embed.addField('Reporter', req.body.reporter, true);
|
||||||
embed.addField('Status', req.body.status, true);
|
embed.addField('Status', req.body.status, true);
|
||||||
embed.addField('Summary', req.body.summary);
|
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();
|
embed.setTimestamp();
|
||||||
const chan = <TextChannel> this.server.client.getChannel('780513128240382002');
|
const channel = this.server.client.channels.cache.get('780513128240382002') as TextChannel;
|
||||||
chan.createMessage({ content: '<@&741797822940315650>', embed });
|
channel.send({ content: '<@&741797822940315650>', embeds: [embed] });
|
||||||
return res.status(200).json({ code: this.constants.codes.SUCCESS, message: this.constants.codes.SUCCESS });
|
return res.status(200).json({ code: this.constants.codes.SUCCESS, message: this.constants.codes.SUCCESS });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return this.handleError(err, res);
|
return this.handleError(err, res);
|
||||||
|
@ -47,17 +47,18 @@ export default class Webhook extends Route {
|
||||||
} else {
|
} else {
|
||||||
await account.updateOne({ $set: { tier: 3 } });
|
await account.updateOne({ $set: { tier: 3 } });
|
||||||
}
|
}
|
||||||
const embed = new RichEmbed();
|
const embed = new MessageEmbed();
|
||||||
embed.setTitle('Cloud Account | Tier Change');
|
embed.setTitle('Cloud Account | Tier Change');
|
||||||
embed.setColor('#0099ff');
|
embed.setColor('#0099ff');
|
||||||
embed.addField('User', `${account.username} | <@${account.userID}>`, true);
|
embed.addField('User', `${account.username} | <@${account.userID}>`, true);
|
||||||
embed.addField('Technician', 'SYSTEM', true);
|
embed.addField('Technician', 'SYSTEM', true);
|
||||||
embed.addField('Old Tier -> New Tier', `${account.tier} -> 3`, 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();
|
embed.setTimestamp();
|
||||||
await this.server.client.util.sendMessageToUserTerminal(account.username, 'A technician has changed your tier to 3').catch(() => { });
|
await this.server.client.util.sendMessageToUserTerminal(account.username, 'A technician has changed your tier to 3').catch(() => { });
|
||||||
this.server.client.createMessage('580950455581147146', { embed });
|
const channel = this.server.client.channels.cache.get('580950455581147146') as TextChannel;
|
||||||
this.server.client.getDMChannel(account.userID).then((channel) => channel.createMessage({ embed })).catch();
|
channel.send({ embeds: [embed] });
|
||||||
|
this.server.client.users.cache.get(account.userID).send({ embeds: [embed] });
|
||||||
return res.sendStatus(200);
|
return res.sendStatus(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -74,17 +75,18 @@ export default class Webhook extends Route {
|
||||||
} else {
|
} else {
|
||||||
await account.updateOne({ $set: { tier: 1 } });
|
await account.updateOne({ $set: { tier: 1 } });
|
||||||
}
|
}
|
||||||
const embed = new RichEmbed();
|
const embed = new MessageEmbed();
|
||||||
embed.setTitle('Cloud Account | Tier Change');
|
embed.setTitle('Cloud Account | Tier Change');
|
||||||
embed.setColor('#0099ff');
|
embed.setColor('#0099ff');
|
||||||
embed.addField('User', `${account.username} | <@${account.userID}>`, true);
|
embed.addField('User', `${account.username} | <@${account.userID}>`, true);
|
||||||
embed.addField('Technician', 'SYSTEM', true);
|
embed.addField('Technician', 'SYSTEM', true);
|
||||||
embed.addField('Old Tier -> New Tier', `${account.tier} -> 1`, 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();
|
embed.setTimestamp();
|
||||||
await this.server.client.util.sendMessageToUserTerminal(account.username, 'A technician has changed your tier.').catch(() => { });
|
await this.server.client.util.sendMessageToUserTerminal(account.username, 'A technician has changed your tier.').catch(() => { });
|
||||||
this.server.client.createMessage('580950455581147146', { embed });
|
const ch = this.server.client.channels.cache.get('580950455581147146') as TextChannel;
|
||||||
this.server.client.getDMChannel(account.userID).then((channel) => channel.createMessage({ embed })).catch();
|
ch.send({ embeds: [embed] });
|
||||||
|
this.server.client.users.cache.get(account.userID).send({ embeds: [embed] });
|
||||||
return res.sendStatus(200);
|
return res.sendStatus(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -105,17 +107,18 @@ export default class Webhook extends Route {
|
||||||
} else {
|
} else {
|
||||||
await account.updateOne({ $set: { tier: 2 } });
|
await account.updateOne({ $set: { tier: 2 } });
|
||||||
}
|
}
|
||||||
const embed = new RichEmbed();
|
const embed = new MessageEmbed();
|
||||||
embed.setTitle('Cloud Account | Tier Change');
|
embed.setTitle('Cloud Account | Tier Change');
|
||||||
embed.setColor('#0099ff');
|
embed.setColor('#0099ff');
|
||||||
embed.addField('User', `${account.username} | <@${account.userID}>`, true);
|
embed.addField('User', `${account.username} | <@${account.userID}>`, true);
|
||||||
embed.addField('Technician', 'SYSTEM', true);
|
embed.addField('Technician', 'SYSTEM', true);
|
||||||
embed.addField('Old Tier -> New Tier', `${account.tier} -> 2`, 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();
|
embed.setTimestamp();
|
||||||
await this.server.client.util.sendMessageToUserTerminal(account.username, 'A technician has changed your tier to 2').catch(() => { });
|
await this.server.client.util.sendMessageToUserTerminal(account.username, 'A technician has changed your tier to 2').catch(() => { });
|
||||||
this.server.client.createMessage('580950455581147146', { embed });
|
const ch = this.server.client.channels.cache.get('580950455581147146') as TextChannel;
|
||||||
this.server.client.getDMChannel(account.userID).then((channel) => channel.createMessage({ embed })).catch();
|
ch.send({ embeds: [embed] });
|
||||||
|
this.server.client.users.cache.get(account.userID).send({ embeds: [embed] });
|
||||||
return res.sendStatus(200);
|
return res.sendStatus(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -132,17 +135,48 @@ export default class Webhook extends Route {
|
||||||
} else {
|
} else {
|
||||||
await account.updateOne({ $set: { tier: 1 } });
|
await account.updateOne({ $set: { tier: 1 } });
|
||||||
}
|
}
|
||||||
const embed = new RichEmbed();
|
const embed = new MessageEmbed();
|
||||||
embed.setTitle('Cloud Account | Tier Change');
|
embed.setTitle('Cloud Account | Tier Change');
|
||||||
embed.setColor('#0099ff');
|
embed.setColor('#0099ff');
|
||||||
embed.addField('User', `${account.username} | <@${account.userID}>`, true);
|
embed.addField('User', `${account.username} | <@${account.userID}>`, true);
|
||||||
embed.addField('Technician', 'SYSTEM', true);
|
embed.addField('Technician', 'SYSTEM', true);
|
||||||
embed.addField('Old Tier -> New Tier', `${account.tier} -> 1`, 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();
|
embed.setTimestamp();
|
||||||
await this.server.client.util.sendMessageToUserTerminal(account.username, 'A technician has changed your tier.').catch(() => { });
|
await this.server.client.util.sendMessageToUserTerminal(account.username, 'A technician has changed your tier.').catch(() => { });
|
||||||
this.server.client.createMessage('580950455581147146', { embed });
|
const ch = this.server.client.channels.cache.get('580950455581147146') as TextChannel;
|
||||||
this.server.client.getDMChannel(account.userID).then((channel) => channel.createMessage({ embed })).catch();
|
ch.send({ embeds: [embed] });
|
||||||
|
this.server.client.users.cache.get(account.userID).send({ embeds: [embed] });
|
||||||
|
return res.sendStatus(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.router.get('/set-tier', async (req, res) => {
|
||||||
|
if (req.query?.auth !== this.server.security.keys.internal.toString('hex')) return res.status(401).json({ code: this.constants.codes.UNAUTHORIZED, message: this.constants.messages.UNAUTHORIZED });
|
||||||
|
if (!req.query?.userID) return res.status(400).json({ code: this.constants.codes.CLIENT_ERROR });
|
||||||
|
|
||||||
|
if (Number(req.query.t.toString()) > 3 || Number(req.query.t.toString()) < 1) return res.status(400).json({ code: this.constants.codes.CLIENT_ERROR });
|
||||||
|
|
||||||
|
const account = await this.server.client.db.Account.findOne({ userID: req.query.userID.toString() });
|
||||||
|
if (!account) return res.sendStatus(404);
|
||||||
|
const tier = await this.server.client.db.Tier.findOne({ id: Number(req.query.t.toString()) });
|
||||||
|
|
||||||
|
if (account.ramLimitNotification !== -1) {
|
||||||
|
await account.updateOne({ $set: { tier: Number(req.query.t.toString()), ramLimitNotification: tier.resourceLimits.ram - 20 } });
|
||||||
|
} else {
|
||||||
|
await account.updateOne({ $set: { tier: Number(req.query.t.toString()) } });
|
||||||
|
}
|
||||||
|
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} -> ${Number(req.query.t.toString())}`, true);
|
||||||
|
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(() => { });
|
||||||
|
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);
|
return res.sendStatus(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@ import { randomBytes } from 'crypto';
|
||||||
import { AccountInterface } from '../models';
|
import { AccountInterface } from '../models';
|
||||||
import { Client } from '..';
|
import { Client } from '..';
|
||||||
|
|
||||||
|
export const LINUX_USERNAME_REGEX = /^[a-z][-a-z0-9]*$/;
|
||||||
|
|
||||||
export default class AccountUtil {
|
export default class AccountUtil {
|
||||||
public client: Client;
|
public client: Client;
|
||||||
|
|
||||||
|
@ -20,15 +22,15 @@ export default class AccountUtil {
|
||||||
* @param moderator The Discord user ID for the Staff member that created the account.
|
* @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 }> {
|
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();
|
const tempPass = this.client.util.randomPassword();
|
||||||
let passHash = await this.client.util.createHash(tempPass); passHash = passHash.replace(/[$]/g, '\\$').replace('\n', '');
|
const passHash = (await this.client.util.createHash(tempPass)).replace(/[$]/g, '\\$').replace('\n', '');
|
||||||
const acctName = this.client.users.get(data.userID).username.replace(/[!@#$%^&*(),.?":{}|<>]/g, '-').replace(/\s/g, '-');
|
const acctName = this.client.users.cache.get(data.userID).username.replace(/[!@#$%^&*(),.?":{}|<>]/g, '-').replace(/\s/g, '-');
|
||||||
const etcPasswd = `${acctName},${data.userID},,`;
|
const etcPasswd = `${acctName},${data.userID},,`;
|
||||||
const code = randomBytes(3).toString('hex').toUpperCase();
|
const code = randomBytes(3).toString('hex').toUpperCase();
|
||||||
|
|
||||||
const accountInterface = await this.client.util.createAccount(passHash, etcPasswd, data.username, data.userID, data.emailAddress, moderator, code);
|
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 req = await axios.get('https://loc.sh/int/directory');
|
||||||
const find = req.data.find((mem) => mem.userID === moderator);
|
const find = req.data.find((mem) => mem.userID === moderator);
|
||||||
|
|
||||||
|
@ -39,13 +41,13 @@ export default class AccountUtil {
|
||||||
subject: 'Approval for CS Account',
|
subject: 'Approval for CS Account',
|
||||||
html: `
|
html: `
|
||||||
<body>
|
<body>
|
||||||
<style>* {font-family: 'Calibri';}</style>
|
<style>* {font-family: 'Calibri',sans-serif;}</style>
|
||||||
<h1>Library of Code | Cloud Services</h1>
|
<h1>Library of Code | Cloud Services</h1>
|
||||||
<h2>Congratulations, your CS Account application has been approved. Welcome! Please see below for some details regarding your account and our services</h2>
|
<h2>Congratulations, your CS Account application has been approved. Welcome! Please see below for some details regarding your account and our services</h2>
|
||||||
<p><b>Username:</b> ${data.username}</p>
|
<p><b>Username:</b> ${data.username}</p>
|
||||||
<p><b>Support Key:</b> ${code} || <i>You may be asked for this support key when contacting Library of Code, please keep the code in a safe area.</i></p>
|
<p><b>Support Key:</b> ${code} || <i>You may be asked for this support key when contacting Library of Code, please keep the code in a safe area.</i></p>
|
||||||
<p><b>SSH Login:</b> <pre><code style="font-family: Courier;">ssh ${data.username}@cloud.libraryofcode.org</code></pre>
|
<p><b>SSH Login:</b> <pre><code style="font-family: Courier,sans-serif;">ssh ${data.username}@cloud.libraryofcode.org</code></pre>
|
||||||
<p><b>Underwritten by:</b> ${moderatorMember.user.username}, ${find.pn.join(', ')}
|
<p><b>Underwritten by:</b> ${moderatorMember.user.username}${find.isManager ? ' [k]' : ' '}</p>
|
||||||
<h2>Useful information</h2>
|
<h2>Useful information</h2>
|
||||||
<h3>How to log in:</h3>
|
<h3>How to log in:</h3>
|
||||||
<ol>
|
<ol>
|
||||||
|
@ -67,22 +69,27 @@ export default class AccountUtil {
|
||||||
<h3>Want to support us?</h3>
|
<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>
|
<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>
|
<b><i>Library of Code sp-us | Support Team</i></b>
|
||||||
</body>
|
</body>
|
||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
|
const guild = this.client.guilds.cache.get('446067825673633794');
|
||||||
this.client.guilds.get('446067825673633794').members.get(data.userID).addRole('546457886440685578');
|
const member = guild.members.cache.get(data.userID);
|
||||||
const dmChannel = await this.client.getDMChannel(data.userID).catch();
|
await member.roles.add('546457886440685578');
|
||||||
dmChannel.createMessage('<:loc:607695848612167700> **Thank you for creating an account with us!** <:loc:607695848612167700>\n'
|
const user = this.client.users.cache.get(data.userID);
|
||||||
+ `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`
|
try {
|
||||||
+ `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`
|
await user.send('<:loc:607695848612167700> **Thank you for creating an account with us!** <:loc:607695848612167700>\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'
|
+ `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`
|
||||||
+ 'An email containing some useful information has also been sent.\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`
|
||||||
+ `Your support key is \`${code}\`. Pin this message, you may need this key to contact Library of Code in the future.`).catch();
|
+ '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'
|
||||||
|
+ 'An email containing some useful information has also been sent.\n'
|
||||||
|
+ `Your support key is \`${code}\`. Pin this message, you may need this key to contact Library of Code in the future.`);
|
||||||
|
} catch (error) {
|
||||||
|
this.client.util.handleError(error);
|
||||||
|
}
|
||||||
return { account: accountInterface, tempPass };
|
return { account: accountInterface, tempPass };
|
||||||
}
|
}
|
||||||
|
|
||||||
public async lock(username: string, moderatorID: string, data?: { reason?: string, time?: number}) {
|
public async lock(username: string, moderatorID: string, data?: { reason?: string, time?: number }) {
|
||||||
const account = await this.client.db.Account.findOne({ username });
|
const account = await this.client.db.Account.findOne({ username });
|
||||||
if (!account) throw new Error('Account does not exist.');
|
if (!account) throw new Error('Account does not exist.');
|
||||||
if (account.locked) throw new Error('Account is already locked.');
|
if (account.locked) throw new Error('Account is already locked.');
|
||||||
|
@ -90,7 +97,7 @@ export default class AccountUtil {
|
||||||
await this.client.util.exec(`lock ${account.username}`);
|
await this.client.util.exec(`lock ${account.username}`);
|
||||||
await account.updateOne({ locked: true });
|
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({
|
this.client.util.transport.sendMail({
|
||||||
to: account.emailAddress,
|
to: account.emailAddress,
|
||||||
|
@ -101,9 +108,9 @@ export default class AccountUtil {
|
||||||
<h1>Library of Code | Cloud Services</h1>
|
<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>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>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>
|
<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>
|
<b><i>Library of Code sp-us | Support Team</i></b>
|
||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,127 +1,127 @@
|
||||||
/* eslint-disable max-classes-per-file */
|
/* eslint-disable max-classes-per-file */
|
||||||
/* eslint-disable no-case-declarations */
|
/* eslint-disable no-case-declarations */
|
||||||
/* eslint-disable consistent-return */
|
/* eslint-disable consistent-return */
|
||||||
import net from 'net';
|
import net from 'net';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import { Client, Collection, Context } from '.';
|
import { Client, Collection, Context } from '.';
|
||||||
import type { Handler } from '.';
|
import type { Handler } from '.';
|
||||||
|
|
||||||
export default class CSCLI {
|
export default class CSCLI {
|
||||||
public client: Client;
|
public client: Client;
|
||||||
|
|
||||||
public servers: {
|
public servers: {
|
||||||
tcp?: net.Server,
|
tcp?: net.Server,
|
||||||
unix?: net.Server,
|
unix?: net.Server,
|
||||||
};
|
};
|
||||||
|
|
||||||
public handlers: Collection<Handler>;
|
public handlers: Collection<Handler>;
|
||||||
|
|
||||||
#hmac: string;
|
#hmac: string;
|
||||||
|
|
||||||
constructor(client: Client) {
|
constructor(client: Client) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.servers = {};
|
this.servers = {};
|
||||||
this.loadKeys();
|
this.loadKeys();
|
||||||
this.servers.tcp = net.createServer((socket) => {
|
this.servers.tcp = net.createServer((socket) => {
|
||||||
socket.on('data', async (data) => {
|
socket.on('data', async (data) => {
|
||||||
try {
|
try {
|
||||||
await this.tcpHandle(socket, data);
|
await this.tcpHandle(socket, data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await this.client.util.handleError(err);
|
await this.client.util.handleError(err);
|
||||||
socket.destroy();
|
socket.destroy();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.servers.unix = net.createServer((socket) => {
|
this.servers.unix = net.createServer((socket) => {
|
||||||
socket.on('data', async (data) => {
|
socket.on('data', async (data) => {
|
||||||
try {
|
try {
|
||||||
await this.unixHandle(socket, data);
|
await this.unixHandle(socket, data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await this.client.util.handleError(err);
|
await this.client.util.handleError(err);
|
||||||
socket.destroy();
|
socket.destroy();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
public load(handlerFiles: { [s: string]: typeof Handler; } | ArrayLike<typeof Handler>) {
|
public load(handlerFiles: { [s: string]: typeof Handler; } | ArrayLike<typeof Handler>) {
|
||||||
this.handlers = new Collection();
|
this.handlers = new Collection();
|
||||||
const hdFiles = Object.values<typeof Handler>(handlerFiles);
|
const hdFiles = Object.values<typeof Handler>(handlerFiles);
|
||||||
for (const Handler1 of hdFiles) {
|
for (const Handler1 of hdFiles) {
|
||||||
const handler = new Handler1();
|
const handler = new Handler1();
|
||||||
this.handlers.add(handler.endpoint, handler);
|
this.handlers.add(handler.endpoint, handler);
|
||||||
this.client.signale.success(`Successfully loaded endpoint '${handler.endpoint}'.`);
|
this.client.signale.success(`Successfully loaded endpoint '${handler.endpoint}'.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async unixHandle(socket: net.Socket, data: Buffer) {
|
public async unixHandle(socket: net.Socket, data: Buffer) {
|
||||||
const args = data.toString().trim().split('$');
|
const args = data.toString().trim().split('$');
|
||||||
const parsed: { Username: string, Type: string, Message?: string, Data?: any, HMAC: string } = JSON.parse(args[0]);
|
const parsed: { Username: string, Type: string, Message?: string, Data?: any, HMAC: string } = JSON.parse(args[0]);
|
||||||
// FINISH VERIFICATION CHECKS
|
// FINISH VERIFICATION CHECKS
|
||||||
const handler: Handler = this.handlers.get(parsed.Type);
|
const handler: Handler = this.handlers.get(parsed.Type);
|
||||||
if (!handler) return socket.destroy();
|
if (!handler) return socket.destroy();
|
||||||
|
|
||||||
const context = new Context(socket, args[0], this.client);
|
const context = new Context(socket, args[0], this.client);
|
||||||
await handler.handle(context);
|
await handler.handle(context);
|
||||||
if (!context.socket.destroyed) {
|
if (!context.socket.destroyed) {
|
||||||
socket.destroy();
|
socket.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async tcpHandle(socket: net.Socket, data: Buffer) {
|
public async tcpHandle(socket: net.Socket, data: Buffer) {
|
||||||
const args = data.toString().trim().split('$');
|
const args = data.toString().trim().split('$');
|
||||||
const verification = this.verifyConnection(args[1], args[0]);
|
const verification = this.verifyConnection(args[1], args[0]);
|
||||||
if (!verification) {
|
if (!verification) {
|
||||||
socket.write('UNAUTHORIZED TO EXECUTE ON THIS SERVER\n');
|
socket.write('UNAUTHORIZED TO EXECUTE ON THIS SERVER\n');
|
||||||
return socket.destroy();
|
return socket.destroy();
|
||||||
}
|
}
|
||||||
const parsed: { Username: string, Type: string, Message?: string, Data?: any, HMAC: string } = JSON.parse(args[0]);
|
const parsed: { Username: string, Type: string, Message?: string, Data?: any, HMAC: string } = JSON.parse(args[0]);
|
||||||
// FINISH VERIFICATION CHECKS
|
// FINISH VERIFICATION CHECKS
|
||||||
const handler: Handler = this.handlers.get(parsed.Type);
|
const handler: Handler = this.handlers.get(parsed.Type);
|
||||||
if (!handler) return socket.destroy();
|
if (!handler) return socket.destroy();
|
||||||
|
|
||||||
const context = new Context(socket, args[0], this.client);
|
const context = new Context(socket, args[0], this.client);
|
||||||
await handler.handle(context);
|
await handler.handle(context);
|
||||||
if (!context.socket.destroyed) {
|
if (!context.socket.destroyed) {
|
||||||
socket.destroy();
|
socket.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public verifyConnection(key: string, data: any): boolean {
|
public verifyConnection(key: string, data: any): boolean {
|
||||||
const hmac = crypto.createHmac('sha256', this.#hmac);
|
const hmac = crypto.createHmac('sha256', this.#hmac);
|
||||||
hmac.update(data);
|
hmac.update(data);
|
||||||
const computed = hmac.digest('hex');
|
const computed = hmac.digest('hex');
|
||||||
if (computed === key) return true;
|
if (computed === key) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async loadKeys() {
|
public async loadKeys() {
|
||||||
const key = await fs.readFile('/etc/cscli.conf', { encoding: 'utf8' });
|
const key = await fs.readFile('/etc/cscli.conf', { encoding: 'utf8' });
|
||||||
this.#hmac = key.toString().trim();
|
this.#hmac = key.toString().trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async init() {
|
public async init() {
|
||||||
try {
|
try {
|
||||||
await fs.unlink('/run/csd-comm.sock');
|
await fs.unlink('/run/csd-comm.sock');
|
||||||
} catch {
|
} catch {
|
||||||
// eslint-disable-next-line no-unused-expressions
|
// eslint-disable-next-line no-unused-expressions
|
||||||
null;
|
null;
|
||||||
}
|
}
|
||||||
this.servers.tcp.on('error', (err) => {
|
this.servers.tcp.on('error', (err) => {
|
||||||
this.client.util.handleError(err);
|
this.client.util.handleError(err);
|
||||||
});
|
});
|
||||||
this.servers.unix.on('error', (err) => {
|
this.servers.unix.on('error', (err) => {
|
||||||
this.client.util.handleError(err);
|
this.client.util.handleError(err);
|
||||||
});
|
});
|
||||||
this.servers.tcp.listen(8124, () => {
|
this.servers.tcp.listen(8124, () => {
|
||||||
this.client.signale.success('[CSD-COMM] Listen - TCP:8124');
|
this.client.signale.success('[CSD-COMM] Listen - TCP:8124');
|
||||||
});
|
});
|
||||||
this.servers.unix.listen('/var/run/csd-comm.sock', async () => {
|
this.servers.unix.listen('/var/run/csd-comm.sock', async () => {
|
||||||
await fs.chmod('/run/csd-comm.sock', 0o770);
|
await fs.chmod('/run/csd-comm.sock', 0o770);
|
||||||
await fs.chown('/run/csd-comm.sock', 0, 115);
|
await fs.chown('/run/csd-comm.sock', 0, 115);
|
||||||
this.client.signale.success('[CSD-COMM] Listen - UNIX:/run/csd-comm.sock');
|
this.client.signale.success('[CSD-COMM] Listen - UNIX:/run/csd-comm.sock');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import Eris from 'eris';
|
import { Client as DiscordClient, Intents } from 'discord.js';
|
||||||
import Redis from 'ioredis';
|
import Redis from 'ioredis';
|
||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
import signale from 'signale';
|
import signale from 'signale';
|
||||||
|
@ -8,8 +8,7 @@ import { Account, AccountInterface, Moderation, ModerationInterface, Domain, Dom
|
||||||
import { emojis } from '../stores';
|
import { emojis } from '../stores';
|
||||||
import { Command, CSCLI, Util, Collection, Server, Event } from '.';
|
import { Command, CSCLI, Util, Collection, Server, Event } from '.';
|
||||||
|
|
||||||
|
export default class Client extends DiscordClient {
|
||||||
export default class Client extends Eris.Client {
|
|
||||||
public config: { 'token': string; 'cloudflare': string; 'prefix': string; 'emailPass': string; 'mongoURL': string; 'port': number; 'keyPair': { 'publicKey': string; 'privateKey': string; }; vendorKey: string; internalKey: string; };
|
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;
|
public util: Util;
|
||||||
|
@ -35,7 +34,36 @@ export default class Client extends Eris.Client {
|
||||||
public buildError: boolean
|
public buildError: boolean
|
||||||
|
|
||||||
constructor() {
|
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';
|
process.title = 'cloudservices';
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
@ -44,7 +72,9 @@ export default class Client extends Eris.Client {
|
||||||
this.events = new Collection<Event>();
|
this.events = new Collection<Event>();
|
||||||
this.functions = new Collection<Function>();
|
this.functions = new Collection<Function>();
|
||||||
this.db = { Account, Domain, Moderation, Tier };
|
this.db = { Account, Domain, Moderation, Tier };
|
||||||
this.redis = new Redis();
|
this.redis = new Redis({
|
||||||
|
password: config.redis,
|
||||||
|
});
|
||||||
this.stores = { emojis };
|
this.stores = { emojis };
|
||||||
this.signale = signale;
|
this.signale = signale;
|
||||||
this.signale.config({
|
this.signale.config({
|
||||||
|
@ -74,7 +104,7 @@ export default class Client extends Eris.Client {
|
||||||
const funcRequire: Function = require(`${__dirname}/../functions/${func}`).default;
|
const funcRequire: Function = require(`${__dirname}/../functions/${func}`).default;
|
||||||
this.functions.set(func.split('.')[0], funcRequire);
|
this.functions.set(func.split('.')[0], funcRequire);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.signale.error(`Error occured loading ${func}`);
|
this.signale.error(`Error occurred loading ${func}`);
|
||||||
await this.util.handleError(error);
|
await this.util.handleError(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -83,7 +113,6 @@ export default class Client extends Eris.Client {
|
||||||
public loadCommand(CommandFile: any) {
|
public loadCommand(CommandFile: any) {
|
||||||
// eslint-disable-next-line no-useless-catch
|
// eslint-disable-next-line no-useless-catch
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line
|
|
||||||
const command: Command = new CommandFile(this);
|
const command: Command = new CommandFile(this);
|
||||||
if (command.subcmds.length) {
|
if (command.subcmds.length) {
|
||||||
command.subcmds.forEach((C) => {
|
command.subcmds.forEach((C) => {
|
||||||
|
@ -126,19 +155,17 @@ export default class Client extends Eris.Client {
|
||||||
|
|
||||||
public async init() {
|
public async init() {
|
||||||
await mongoose.connect(config.mongoURL, { useNewUrlParser: true, useUnifiedTopology: true });
|
await mongoose.connect(config.mongoURL, { useNewUrlParser: true, useUnifiedTopology: true });
|
||||||
await this.connect();
|
await this.login(config.token);
|
||||||
this.on('ready', () => {
|
this.on('ready', () => {
|
||||||
this.signale.info(`Connected to Discord as ${this.user.username}#${this.user.discriminator}`);
|
this.signale.info(`Connected to Discord as ${this.user.username}#${this.user.discriminator}`);
|
||||||
});
|
});
|
||||||
const intervals = await fs.readdir(`${__dirname}/../intervals`);
|
const intervals = await fs.readdir(`${__dirname}/../intervals`);
|
||||||
intervals.forEach((interval) => {
|
intervals.forEach((interval) => {
|
||||||
// eslint-disable-next-line
|
|
||||||
if (interval === 'index.js') return;
|
if (interval === 'index.js') return;
|
||||||
require(`${__dirname}/../intervals/${interval}`).default(this);
|
require(`${__dirname}/../intervals/${interval}`).default(this);
|
||||||
this.signale.complete(`Loaded interval ${interval.split('.')[0]}`);
|
this.signale.complete(`Loaded interval ${interval.split('.')[0]}`);
|
||||||
});
|
});
|
||||||
this.server = new Server(this, { port: this.config.port });
|
this.server = new Server(this, { port: this.config.port });
|
||||||
// eslint-disable-next-line no-new
|
|
||||||
|
|
||||||
const corepath = '/opt/CloudServices/dist';
|
const corepath = '/opt/CloudServices/dist';
|
||||||
const cmdFiles = await fs.readdir('/opt/CloudServices/dist/commands');
|
const cmdFiles = await fs.readdir('/opt/CloudServices/dist/commands');
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message, TextableChannel } from 'eris';
|
import { Message, TextBasedChannels } from 'discord.js';
|
||||||
import { Client, Collection } from '.';
|
import { Client, Collection } from '.';
|
||||||
|
|
||||||
export default class Command {
|
export default class Command {
|
||||||
|
@ -39,15 +39,15 @@ export default class Command {
|
||||||
this.permissions = {};
|
this.permissions = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
public success(channel: TextableChannel, txt: string) {
|
public success(channel: TextBasedChannels, txt: string) {
|
||||||
return channel.createMessage(`***${this.client.stores.emojis.success} ${txt}***`);
|
return channel.send(`***${this.client.stores.emojis.success} ${txt}***`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public loading(channel: TextableChannel, txt: string) {
|
public loading(channel: TextBasedChannels, txt: string) {
|
||||||
return channel.createMessage(`***${this.client.stores.emojis.loading} ${txt}***`);
|
return channel.send(`***${this.client.stores.emojis.loading} ${txt}***`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public error(channel: TextableChannel, txt: string) {
|
public error(channel: TextBasedChannels, txt: string) {
|
||||||
return channel.createMessage(`***${this.client.stores.emojis.error} ${txt}***`);
|
return channel.send(`***${this.client.stores.emojis.error} ${txt}***`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +1,39 @@
|
||||||
import net from 'net';
|
import net from 'net';
|
||||||
import { Client } from '.';
|
import { Client } from '.';
|
||||||
|
|
||||||
export default class Context {
|
export default class Context {
|
||||||
public socket: net.Socket;
|
public socket: net.Socket;
|
||||||
|
|
||||||
public client: Client;
|
public client: Client;
|
||||||
|
|
||||||
public data: {
|
public data: {
|
||||||
username: string,
|
username: string,
|
||||||
endpoint: string,
|
endpoint: string,
|
||||||
message?: string,
|
message?: string,
|
||||||
additionalData?: object,
|
additionalData?: object,
|
||||||
HMAC: string,
|
HMAC: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(socket: net.Socket, data: string, client: Client) {
|
constructor(socket: net.Socket, data: string, client: Client) {
|
||||||
const parsed: { Username: string, Type: string, Message?: string, Data?: object, HMAC: string } = JSON.parse(data);
|
const parsed: { Username: string, Type: string, Message?: string, Data?: object, HMAC: string } = JSON.parse(data);
|
||||||
|
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.data = {
|
this.data = {
|
||||||
username: parsed.Username,
|
username: parsed.Username,
|
||||||
endpoint: parsed.Type,
|
endpoint: parsed.Type,
|
||||||
message: parsed.Message,
|
message: parsed.Message,
|
||||||
additionalData: parsed.Data,
|
additionalData: parsed.Data,
|
||||||
HMAC: parsed.HMAC,
|
HMAC: parsed.HMAC,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public send(v: string) {
|
public send(v: string) {
|
||||||
this.socket.write(`${v.toString()}\n`);
|
this.socket.write(`${v.toString()}\n`, (err) => {
|
||||||
this.socket.destroy();
|
if (err) {
|
||||||
}
|
this.client.signale.error(`Error occurred while writing: ${err}`);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
this.socket.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,9 +5,8 @@ import { gzip, gzipSync, unzip } from 'zlib';
|
||||||
|
|
||||||
type JSONData = [{key: string, value: any}?];
|
type JSONData = [{key: string, value: any}?];
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persistant local JSON-based storage.
|
* Persistent local JSON-based storage.
|
||||||
* - auto-locking system to prevent corrupted data
|
* - auto-locking system to prevent corrupted data
|
||||||
* - uses gzip compression to keep DB storage space utilization low
|
* - uses gzip compression to keep DB storage space utilization low
|
||||||
* @author Matthew <matthew@staff.libraryofcode.org>
|
* @author Matthew <matthew@staff.libraryofcode.org>
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -104,7 +104,7 @@ export default class Report {
|
||||||
* @param pin The last 4 digits of the member's PIN number.
|
* @param pin The last 4 digits of the member's PIN number.
|
||||||
* @param reason A reason for the hard inquiry.
|
* @param reason A reason for the hard inquiry.
|
||||||
* ```ts
|
* ```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> {
|
public static async hard(userID: string, pin: number, reason: string, auth: string): Promise<HardReport> {
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -44,7 +44,7 @@ export default class Route {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function checks for the presense of a Bearer token with Security.extractBearer(),
|
* This function checks for the presence of a Bearer token with Security.extractBearer(),
|
||||||
* then it will attempt to validate it with Security.checkBearer().
|
* 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
|
* 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
|
* `account`, which will hold an the bearer token's account owner. The account is of the
|
||||||
|
|
|
@ -6,7 +6,6 @@ import fs from 'fs-extra';
|
||||||
import { Client, Collection, LocalStorage, Route } from '.';
|
import { Client, Collection, LocalStorage, Route } from '.';
|
||||||
import { Security } from '../api';
|
import { Security } from '../api';
|
||||||
|
|
||||||
|
|
||||||
export default class Server {
|
export default class Server {
|
||||||
public routes: Collection<Route>
|
public routes: Collection<Route>
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,25 @@
|
||||||
|
/* eslint-disable import/no-unresolved */
|
||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
/* eslint-disable no-param-reassign */
|
/* eslint-disable no-param-reassign */
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { randomBytes } from 'crypto';
|
import { randomBytes } from 'crypto';
|
||||||
import childProcess from 'child_process';
|
import childProcess from 'child_process';
|
||||||
import nodemailer from 'nodemailer';
|
import nodemailer from 'nodemailer';
|
||||||
import { Message, PrivateChannel, GroupChannel, Member, User } from 'eris';
|
import { ColorResolvable, DMChannel, Message, MessageEmbed, TextChannel, User } from 'discord.js';
|
||||||
import uuid from 'uuid/v4';
|
import { v4 as uuid } from 'uuid';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
import hastebin from 'hastebin-gen';
|
||||||
import { getUserByUid } from '../functions';
|
import { getUserByUid } from '../functions';
|
||||||
import { AccountUtil, Client, Command, RichEmbed } from '.';
|
import { AccountUtil, Client, Command, PaginationEmbed } from '.';
|
||||||
import { ModerationInterface, AccountInterface, Account } from '../models';
|
import { AccountInterface, ModerationInterface } from '../models';
|
||||||
|
import { Certificate } from '../../types/x509';
|
||||||
|
|
||||||
|
enum TechnicianNameFormatOpt {
|
||||||
|
Full = 0,
|
||||||
|
Partial,
|
||||||
|
Basic
|
||||||
|
}
|
||||||
|
|
||||||
export default class Util {
|
export default class Util {
|
||||||
public client: Client;
|
public client: Client;
|
||||||
|
@ -43,7 +52,7 @@ export default class Util {
|
||||||
cmd.stdout.on('data', writeFunction);
|
cmd.stdout.on('data', writeFunction);
|
||||||
cmd.stderr.on('data', writeFunction);
|
cmd.stderr.on('data', writeFunction);
|
||||||
cmd.on('error', writeFunction);
|
cmd.on('error', writeFunction);
|
||||||
cmd.once('close', (code, signal) => {
|
cmd.once('close', (code) => {
|
||||||
cmd.stdout.off('data', writeFunction);
|
cmd.stdout.off('data', writeFunction);
|
||||||
cmd.stderr.off('data', writeFunction);
|
cmd.stderr.off('data', writeFunction);
|
||||||
cmd.off('error', writeFunction);
|
cmd.off('error', writeFunction);
|
||||||
|
@ -93,7 +102,6 @@ export default class Util {
|
||||||
return returnArray;
|
return returnArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves a command
|
* Resolves a command
|
||||||
* @param query Command input
|
* @param query Command input
|
||||||
|
@ -123,34 +131,37 @@ export default class Util {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handleError(error: Error, message?: Message, command?: Command): Promise<void> {
|
public async handleError(error: Error | any, message?: Message, command?: Command): Promise<void> {
|
||||||
try {
|
try {
|
||||||
this.client.signale.error(error);
|
this.client.signale.error(error);
|
||||||
const info = { content: `\`\`\`js\n${error.stack}\n\`\`\``, embed: null };
|
const info = { content: `\`\`\`js\n${error.stack}\n\`\`\``, embed: null };
|
||||||
if (message) {
|
if (message) {
|
||||||
const embed = new RichEmbed();
|
const embed = new MessageEmbed();
|
||||||
embed.setColor('FF0000');
|
embed.setColor('#FF0000');
|
||||||
embed.setAuthor(`Error caused by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL);
|
embed.setAuthor(`Error caused by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL());
|
||||||
embed.setTitle('Message content');
|
embed.setTitle('Message content');
|
||||||
embed.setDescription(message.content);
|
embed.setDescription(message.content);
|
||||||
embed.addField('User', `${message.author.mention} (\`${message.author.id}\`)`, true);
|
embed.addField('User', `${message.author.toString()} (\`${message.author.id}\`)`, true);
|
||||||
embed.addField('Channel', message.channel.mention, true);
|
embed.addField('Channel', `<#${message.channel.id}>`, true);
|
||||||
let guild: string;
|
let guild: string;
|
||||||
if (message.channel instanceof PrivateChannel || message.channel instanceof GroupChannel) guild = '@me';
|
if (message.channel instanceof DMChannel) guild = '@me';
|
||||||
else guild = message.channel.guild.id;
|
else guild = message.guild.id;
|
||||||
embed.addField('Message link', `[Click here](https://discordapp.com/channels/${guild}/${message.channel.id}/${message.id})`, true);
|
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;
|
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);
|
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 (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) {
|
} catch (err) {
|
||||||
this.client.signale.error(err);
|
this.client.signale.error(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public createPaginationEmbed = PaginationEmbed;
|
||||||
|
|
||||||
public splitFields(fields: { name: string, value: string, inline?: boolean }[]): { name: string, value: string, inline?: boolean }[][] {
|
public splitFields(fields: { name: string, value: string, inline?: boolean }[]): { name: string, value: string, inline?: boolean }[][] {
|
||||||
let index = 0;
|
let index = 0;
|
||||||
const array: {name: string, value: string, inline?: boolean}[][] = [[]];
|
const array: {name: string, value: string, inline?: boolean}[][] = [[]];
|
||||||
|
@ -178,18 +189,15 @@ export default class Util {
|
||||||
return arrayString;
|
return arrayString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async createHash(password: string): Promise<string> {
|
public async createHash(password: string): Promise<string> {
|
||||||
const hashed = await this.exec(`mkpasswd -m sha-512 "${password}"`);
|
return this.exec(`mkpasswd -m sha-512 "${password}"`);
|
||||||
return hashed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public isValidEmail(email: string): boolean {
|
public isValidEmail(email: string): boolean {
|
||||||
const checkAt = email.indexOf('@');
|
const checkAt = email.indexOf('@');
|
||||||
if (checkAt < 1) return false;
|
if (checkAt < 1) return false;
|
||||||
const checkDomain = email.indexOf('.', checkAt + 2);
|
const checkDomain = email.indexOf('.', checkAt + 2);
|
||||||
if (checkDomain < checkAt) return false;
|
return checkDomain >= checkAt;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public randomPassword(): string {
|
public randomPassword(): string {
|
||||||
|
@ -217,13 +225,15 @@ export default class Util {
|
||||||
this.exec(`deluser ${username} --remove-home --backup-to /management/Archives && rm -rf -R ${account.homepath}`),
|
this.exec(`deluser ${username} --remove-home --backup-to /management/Archives && rm -rf -R ${account.homepath}`),
|
||||||
this.client.db.Account.deleteOne({ username }),
|
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
|
// @ts-ignore
|
||||||
await Promise.all(tasks);
|
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> {
|
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) => {
|
return new Promise((res, rej) => {
|
||||||
const func = (Msg: Message) => {
|
const func = (Msg: Message) => {
|
||||||
if (filter(Msg) === false) return;
|
if (filter(Msg) === false) return;
|
||||||
|
@ -250,7 +260,7 @@ export default class Util {
|
||||||
*
|
*
|
||||||
* `4` - Delete
|
* `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 moderatorID = moderator.id;
|
||||||
const account = await this.client.db.Account.findOne({ $or: [{ username: user }, { userID: user }] });
|
const account = await this.client.db.Account.findOne({ $or: [{ username: user }, { userID: user }] });
|
||||||
if (!account) return Promise.reject(new Error(`Account ${user} not found`));
|
if (!account) return Promise.reject(new Error(`Account ${user} not found`));
|
||||||
|
@ -270,44 +280,65 @@ export default class Util {
|
||||||
} else date = null;
|
} else date = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const expiration = { date, processed };
|
const expiration: { date: Date; processed: boolean } = { date, processed };
|
||||||
|
|
||||||
logInput.expiration = expiration;
|
logInput.expiration = expiration;
|
||||||
const log = new this.client.db.Moderation(logInput);
|
const log = new this.client.db.Moderation(logInput);
|
||||||
await log.save();
|
await log.save();
|
||||||
|
|
||||||
let embedTitle: string;
|
let embedTitle: string;
|
||||||
let color: string;
|
let color: ColorResolvable;
|
||||||
let archType: string;
|
let archType: string;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
default: archType = 'Staff'; embedTitle = 'Cloud Account | Generic'; color = '0892e1'; break;
|
default: archType = 'Staff'; embedTitle = 'Cloud Account | Generic'; color = '#0892e1'; break;
|
||||||
case 0: archType = 'Technician'; embedTitle = 'Cloud Account | Create'; color = '00ff00'; break;
|
case 0: archType = 'Technician'; embedTitle = 'Cloud Account | Create'; color = '#00ff00'; break;
|
||||||
case 1: archType = 'Technician'; embedTitle = 'Account Warning | Warn'; color = 'ffff00'; break;
|
case 1: archType = 'Technician'; embedTitle = 'Account Warning | Warn'; color = '#ffff00'; break;
|
||||||
case 2: archType = 'Technician'; embedTitle = 'Account Infraction | Lock'; color = 'ff6600'; break;
|
case 2: archType = 'Technician'; embedTitle = 'Account Infraction | Lock'; color = '#ff6600'; break;
|
||||||
case 3: archType = 'Technician'; embedTitle = 'Account Reclaim | Unlock'; color = '0099ff'; break;
|
case 3: archType = 'Technician'; embedTitle = 'Account Infraction | Unlock'; color = '#0099ff'; break;
|
||||||
case 4: archType = 'Director'; embedTitle = 'Cloud Account | Delete'; color = 'ff0000'; break;
|
case 4: archType = 'Manager'; embedTitle = 'Cloud Account | Delete'; color = '#ff0000'; break;
|
||||||
}
|
}
|
||||||
const req = await axios.get('https://loc.sh/int/directory');
|
const req = await axios.get('https://loc.sh/int/directory');
|
||||||
const find = req.data.find((mem) => mem.userID === moderator.id);
|
const find = req.data.find((mem) => mem.userID === moderator.id);
|
||||||
const embed = new RichEmbed()
|
const embed = new MessageEmbed()
|
||||||
.setTitle(embedTitle)
|
.setTitle(embedTitle)
|
||||||
.setColor(color)
|
.setColor(color)
|
||||||
.addField('User', `${username} | <@${userID}>`, true)
|
.addField('User', `${username} | <@${userID}>`, true)
|
||||||
.addField(archType, moderatorID === this.client.user.id ? 'SYSTEM' : `${moderator.username}, ${find.pn.join(', ')} (<@${moderatorID}>)`, true)
|
.addField(archType, moderatorID === this.client.user.id ? 'SYSTEM' : `${moderator.username}${find.isManager ? ' [k] ' : ' '}(<@${moderatorID}>)`, true)
|
||||||
.setFooter(this.client.user.username, this.client.user.avatarURL)
|
.setFooter(this.client.user.username, this.client.user.avatarURL())
|
||||||
.setTimestamp();
|
.setTimestamp();
|
||||||
if (reason) embed.addField('Reason', reason || 'Not specified');
|
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'}`);
|
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);
|
return Promise.resolve(log);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getAcctHash(userpath: string) {
|
public async getTechnicianName(tech: User, format: TechnicianNameFormatOpt = TechnicianNameFormatOpt.Full) {
|
||||||
try {
|
if (!tech) return 'SYSTEM (UT)';
|
||||||
return fs.readFileSync(`${userpath}/.securesign/auth`).toString();
|
if (tech.id === this.client.user.id) return 'SYSTEM (SELF)';
|
||||||
} catch (error) {
|
|
||||||
return null;
|
const req = await axios.get('https://loc.sh/int/directory');
|
||||||
|
const find = req.data.find((mem) => mem.userID === tech.id);
|
||||||
|
if (!find) return 'SYSTEM (UF)';
|
||||||
|
if (format === TechnicianNameFormatOpt.Full) {
|
||||||
|
return `${tech.username}${find.isManager ? ' [k]' : ''}${find.title ? ` (${find.title} / ${find.dept})` : ` (${find.dept})`} <<@${tech.id}>>`;
|
||||||
|
} else if (format === TechnicianNameFormatOpt.Partial) {
|
||||||
|
return `${tech.username}${find.isManager ? ' [k]' : ''}${find.title ? ` (${find.title})` : ` (${find.dept})`} <<@${tech.id}>>`;
|
||||||
}
|
}
|
||||||
|
return `${tech.username}${find.isManager ? ' [k]' : ''} <<@${tech.id}>>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public parseCertificate(pem: string) {
|
||||||
|
return axios.post<Certificate>('https://certapi.libraryofcode.org/parse', pem)
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public upload(text: string, extension = 'txt') {
|
||||||
|
return hastebin(text, {
|
||||||
|
url: 'https://snippets.cloud.libraryofcode.org',
|
||||||
|
extension,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ export { default as Event } from './Event';
|
||||||
export { default as Handler } from './Handler';
|
export { default as Handler } from './Handler';
|
||||||
export { default as LocalStorage } from './LocalStorage';
|
export { default as LocalStorage } from './LocalStorage';
|
||||||
export { default as Report } from './Report';
|
export { default as Report } from './Report';
|
||||||
export { default as RichEmbed } from './RichEmbed';
|
|
||||||
export { default as Route } from './Route';
|
export { default as Route } from './Route';
|
||||||
export { default as Security } from './Security';
|
export { default as Security } from './Security';
|
||||||
export { default as Server } from './Server';
|
export { default as Server } from './Server';
|
||||||
export { default as Util } from './Util';
|
export { default as Util } from './Util';
|
||||||
|
export { default as PaginationEmbed } from './PaginationEmbed';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from 'eris';
|
import { Message, TextChannel } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class AddReferral extends Command {
|
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.');
|
if (!account) return this.error(message.channel, 'Cannot find user.');
|
||||||
|
|
||||||
await account.updateOne({ $inc: { totalReferrals: 1 } });
|
await account.updateOne({ $inc: { totalReferrals: 1 } });
|
||||||
this.client.getDMChannel(account.userID).then((chan) => {
|
this.client.users.fetch(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.');
|
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(() => {});
|
}).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) {
|
} catch (error) {
|
||||||
return this.client.util.handleError(error, message, this);
|
return this.client.util.handleError(error, message, this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class Announce extends Command {
|
export default class Announce extends Command {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* eslint-disable no-useless-escape */
|
/* eslint-disable no-useless-escape */
|
||||||
import { Message } from 'eris';
|
import { Message, MessageEmbed, TextChannel } from 'discord.js';
|
||||||
import { Client, Command, Report, RichEmbed } from '../class';
|
import { Client, Command, Report } from '../class';
|
||||||
|
|
||||||
export default class ApplyT2 extends Command {
|
export default class ApplyT2 extends Command {
|
||||||
constructor(client: Client) {
|
constructor(client: Client) {
|
||||||
|
@ -24,7 +24,7 @@ export default class ApplyT2 extends Command {
|
||||||
const decision = await Report.tier2(account.userID, this.client.config.internalKey);
|
const decision = await Report.tier2(account.userID, this.client.config.internalKey);
|
||||||
if (decision.status === 'SUCCESS') {
|
if (decision.status === 'SUCCESS') {
|
||||||
await loading.delete();
|
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') {
|
if (decision.decision === 'APPROVED') {
|
||||||
const tier = await this.client.db.Tier.findOne({ id: 2 });
|
const tier = await this.client.db.Tier.findOne({ id: 2 });
|
||||||
|
@ -33,21 +33,23 @@ export default class ApplyT2 extends Command {
|
||||||
} else {
|
} else {
|
||||||
await account.updateOne({ $set: { tier: 2 } });
|
await account.updateOne({ $set: { tier: 2 } });
|
||||||
}
|
}
|
||||||
const embed = new RichEmbed();
|
const embed = new MessageEmbed();
|
||||||
embed.setTitle('Cloud Account | Tier Change');
|
embed.setTitle('Cloud Account | Tier Change');
|
||||||
embed.setColor('#0099ff');
|
embed.setColor('#0099ff');
|
||||||
embed.addField('User', `${account.username} | <@${account.userID}>`, true);
|
embed.addField('User', `${account.username} | <@${account.userID}>`, true);
|
||||||
embed.addField('Technician', 'SYSTEM', true);
|
embed.addField('Technician', 'SYSTEM', true);
|
||||||
embed.addField('Old Tier -> New Tier', `${account.tier} -> 2`, 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();
|
embed.setTimestamp();
|
||||||
await this.client.util.sendMessageToUserTerminal(account.username, 'A technician has changed your tier to 2').catch(() => { });
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
await loading.delete();
|
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) {
|
} catch (error) {
|
||||||
return this.client.util.handleError(error, message, this);
|
return this.client.util.handleError(error, message, this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class AuthReferral extends Command {
|
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]);
|
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, '') }] });
|
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.');
|
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.');
|
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' });
|
const token = jwt.sign(
|
||||||
this.client.getDMChannel(referrer.userID).then(async (chan) => {
|
{ staffUserID: message.author.id,
|
||||||
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}>`);
|
referralCode: referrer.referralCode,
|
||||||
await chan.createMessage(`\`${token}\``);
|
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(() => {
|
}).catch(() => {
|
||||||
this.error(message.channel, 'Could not DM referrer.');
|
this.error(message.channel, 'Could not DM referrer.');
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
import Bearer_Revoke from './bearer_revoke';
|
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.');
|
if (!account) return this.error(message.channel, 'Account not found.');
|
||||||
// eslint-disable-next-line no-underscore-dangle
|
// eslint-disable-next-line no-underscore-dangle
|
||||||
const bearer = await this.client.server.security.createBearer(account._id);
|
const bearer = await this.client.server.security.createBearer(account._id);
|
||||||
const dm = await this.client.getDMChannel(message.author.id);
|
const dm = await this.client.users.fetch(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 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.');
|
this.success(message.channel, 'Bearer token sent to direct messages.');
|
||||||
return setTimeout(() => {
|
return setTimeout(() => {
|
||||||
msg.delete();
|
msg.delete();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class Bearer_Revoke extends Command {
|
export default class Bearer_Revoke extends Command {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class Cloudflare extends Command {
|
export default class Cloudflare extends Command {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Message, PrivateChannel, GroupChannel } from 'eris';
|
import { GuildMember, Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
import { LINUX_USERNAME_REGEX } from '../class/AccountUtil';
|
||||||
|
|
||||||
export default class CreateAccount extends Command {
|
export default class CreateAccount extends Command {
|
||||||
constructor(client: Client) {
|
constructor(client: Client) {
|
||||||
|
@ -20,10 +21,10 @@ export default class CreateAccount extends Command {
|
||||||
|
|
||||||
public async run(message: Message, args: string[]) {
|
public async run(message: Message, args: string[]) {
|
||||||
try {
|
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 (!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.');
|
const member: GuildMember = await message.guild.members.fetch(args[0]);
|
||||||
if (message.channel.guild.members.get(args[0]).bot) return this.error(message.channel, 'I cannot create accounts for bots.');
|
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] });
|
const checkUser = await this.client.db.Account.findOne({ userID: args[0] });
|
||||||
if (checkUser) return this.error(message.channel, `<@${args[0]}> already has an account.`);
|
if (checkUser) return this.error(message.channel, `<@${args[0]}> already has an account.`);
|
||||||
const checkEmail = await this.client.db.Account.findOne({ emailAddress: args[1] });
|
const checkEmail = await this.client.db.Account.findOne({ emailAddress: args[1] });
|
||||||
|
@ -32,7 +33,7 @@ export default class CreateAccount extends Command {
|
||||||
if (checkAccount) return this.error(message.channel, 'Account already exists with this username.');
|
if (checkAccount) return this.error(message.channel, 'Account already exists with this username.');
|
||||||
|
|
||||||
if (!this.client.util.isValidEmail(args[1])) return this.error(message.channel, 'Invalid email address supplied.');
|
if (!this.client.util.isValidEmail(args[1])) return this.error(message.channel, 'Invalid email address supplied.');
|
||||||
if (!/^[a-z][-a-z0-9]*$/.test(args[2])) return this.error(message.channel, 'Invalid username supplied.');
|
if (!LINUX_USERNAME_REGEX.test(args[2])) return this.error(message.channel, 'Invalid username supplied.');
|
||||||
|
|
||||||
const confirmation = await this.loading(message.channel, 'Creating account...');
|
const confirmation = await this.loading(message.channel, 'Creating account...');
|
||||||
const data = await this.client.util.accounts.createAccount({ userID: args[0], username: args[2], emailAddress: args[1] }, message.author.id);
|
const data = await this.client.util.accounts.createAccount({ userID: args[0], username: args[2], emailAddress: args[1] }, message.author.id);
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
import Create from './cwg_create';
|
import Create from './cwg_create';
|
||||||
import Data from './cwg_data';
|
import Data from './cwg_data';
|
||||||
import Delete from './cwg_delete';
|
import Delete from './cwg_delete';
|
||||||
import UpdateCert from './cwg_updatecert';
|
import UpdateCert from './cwg_updatecert';
|
||||||
|
import SelvServ from './cwg_selfserv';
|
||||||
|
|
||||||
export default class CWG extends Command {
|
export default class CWG extends Command {
|
||||||
constructor(client: Client) {
|
constructor(client: Client) {
|
||||||
|
@ -11,8 +12,8 @@ export default class CWG extends Command {
|
||||||
this.name = 'cwg';
|
this.name = 'cwg';
|
||||||
this.description = 'Manages aspects for the CWG.';
|
this.description = 'Manages aspects for the CWG.';
|
||||||
this.usage = `Run ${this.client.config.prefix}${this.name} [subcommand] for usage information`;
|
this.usage = `Run ${this.client.config.prefix}${this.name} [subcommand] for usage information`;
|
||||||
this.permissions = { roles: ['446104438969466890'] };
|
// this.permissions = { roles: ['446104438969466890'] };
|
||||||
this.subcmds = [Create, Data, Delete, UpdateCert];
|
this.subcmds = [Create, Data, Delete, UpdateCert, SelvServ];
|
||||||
this.enabled = true;
|
this.enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,222 +1,225 @@
|
||||||
import fs, { writeFile, unlink } from 'fs-extra';
|
import fs, { writeFile, unlink, symlink } from 'fs-extra';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { randomBytes } from 'crypto';
|
import { randomBytes } from 'crypto';
|
||||||
import { Message } from 'eris';
|
import { Message, MessageEmbed, TextChannel } from 'discord.js';
|
||||||
import { AccountInterface } from '../models';
|
import { AccountInterface } from '../models';
|
||||||
import { Client, Command, RichEmbed } from '../class';
|
import { Client, Command } from '../class';
|
||||||
import { parseCertificate } from '../functions';
|
import { parseCertificate } from '../functions';
|
||||||
|
|
||||||
export default class CWG_Create extends Command {
|
export default class CWG_Create extends Command {
|
||||||
public urlRegex: RegExp;
|
public urlRegex: RegExp;
|
||||||
|
|
||||||
constructor(client: Client) {
|
constructor(client: Client) {
|
||||||
super(client);
|
super(client);
|
||||||
this.name = 'create';
|
this.name = 'create';
|
||||||
this.description = 'Bind a domain to the CWG';
|
this.description = 'Bind a domain to the CWG';
|
||||||
this.usage = `${this.client.config.prefix}cwg create [User ID | Username] [Domain] [Port] <Cert Chain> <Private Key> || Use snippets raw URL`;
|
this.usage = `${this.client.config.prefix}cwg create <User ID | Username> <Domain> <Port> [Cert Chain] [Private Key] || Use snippets raw URL`;
|
||||||
this.permissions = { roles: ['662163685439045632', '701454780828221450'] };
|
this.permissions = { roles: ['662163685439045632', '701454780828221450'] };
|
||||||
this.aliases = ['bind'];
|
this.aliases = ['bind'];
|
||||||
this.enabled = true;
|
this.enabled = true;
|
||||||
this.urlRegex = /^[a-zA-Z0-9\-._~:/?#[\]@!$&'()*+,;=]+$/;
|
this.urlRegex = /^[a-zA-Z0-9\-._~:/?#[\]@!$&'()*+,;=]+$/;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async run(message: Message, args: string[]) {
|
public async run(message: Message, args: string[]) {
|
||||||
/*
|
/*
|
||||||
args[0] should be the user's ID OR account username; required
|
args[0] should be the user's ID OR account username; required
|
||||||
args[1] should be the domain; required
|
args[1] should be the domain; required
|
||||||
args[2] should be the port; required
|
args[2] should be the port; required
|
||||||
args[3] should be the path to the x509 certificate; not required
|
args[3] should be the path to the x509 certificate; not required
|
||||||
args[4] should be the path to the x509 key; not required
|
args[4] should be the path to the x509 key; not required
|
||||||
*/
|
*/
|
||||||
try {
|
try {
|
||||||
if (!args[2]) return this.client.commands.get('help').run(message, ['cwg', this.name]);
|
if (!args[2]) return this.client.commands.get('help').run(message, ['cwg', this.name]);
|
||||||
|
|
||||||
if (!this.urlRegex.test(args[1])) return this.error(message.channel, 'Invalid URL supplied.');
|
if (!this.urlRegex.test(args[1])) return this.error(message.channel, 'Invalid URL supplied.');
|
||||||
if (Number(args[2]) <= 1024 || Number(args[2]) >= 65535) return this.error(message.channel, 'Port must be greater than 1024 and less than 65535.');
|
if (Number(args[2]) <= 1024 || Number(args[2]) >= 65535) return this.error(message.channel, 'Port must be greater than 1024 and less than 65535.');
|
||||||
if (!args[1].endsWith('.cloud.libraryofcode.org') && !args[4]) return this.error(message.channel, 'Certificate Chain and Private Key are required for custom domains.');
|
if (!args[1].endsWith('.cloud.libraryofcode.org') && !args[4]) return this.error(message.channel, 'Certificate Chain and Private Key are required for custom domains.');
|
||||||
|
|
||||||
const account = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { userID: args[0] }] });
|
const account = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { userID: args[0] }] });
|
||||||
if (!account) return this.error(message.channel, 'Cannot locate account.');
|
if (!account) return this.error(message.channel, 'Cannot locate account.');
|
||||||
|
|
||||||
if (await this.client.db.Domain.exists({ domain: args[1] })) return this.error(message.channel, 'This domain already exists.');
|
if (await this.client.db.Domain.exists({ domain: args[1] })) return this.error(message.channel, 'This domain already exists.');
|
||||||
|
|
||||||
if (await this.client.db.Domain.exists({ port: Number(args[2]) })) {
|
if (await this.client.db.Domain.exists({ port: Number(args[2]) })) {
|
||||||
let answer: Message;
|
let answer: Message;
|
||||||
try {
|
try {
|
||||||
answer = await this.client.util.messageCollector(
|
answer = await this.client.util.messageCollector(
|
||||||
message,
|
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,
|
30000, true, ['y', 'n'], (msg) => msg.author.id === message.author.id && msg.channel.id === message.channel.id,
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return this.error(message.channel, 'Bind request cancelled.');
|
return this.error(message.channel, 'Bind request cancelled.');
|
||||||
}
|
}
|
||||||
if (answer.content === 'n') return this.error(message.channel, 'Bind request cancelled.');
|
if (answer.content === 'n') return this.error(message.channel, 'Bind request cancelled.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const edit = await this.loading(message.channel, 'Binding domain...');
|
const edit = await this.loading(message.channel, 'Binding domain...');
|
||||||
|
|
||||||
let certs: { cert?: string, key?: string } = {};
|
let certs: { cert?: string, key?: string } = {};
|
||||||
if (!args[1].endsWith('.cloud.libraryofcode.org')) {
|
if (!args[1].endsWith('.cloud.libraryofcode.org')) {
|
||||||
const urls = args.slice(3, 5);
|
const urls = args.slice(3, 5);
|
||||||
if (urls.some((l) => !l.includes('snippets.cloud.libraryofcode.org/raw/'))) return this.error(message.channel, 'Invalid snippets URL. Make sure to use https://snippets.libraryofcode.org/raw/*.');
|
if (urls.some((l) => !l.includes('snippets.cloud.libraryofcode.org/raw/'))) return this.error(message.channel, 'Invalid snippets URL. Make sure to use https://snippets.cloud.libraryofcode.org/raw/*.');
|
||||||
|
|
||||||
const tasks = urls.map((l) => axios({ method: 'GET', url: l }));
|
const tasks = urls.map((l) => axios({ method: 'GET', url: l }));
|
||||||
const response = await Promise.all(tasks);
|
const response = await Promise.all(tasks);
|
||||||
const certAndPrivateKey: string[] = response.map((r) => r.data);
|
const certAndPrivateKey: string[] = response.map((r) => r.data);
|
||||||
|
|
||||||
if (!this.isValidCertificateChain(certAndPrivateKey[0])) return this.error(message.channel, 'The certificate chain provided is invalid.');
|
if (!this.isValidCertificateChain(certAndPrivateKey[0])) return this.error(message.channel, 'The certificate chain provided is invalid.');
|
||||||
if (!this.isValidPrivateKey(certAndPrivateKey[1])) return this.error(message.channel, 'The private key provided is invalid.');
|
if (!this.isValidPrivateKey(certAndPrivateKey[1])) return this.error(message.channel, 'The private key provided is invalid.');
|
||||||
|
|
||||||
certs = { cert: certAndPrivateKey[0], key: certAndPrivateKey[1] };
|
certs = { cert: certAndPrivateKey[0], key: certAndPrivateKey[1] };
|
||||||
} else {
|
} else {
|
||||||
certs.cert = await fs.readFile('/etc/ssl/private/cloud-libraryofcode-org.chain.crt', { encoding: 'utf8' });
|
certs.cert = await fs.readFile('/etc/ssl/private/cloud-libraryofcode-org.chain.crt', { encoding: 'utf8' });
|
||||||
certs.key = await fs.readFile('/etc/ssl/private/cloud-libraryofcode-org.key', { encoding: 'utf8' });
|
certs.key = await fs.readFile('/etc/ssl/private/cloud-libraryofcode-org.key', { encoding: 'utf8' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const domain = await this.createDomain(account, args[1], Number(args[2]), certs);
|
const domain = await this.createDomain(account, args[1], Number(args[2]), certs);
|
||||||
|
|
||||||
const tasks = [message.delete(), this.client.util.exec('systemctl reload nginx')];
|
const tasks = [message.delete(), this.client.util.exec('systemctl reload nginx')];
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
await Promise.all(tasks);
|
await Promise.all(tasks);
|
||||||
|
|
||||||
const embed = new RichEmbed()
|
const embed = new MessageEmbed()
|
||||||
.setTitle('Domain Creation')
|
.setTitle('Domain Creation')
|
||||||
.setColor(3066993)
|
.setColor(3066993)
|
||||||
.addField('Account Username', `${account.username} | <@${account.userID}>`, true)
|
.addField('Account Username', `${account.username} | <@${account.userID}>`, true)
|
||||||
.addField('Account ID', account.id, true)
|
.addField('Technician', await this.client.util.getTechnicianName(message.author), true)
|
||||||
.addField('Technician', `<@${message.author.id}>`, true)
|
.addField('Domain', domain.domain, true)
|
||||||
.addField('Domain', domain.domain, true)
|
.addField('Port', String(domain.port), true);
|
||||||
.addField('Port', String(domain.port), true);
|
|
||||||
|
const certPath = `/opt/CloudServices/temp/${randomBytes(5).toString('hex')}`;
|
||||||
const certPath = `/opt/CloudServices/temp/${randomBytes(5).toString('hex')}`;
|
await writeFile(certPath, certs.cert, { encoding: 'utf8' });
|
||||||
await writeFile(certPath, certs.cert, { encoding: 'utf8' });
|
const cert = await parseCertificate(this.client, certPath);
|
||||||
const cert = await parseCertificate(this.client, certPath);
|
|
||||||
|
embed.addField('Certificate Issuer', cert.issuer.organizationName, true)
|
||||||
embed.addField('Certificate Issuer', cert.issuer.organizationName, true)
|
.addField('Certificate Subject', cert.subject.commonName, true)
|
||||||
.addField('Certificate Subject', cert.subject.commonName, true)
|
.setFooter(this.client.user.username, this.client.user.avatarURL())
|
||||||
.setFooter(this.client.user.username, this.client.user.avatarURL)
|
.setTimestamp(new Date(message.createdTimestamp));
|
||||||
.setTimestamp(new Date(message.timestamp));
|
|
||||||
|
const completed = [
|
||||||
const completed = [
|
edit.edit(`***${this.client.stores.emojis.success} Successfully bound ${domain.domain} to port ${domain.port} for ${account.username}.***`),
|
||||||
edit.edit(`***${this.client.stores.emojis.success} Successfully binded ${domain.domain} to port ${domain.port} for ${account.username}.***`),
|
(this.client.channels.cache.get('580950455581147146') as TextChannel).send({ embeds: [embed] }),
|
||||||
this.client.createMessage('580950455581147146', { embed }),
|
this.client.users.fetch(account.userID).then((r) => r.send({ embeds: [embed] })),
|
||||||
this.client.getDMChannel(account.userID).then((r) => r.createMessage({ embed })),
|
this.client.util.transport.sendMail({
|
||||||
this.client.util.transport.sendMail({
|
to: account.emailAddress,
|
||||||
to: account.emailAddress,
|
from: 'Library of Code sp-us | Support Team <help@libraryofcode.org>',
|
||||||
from: 'Library of Code sp-us | Support Team <help@libraryofcode.org>',
|
subject: 'Your domain has been bound',
|
||||||
subject: 'Your domain has been binded',
|
html: `
|
||||||
html: `
|
<h1>Library of Code sp-us | Cloud Services</h1>
|
||||||
<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 bound.
|
||||||
<p>Hello, this is an email informing you that a new domain under your account has been binded.
|
Information is below.</p>
|
||||||
Information is below.</p>
|
<b>Domain:</b> ${domain.domain}<br>
|
||||||
<b>Domain:</b> ${domain.domain}<br>
|
<b>Port:</b> ${domain.port}<br>
|
||||||
<b>Port:</b> ${domain.port}<br>
|
<b>Certificate Issuer:</b> ${cert.issuer.organizationName}<br>
|
||||||
<b>Certificate Issuer:</b> ${cert.issuer.organizationName}<br>
|
<b>Certificate Subject:</b> ${cert.subject.commonName}<br>
|
||||||
<b>Certificate Subject:</b> ${cert.subject.commonName}<br>
|
<b>Responsible Engineer:</b> ${message.author.username}#${message.author.discriminator}<br><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>
|
||||||
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>
|
||||||
<b><i>Library of Code sp-us | Support Team</i></b>
|
`,
|
||||||
`,
|
}),
|
||||||
}),
|
];
|
||||||
];
|
|
||||||
|
if (!domain.domain.includes('cloud.libraryofcode.org')) {
|
||||||
if (!domain.domain.includes('cloud.libraryofcode.org')) {
|
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.`;
|
||||||
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.users.fetch(account.userID).then((r) => r.send(content)));
|
||||||
completed.push(this.client.getDMChannel(account.userID).then((r) => r.createMessage(content)));
|
}
|
||||||
}
|
|
||||||
|
return Promise.all(completed);
|
||||||
return Promise.all(completed);
|
} catch (err) {
|
||||||
} catch (err) {
|
this.client.util.handleError(err, message, this);
|
||||||
this.client.util.handleError(err, message, this);
|
const tasks = [fs.unlink(`/etc/nginx/sites-enabled/${args[1]}`), fs.unlink(`/etc/nginx/sites-available/${args[1]}`), this.client.db.Domain.deleteMany({ domain: args[1] })];
|
||||||
const tasks = [fs.unlink(`/etc/nginx/sites-enabled/${args[1]}`), fs.unlink(`/etc/nginx/sites-available/${args[1]}`), this.client.db.Domain.deleteMany({ domain: args[1] })];
|
return Promise.allSettled(tasks);
|
||||||
return Promise.allSettled(tasks);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* This function binds a domain to a port on the CWG.
|
||||||
* This function binds a domain to a port on the CWG.
|
* @param account The account of the user.
|
||||||
* @param account The account of the user.
|
* @param subdomain The domain to use. `mydomain.cloud.libraryofcode.org`
|
||||||
* @param subdomain The domain to use. `mydomain.cloud.libraryofcode.org`
|
* @param port The port to use, must be between 1024 and 65535.
|
||||||
* @param port The port to use, must be between 1024 and 65535.
|
* @param x509Certificate The contents the certificate and key files.
|
||||||
* @param x509Certificate The contents the certificate and key files.
|
* @example await CWG.createDomain(account, 'mydomain.cloud.libraryofcode.org', 6781);
|
||||||
* @example await CWG.createDomain(account, 'mydomain.cloud.libraryofcode.org', 6781);
|
*/
|
||||||
*/
|
public async createDomain(account: AccountInterface, domain: string, port: number, x509Certificate: { cert?: string, key?: string }) {
|
||||||
public async createDomain(account: AccountInterface, domain: string, port: number, x509Certificate: { cert?: string, key?: string }) {
|
try {
|
||||||
try {
|
if (port <= 1024 || port >= 65535) throw new RangeError(`Port range must be between 1024 and 65535, received ${port}.`);
|
||||||
if (port <= 1024 || port >= 65535) throw new RangeError(`Port range must be between 1024 and 65535, received ${port}.`);
|
if (await this.client.db.Domain.exists({ domain })) throw new Error(`Domain ${domain} already exists in the database.`);
|
||||||
if (await this.client.db.Domain.exists({ domain })) throw new Error(`Domain ${domain} already exists in the database.`);
|
if (!await this.client.db.Account.exists({ userID: account.userID })) throw new Error(`Cannot find account ${account.userID}.`);
|
||||||
if (!await this.client.db.Account.exists({ userID: account.userID })) throw new Error(`Cannot find account ${account.userID}.`);
|
let x509: { cert: string, key: string };
|
||||||
let x509: { cert: string, key: string };
|
if (x509Certificate) {
|
||||||
if (x509Certificate) {
|
x509 = await this.createCertAndPrivateKey(domain, x509Certificate.cert, x509Certificate.key);
|
||||||
x509 = await this.createCertAndPrivateKey(domain, x509Certificate.cert, x509Certificate.key);
|
} else {
|
||||||
} else {
|
x509 = {
|
||||||
x509 = {
|
cert: '/etc/ssl/private/cloud-libraryofcode-org.chain.crt',
|
||||||
cert: '/etc/ssl/private/cloud-libraryofcode-org.chain.crt',
|
key: '/etc/ssl/private/cloud-libraryofcode-org.key',
|
||||||
key: '/etc/ssl/private/cloud-libraryofcode-org.key',
|
};
|
||||||
};
|
}
|
||||||
}
|
let cfg = await fs.readFile('/opt/CloudServices/src/static/nginx.conf', { encoding: 'utf8' });
|
||||||
let cfg = await fs.readFile('/opt/CloudServices/src/static/nginx.conf', { encoding: 'utf8' });
|
cfg = cfg.replace(/\[DOMAIN]/g, domain);
|
||||||
cfg = cfg.replace(/\[DOMAIN]/g, domain);
|
cfg = cfg.replace(/\[PORT]/g, String(port));
|
||||||
cfg = cfg.replace(/\[PORT]/g, String(port));
|
cfg = cfg.replace(/\[CERTIFICATE]/g, x509.cert);
|
||||||
cfg = cfg.replace(/\[CERTIFICATE]/g, x509.cert);
|
cfg = cfg.replace(/\[KEY]/g, x509.key);
|
||||||
cfg = cfg.replace(/\[KEY]/g, x509.key);
|
await fs.writeFile(`/etc/nginx/sites-available/${domain}`, cfg, { encoding: 'utf8' });
|
||||||
await fs.writeFile(`/etc/nginx/sites-available/${domain}`, cfg, { encoding: 'utf8' });
|
await fs.symlink(`/etc/nginx/sites-available/${domain}`, `/etc/nginx/sites-enabled/${domain}`);
|
||||||
await fs.symlink(`/etc/nginx/sites-available/${domain}`, `/etc/nginx/sites-enabled/${domain}`);
|
const entry = new this.client.db.Domain({
|
||||||
const entry = new this.client.db.Domain({
|
account,
|
||||||
account,
|
domain,
|
||||||
domain,
|
port,
|
||||||
port,
|
x509,
|
||||||
x509,
|
enabled: true,
|
||||||
enabled: true,
|
});
|
||||||
});
|
return entry.save();
|
||||||
return entry.save();
|
} catch (error) {
|
||||||
} catch (error) {
|
const tasks = [fs.unlink(`/etc/nginx/sites-enabled/${domain}`), fs.unlink(`/etc/nginx/sites-available/${domain}`), this.client.db.Domain.deleteMany({ domain })];
|
||||||
const tasks = [fs.unlink(`/etc/nginx/sites-enabled/${domain}`), fs.unlink(`/etc/nginx/sites-available/${domain}`), this.client.db.Domain.deleteMany({ domain })];
|
await Promise.allSettled(tasks);
|
||||||
await Promise.allSettled(tasks);
|
throw error;
|
||||||
throw error;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public async createCertAndPrivateKey(domain: string, certChain: string, privateKey: string) {
|
||||||
public async createCertAndPrivateKey(domain: string, certChain: string, privateKey: string) {
|
// if (!this.isValidCertificateChain(certChain)) throw new Error('Invalid Certificate Chain');
|
||||||
if (!this.isValidCertificateChain(certChain)) throw new Error('Invalid Certificate Chain');
|
// if (!this.isValidPrivateKey(privateKey)) throw new Error('Invalid Private Key');
|
||||||
// if (!this.isValidPrivateKey(privateKey)) throw new Error('Invalid Private Key');
|
const path = `/opt/CloudServices/temp/${domain}`;
|
||||||
const path = `/opt/CloudServices/temp/${domain}`;
|
await Promise.all([writeFile(`${path}.chain.crt`, certChain), writeFile(`${path}.key.pem`, privateKey)]);
|
||||||
await Promise.all([writeFile(`${path}.chain.crt`, certChain), writeFile(`${path}.key.pem`, privateKey)]);
|
if (!this.isMatchingPair(`${path}.chain.crt`, `${path}.key.pem`)) {
|
||||||
if (!this.isMatchingPair(`${path}.chain.crt`, `${path}.key.pem`)) {
|
await Promise.all([unlink(`${path}.chain.crt`), unlink(`${path}.key.pem`)]);
|
||||||
await Promise.all([unlink(`${path}.chain.crt`), unlink(`${path}.key.pem`)]);
|
throw new Error('Certificate and Private Key do not match');
|
||||||
throw new Error('Certificate and Private Key do not match');
|
}
|
||||||
}
|
|
||||||
|
if (domain.endsWith('.cloud.libraryofcode.org')) {
|
||||||
await Promise.all([writeFile(`/etc/ssl/certs/cwg/${domain}.chain.crt`, certChain), writeFile(`/etc/ssl/private/cwg/${domain}.key.pem`, privateKey)]);
|
await Promise.all([symlink('/etc/ssl/private/cloud-libraryofcode-org.chain.crt', `/etc/ssl/certs/cwg/${domain}.chain.crt`), symlink('/etc/ssl/private/cloud-libraryofcode-org.key', `/etc/ssl/private/cwg/${domain}.key.pem`)]);
|
||||||
return { cert: `/etc/ssl/certs/cwg/${domain}.chain.crt`, key: `/etc/ssl/private/cwg/${domain}.key.pem` };
|
} else {
|
||||||
}
|
await Promise.all([writeFile(`/etc/ssl/certs/cwg/${domain}.chain.crt`, certChain), writeFile(`/etc/ssl/private/cwg/${domain}.key.pem`, privateKey)]);
|
||||||
|
}
|
||||||
public checkOccurance(text: string, query: string) {
|
return { cert: `/etc/ssl/certs/cwg/${domain}.chain.crt`, key: `/etc/ssl/private/cwg/${domain}.key.pem` };
|
||||||
return (text.match(new RegExp(query, 'g')) || []).length;
|
}
|
||||||
}
|
|
||||||
|
public checkOccurrence(text: string, query: string) {
|
||||||
public isValidCertificateChain(cert: string) {
|
return (text.match(new RegExp(query, 'g')) || []).length;
|
||||||
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;
|
public isValidCertificateChain(cert: string) {
|
||||||
if (this.checkOccurance(cert.replace(/^\s+|\s+$/g, ''), '-----END CERTIFICATE-----') !== 2) return false;
|
if (!cert.replace(/^\s+|\s+$/g, '').startsWith('-----BEGIN CERTIFICATE-----')) return false;
|
||||||
return true;
|
if (!cert.replace(/^\s+|\s+$/g, '').endsWith('-----END CERTIFICATE-----')) 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;
|
||||||
public isValidPrivateKey(key: string) {
|
return true;
|
||||||
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;
|
public isValidPrivateKey(key: string) {
|
||||||
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 (!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;
|
||||||
return true;
|
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.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;
|
||||||
public async isMatchingPair(cert: string, privateKey: string) {
|
return true;
|
||||||
const result: string = await this.client.util.exec(`${__dirname}/../bin/checkCertSignatures ${cert} ${privateKey}`);
|
}
|
||||||
const { ok }: { ok: boolean } = JSON.parse(result);
|
|
||||||
return ok;
|
public async isMatchingPair(cert: string, privateKey: string) {
|
||||||
}
|
const result: string = await this.client.util.exec(`${__dirname}/../bin/checkCertSignatures ${cert} ${privateKey}`);
|
||||||
}
|
const { ok }: { ok: boolean } = JSON.parse(result);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import x509 from 'x509';
|
import { Message, MessageEmbed } from 'discord.js';
|
||||||
import { createPaginationEmbed } from 'eris-pagination';
|
import { Client, Command, PaginationEmbed } from '../class';
|
||||||
import { Message } from 'eris';
|
|
||||||
import { Client, Command, RichEmbed } from '../class';
|
|
||||||
|
|
||||||
export default class CWG_Data extends Command {
|
export default class CWG_Data extends Command {
|
||||||
constructor(client: Client) {
|
constructor(client: Client) {
|
||||||
super(client);
|
super(client);
|
||||||
this.name = 'data';
|
this.name = 'data';
|
||||||
this.description = 'Check CWG data';
|
this.description = 'Check CWG data';
|
||||||
this.usage = `${this.client.config.prefix}cwg data [Domain | Port]`;
|
this.usage = `${this.client.config.prefix}cwg data <Domain | Port>`;
|
||||||
this.permissions = { roles: ['662163685439045632', '701454780828221450'] };
|
this.permissions = { roles: ['662163685439045632', '701454780828221450'] };
|
||||||
this.enabled = true;
|
this.enabled = true;
|
||||||
}
|
}
|
||||||
|
@ -30,24 +28,31 @@ export default class CWG_Data extends Command {
|
||||||
}
|
}
|
||||||
return this.error(message.channel, 'The domain or port you provided could not be found.');
|
return this.error(message.channel, 'The domain or port you provided could not be found.');
|
||||||
}
|
}
|
||||||
const embeds = dom.map((domain) => {
|
const embeds = await Promise.all(dom.map(async (domain) => {
|
||||||
const cert = fs.readFileSync(domain.x509.cert, { encoding: 'utf8' });
|
let pem: string;
|
||||||
const embed = new RichEmbed();
|
try {
|
||||||
|
pem = fs.readFileSync(domain.x509.cert, { encoding: 'utf8' });
|
||||||
|
} catch {
|
||||||
|
pem = fs.readFileSync('/etc/ssl/private/cloud-libraryofcode-org.chain.crt', { encoding: 'utf8' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const cert = await this.client.util.parseCertificate(pem);
|
||||||
|
const embed = new MessageEmbed();
|
||||||
embed.setTitle('Domain Information');
|
embed.setTitle('Domain Information');
|
||||||
embed.addField('Account Username', domain.account.username, true);
|
embed.addField('Account Username', domain.account.username, true);
|
||||||
embed.addField('Account ID', domain.account.userID, true);
|
embed.addField('Account ID', domain.account.userID, true);
|
||||||
embed.addField('Domain', domain.domain, true);
|
embed.addField('Domain', domain.domain, true);
|
||||||
embed.addField('Port', String(domain.port), true);
|
embed.addField('Port', String(domain.port), true);
|
||||||
embed.addField('Certificate Issuer', x509.getIssuer(cert).organizationName, true);
|
embed.addField('Certificate Issuer', `${cert.issuer.organization[0]} (${cert.issuer.commonName})` || 'N/A', true);
|
||||||
embed.addField('Certificate Subject', x509.getSubject(cert).commonName, true);
|
embed.addField('Certificate Subject', cert.subject.commonName || 'N/A', true);
|
||||||
embed.addField('Certificate Expiration Date', moment(x509.parseCert(cert).notAfter).format('dddd, MMMM Do YYYY, h:mm:ss A'), true);
|
embed.addField('Certificate Expiration Date', cert.notAfter ? moment(cert.notAfter).format('dddd, MMMM Do YYYY, h:mm:ss A') || 'N/A' : 'N/A', true);
|
||||||
embed.setFooter(this.client.user.username, this.client.user.avatarURL);
|
embed.setFooter(this.client.user.username, this.client.user.avatarURL());
|
||||||
embed.setTimestamp();
|
embed.setTimestamp();
|
||||||
return embed;
|
return embed;
|
||||||
});
|
}));
|
||||||
this.client.signale.log(embeds);
|
this.client.signale.log(embeds);
|
||||||
if (embeds.length === 1) return message.channel.createMessage({ embed: embeds[0] });
|
if (embeds.length === 1) return message.channel.send({ embeds: [embeds[0]] });
|
||||||
return createPaginationEmbed(message, embeds);
|
return this.client.util.createPaginationEmbed(message, embeds);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return this.client.util.handleError(error, message, this);
|
return this.client.util.handleError(error, message, this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,60 +1,61 @@
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Message } from 'eris';
|
import { Message, MessageEmbed, TextChannel } from 'discord.js';
|
||||||
import { Client, Command, RichEmbed } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class CWG_Delete extends Command {
|
export default class CWG_Delete extends Command {
|
||||||
constructor(client: Client) {
|
constructor(client: Client) {
|
||||||
super(client);
|
super(client);
|
||||||
this.name = 'delete';
|
this.name = 'delete';
|
||||||
this.description = 'Unbind a domain to the CWG';
|
this.description = 'Unbind a domain from the CWG';
|
||||||
this.usage = `${this.client.config.prefix}cwg delete [Domain | Port]`;
|
this.usage = `${this.client.config.prefix}cwg delete [Domain | Port]`;
|
||||||
this.permissions = { roles: ['662163685439045632'] };
|
this.permissions = { roles: ['662163685439045632', '701454780828221450'] };
|
||||||
this.aliases = ['unbind'];
|
this.aliases = ['unbind'];
|
||||||
this.enabled = true;
|
this.enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async run(message: Message, args: string[]) {
|
public async run(message: Message, args: string[]) {
|
||||||
try {
|
try {
|
||||||
if (!args[0]) return this.client.commands.get('help').run(message, ['cwg', this.name]);
|
if (!args[0]) return this.client.commands.get('help').run(message, ['cwg', this.name]);
|
||||||
const domain = await this.client.db.Domain.findOne({ $or: [{ domain: args[0] }, { port: Number(args[0]) || 0 }] });
|
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.');
|
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 edit = await this.loading(message.channel, 'Deleting domain...');
|
||||||
const embed = new RichEmbed();
|
const embed = new MessageEmbed();
|
||||||
embed.setTitle('Domain Deletion');
|
embed.setTitle('Domain Deletion');
|
||||||
embed.addField('Account Username', `${domain.account.username} | <@${domain.account.userID}>`, true);
|
embed.addField('Account Username', `${domain.account.username} | <@${domain.account.userID}>`, true);
|
||||||
embed.addField('Account ID', domain.account.userID, true);
|
embed.addField('Technician', await this.client.util.getTechnicianName(message.author), true);
|
||||||
embed.addField('Domain', domain.domain, true);
|
embed.addField('Domain', domain.domain, true);
|
||||||
embed.addField('Port', String(domain.port), 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();
|
embed.setTimestamp();
|
||||||
if (domain.domain.includes('cloud.libraryofcode.org')) {
|
if (domain.domain.includes('cloud.libraryofcode.org')) {
|
||||||
const resultID = await axios({
|
const resultID = await axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: `https://api.cloudflare.com/client/v4/zones/5e82fc3111ed4fbf9f58caa34f7553a7/dns_records?name=${domain.domain}`,
|
url: `https://api.cloudflare.com/client/v4/zones/5e82fc3111ed4fbf9f58caa34f7553a7/dns_records?name=${domain.domain}`,
|
||||||
headers: { Authorization: `Bearer ${this.client.config.cloudflare}` },
|
headers: { Authorization: `Bearer ${this.client.config.cloudflare}` },
|
||||||
});
|
});
|
||||||
this.client.signale.debug(resultID.data);
|
this.client.signale.debug(resultID.data);
|
||||||
if (resultID.data.result[0]) {
|
if (resultID.data.result[0]) {
|
||||||
const recordID = resultID.data.result[0].id;
|
const recordID = resultID.data.result[0].id;
|
||||||
await axios({
|
await axios({
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
url: `https://api.cloudflare.com/client/v4/zones/5e82fc3111ed4fbf9f58caa34f7553a7/dns_records/${recordID}`,
|
url: `https://api.cloudflare.com/client/v4/zones/5e82fc3111ed4fbf9f58caa34f7553a7/dns_records/${recordID}`,
|
||||||
headers: { Authorization: `Bearer ${this.client.config.cloudflare}` },
|
headers: { Authorization: `Bearer ${this.client.config.cloudflare}` },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await fs.unlink(`/etc/nginx/sites-enabled/${domain.domain}`);
|
await fs.unlink(`/etc/nginx/sites-enabled/${domain.domain}`);
|
||||||
await fs.unlink(`/etc/nginx/sites-available/${domain.domain}`);
|
await fs.unlink(`/etc/nginx/sites-available/${domain.domain}`);
|
||||||
} catch (e) { this.client.signale.error(e); }
|
} catch (e) { this.client.signale.error(e); }
|
||||||
await this.client.db.Domain.deleteOne({ domain: domain.domain });
|
await this.client.db.Domain.deleteOne({ domain: domain.domain });
|
||||||
await this.client.util.exec('systemctl reload nginx');
|
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.***`);
|
edit.edit(`***${this.client.stores.emojis.success} Domain ${domain.domain} with port ${domain.port} has been successfully deleted.***`);
|
||||||
this.client.createMessage('580950455581147146', { embed });
|
const ch = this.client.channels.cache.get('580950455581147146') as TextChannel;
|
||||||
return this.client.getDMChannel(domain.account.userID).then((channel) => channel.createMessage({ embed })).catch(() => {});
|
ch.send({ embeds: [embed] });
|
||||||
} catch (error) {
|
return this.client.users.fetch(domain.account.userID).then((u) => u.send({ embeds: [embed] })).catch(() => {});
|
||||||
return this.client.util.handleError(error, message, this);
|
} catch (error) {
|
||||||
}
|
return this.client.util.handleError(error, message, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,227 @@
|
||||||
|
import fs, { writeFile, unlink, symlink } from 'fs-extra';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { randomBytes } from 'crypto';
|
||||||
|
import { Message, MessageEmbed, TextChannel } from 'discord.js';
|
||||||
|
import { AccountInterface } from '../models';
|
||||||
|
import { Client, Command } from '../class';
|
||||||
|
import { parseCertificate } from '../functions';
|
||||||
|
|
||||||
|
export default class CWG_SelfService extends Command {
|
||||||
|
public urlRegex: RegExp;
|
||||||
|
|
||||||
|
constructor(client: Client) {
|
||||||
|
super(client);
|
||||||
|
this.name = 'selfserv';
|
||||||
|
this.description = 'Creates a subdomain on your account. Do not include the entire subdomain: if you want `mydomain.cloud.libraryofcode.org`, just supply `mydomain` in the command parameters.\nYou must receive an authentication token from the Instant Application Service, see `?apply` for more information.';
|
||||||
|
this.usage = `${this.client.config.prefix}cwg selfserv <desired subdomain> <authentication token>`;
|
||||||
|
this.aliases = ['ss'];
|
||||||
|
this.enabled = true;
|
||||||
|
this.urlRegex = /^[a-zA-Z0-9\-._~:/?#[\]@!$&'()*+,;=]+$/;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async run(message: Message, args: string[]) {
|
||||||
|
if (!args[0].endsWith('.cloud.libraryofcode.org')) return this.error(message.channel, 'Only subdomains may be created with this command. If you\'d like to use a custom domain please contact a Technician.');
|
||||||
|
const reqDomain = `${escape(args[0])}.cloud.libraryofcode.org`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!args[1]) return this.client.commands.get('help').run(message, ['cwg', this.name]);
|
||||||
|
|
||||||
|
const account = await this.client.db.Account.findOne({ userID: message.author.id });
|
||||||
|
if (!account) return this.error(message.channel, 'Cannot locate account.');
|
||||||
|
|
||||||
|
if (!this.domainTextValidation(args[0])) return this.error(message.channel, 'The domain value you provided is invalid.');
|
||||||
|
|
||||||
|
if (await this.client.db.Domain.exists({ domain: reqDomain })) return this.error(message.channel, 'This domain already exists.');
|
||||||
|
|
||||||
|
if (await this.checkAuthorizationToken(message.author.id, args[1]) !== true) return this.error(message.channel, 'Permission denied.');
|
||||||
|
|
||||||
|
const edit = await this.loading(message.channel, 'Binding domain...');
|
||||||
|
|
||||||
|
const certs: { cert?: string, key?: string } = {};
|
||||||
|
certs.cert = await fs.readFile('/etc/ssl/private/cloud-libraryofcode-org.chain.crt', { encoding: 'utf8' });
|
||||||
|
certs.key = await fs.readFile('/etc/ssl/private/cloud-libraryofcode-org.key', { encoding: 'utf8' });
|
||||||
|
|
||||||
|
let port: number;
|
||||||
|
try {
|
||||||
|
port = await this.getPort();
|
||||||
|
} catch {
|
||||||
|
return this.error(message.channel, 'Unable to acquire port automatically. Please contact a Technician.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const domain = await this.createDomain(account, reqDomain, port, certs);
|
||||||
|
|
||||||
|
const tasks = [message.delete(), this.client.util.exec('systemctl reload nginx')];
|
||||||
|
// @ts-ignore
|
||||||
|
await Promise.all(tasks);
|
||||||
|
|
||||||
|
const embed = new MessageEmbed()
|
||||||
|
.setTitle('Domain Creation')
|
||||||
|
.setColor(3066993)
|
||||||
|
.addField('Account Username', `${account.username} | <@${account.userID}>`, true)
|
||||||
|
.addField('Technician', 'SYSTEM', true)
|
||||||
|
.addField('Domain', domain.domain, true)
|
||||||
|
.addField('Port', String(domain.port), true);
|
||||||
|
|
||||||
|
const certPath = `/opt/CloudServices/temp/${randomBytes(5).toString('hex')}`;
|
||||||
|
await writeFile(certPath, certs.cert, { encoding: 'utf8' });
|
||||||
|
const cert = await parseCertificate(this.client, certPath);
|
||||||
|
|
||||||
|
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.createdTimestamp));
|
||||||
|
|
||||||
|
const completed = [
|
||||||
|
edit.edit(`***${this.client.stores.emojis.success} Your subdomain has been created, please check your DMs or email address for more information.***`),
|
||||||
|
(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>',
|
||||||
|
replyTo: 'Dept of Engineering <engineering@libraryofcode.org>',
|
||||||
|
subject: 'Your domain has been created',
|
||||||
|
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 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> SYSTEM<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>
|
||||||
|
`,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
return Promise.all(completed);
|
||||||
|
} catch (err) {
|
||||||
|
this.client.util.handleError(err, message, this);
|
||||||
|
const tasks = [fs.unlink(`/etc/nginx/sites-enabled/${reqDomain}`), fs.unlink(`/etc/nginx/sites-available/${reqDomain}`), this.client.db.Domain.deleteMany({ domain: reqDomain })];
|
||||||
|
return Promise.allSettled(tasks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getPort() {
|
||||||
|
const port = Number(await this.client.redis.get('cwgsspc'));
|
||||||
|
if (await this.client.db.Domain.exists({ port })) throw new Error('Error retreiving port.');
|
||||||
|
if (port >= Number(process.env.MAX_CWG_PORT)) throw new Error('Error retrieving port.');
|
||||||
|
await this.client.redis.incr('cwgsspc');
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public domainTextValidation(domain: string) {
|
||||||
|
if (domain.length >= 25) return false;
|
||||||
|
return /[A-Za-z0-9](?:[A-Za-z0-9-]{0,19}[A-Za-z0-9])?/.test(domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async checkAuthorizationToken(userID: string, token: string) {
|
||||||
|
try {
|
||||||
|
const resp = await axios({
|
||||||
|
url: 'https://comm.libraryofcode.org/internal/check-cwg-self-auth',
|
||||||
|
params: {
|
||||||
|
userID,
|
||||||
|
token,
|
||||||
|
internalKey: this.client.config.internalKey,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (resp?.status === 204) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function binds a domain to a port on the CWG.
|
||||||
|
* @param account The account of the user.
|
||||||
|
* @param subdomain The domain to use. `mydomain.cloud.libraryofcode.org`
|
||||||
|
* @param port The port to use, must be between 1024 and 65535.
|
||||||
|
* @param x509Certificate The contents the certificate and key files.
|
||||||
|
* @example await CWG.createDomain(account, 'mydomain.cloud.libraryofcode.org', 6781);
|
||||||
|
*/
|
||||||
|
public async createDomain(account: AccountInterface, domain: string, port: number, x509Certificate: { cert?: string, key?: string }) {
|
||||||
|
try {
|
||||||
|
if (port <= 1024 || port >= 65535) throw new RangeError(`Port range must be between 1024 and 65535, received ${port}.`);
|
||||||
|
if (await this.client.db.Domain.exists({ domain })) throw new Error(`Domain ${domain} already exists in the database.`);
|
||||||
|
if (!await this.client.db.Account.exists({ userID: account.userID })) throw new Error(`Cannot find account ${account.userID}.`);
|
||||||
|
let x509: { cert: string, key: string };
|
||||||
|
if (x509Certificate) {
|
||||||
|
x509 = await this.createCertAndPrivateKey(domain, x509Certificate.cert, x509Certificate.key);
|
||||||
|
} else {
|
||||||
|
x509 = {
|
||||||
|
cert: '/etc/ssl/private/cloud-libraryofcode-org.chain.crt',
|
||||||
|
key: '/etc/ssl/private/cloud-libraryofcode-org.key',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let cfg = await fs.readFile('/opt/CloudServices/src/static/nginx.conf', { encoding: 'utf8' });
|
||||||
|
cfg = cfg.replace(/\[DOMAIN]/g, domain);
|
||||||
|
cfg = cfg.replace(/\[PORT]/g, String(port));
|
||||||
|
cfg = cfg.replace(/\[CERTIFICATE]/g, x509.cert);
|
||||||
|
cfg = cfg.replace(/\[KEY]/g, x509.key);
|
||||||
|
await fs.writeFile(`/etc/nginx/sites-available/${domain}`, cfg, { encoding: 'utf8' });
|
||||||
|
await fs.symlink(`/etc/nginx/sites-available/${domain}`, `/etc/nginx/sites-enabled/${domain}`);
|
||||||
|
const entry = new this.client.db.Domain({
|
||||||
|
account,
|
||||||
|
domain,
|
||||||
|
port,
|
||||||
|
x509,
|
||||||
|
enabled: true,
|
||||||
|
});
|
||||||
|
return entry.save();
|
||||||
|
} catch (error) {
|
||||||
|
const tasks = [fs.unlink(`/etc/nginx/sites-enabled/${domain}`), fs.unlink(`/etc/nginx/sites-available/${domain}`), this.client.db.Domain.deleteMany({ domain })];
|
||||||
|
await Promise.allSettled(tasks);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createCertAndPrivateKey(domain: string, certChain: string, privateKey: string) {
|
||||||
|
if (!this.isValidCertificateChain(certChain)) throw new Error('Invalid Certificate Chain');
|
||||||
|
// if (!this.isValidPrivateKey(privateKey)) throw new Error('Invalid Private Key');
|
||||||
|
const path = `/opt/CloudServices/temp/${domain}`;
|
||||||
|
await Promise.all([writeFile(`${path}.chain.crt`, certChain), writeFile(`${path}.key.pem`, privateKey)]);
|
||||||
|
if (!this.isMatchingPair(`${path}.chain.crt`, `${path}.key.pem`)) {
|
||||||
|
await Promise.all([unlink(`${path}.chain.crt`), unlink(`${path}.key.pem`)]);
|
||||||
|
throw new Error('Certificate and Private Key do not match');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (domain.endsWith('.cloud.libraryofcode.org')) {
|
||||||
|
await Promise.all([symlink('/etc/ssl/private/cloud-libraryofcode-org.chain.crt', `/etc/ssl/certs/cwg/${domain}.chain.crt`), symlink('/etc/ssl/private/cloud-libraryofcode-org.key', `/etc/ssl/private/cwg/${domain}.key.pem`)]);
|
||||||
|
} else {
|
||||||
|
await Promise.all([writeFile(`/etc/ssl/certs/cwg/${domain}.chain.crt`, certChain), writeFile(`/etc/ssl/private/cwg/${domain}.key.pem`, privateKey)]);
|
||||||
|
}
|
||||||
|
return { cert: `/etc/ssl/certs/cwg/${domain}.chain.crt`, key: `/etc/ssl/private/cwg/${domain}.key.pem` };
|
||||||
|
}
|
||||||
|
|
||||||
|
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.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.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async isMatchingPair(cert: string, privateKey: string) {
|
||||||
|
const result: string = await this.client.util.exec(`${__dirname}/../bin/checkCertSignatures ${cert} ${privateKey}`);
|
||||||
|
const { ok }: { ok: boolean } = JSON.parse(result);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import { writeFile, unlink } from 'fs-extra';
|
import { writeFile, unlink } from 'fs-extra';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class CWG_UpdateCert extends Command {
|
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;
|
return (text.match(new RegExp(query, 'g')) || []).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public isValidCertificateChain(cert: string) {
|
public isValidCertificateChain(cert: string) {
|
||||||
if (!cert.replace(/^\s+|\s+$/g, '').startsWith('-----BEGIN CERTIFICATE-----')) return false;
|
if (!cert.replace(/^\s+|\s+$/g, '').startsWith('-----BEGIN CERTIFICATE-----')) return false;
|
||||||
if (!cert.replace(/^\s+|\s+$/g, '').endsWith('-----END 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.checkOccurrence(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, ''), '-----END CERTIFICATE-----') !== 2) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public isValidPrivateKey(key: string) {
|
public isValidPrivateKey(key: string) {
|
||||||
if (!key.replace(/^\s+|\s+$/g, '').startsWith('-----BEGIN PRIVATE KEY-----')) return false;
|
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 (!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.checkOccurrence(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, ''), '-----END PRIVATE KEY-----') !== 1) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Message, PrivateChannel } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import uuid from 'uuid/v4';
|
import { v4 as uuid } from 'uuid';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class DeleteAccount extends Command {
|
export default class DeleteAccount extends Command {
|
||||||
|
@ -18,9 +18,9 @@ export default class DeleteAccount extends Command {
|
||||||
try {
|
try {
|
||||||
if (!args[1]) return this.client.commands.get('help').run(message, [this.name]);
|
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] }] });
|
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;
|
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 pad = (number: number, amount: number): string => '0'.repeat(amount - number.toString().length) + number;
|
||||||
const randomNumber = Math.floor(Math.random() * 9999);
|
const randomNumber = Math.floor(Math.random() * 9999);
|
||||||
|
@ -28,17 +28,17 @@ export default class DeleteAccount extends Command {
|
||||||
try {
|
try {
|
||||||
await this.client.util.messageCollector(message,
|
await this.client.util.messageCollector(message,
|
||||||
`***Please confirm that you are permanently deleting ${username}'s account by entering ${verify}. This action cannot be reversed.***`,
|
`***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) {
|
} catch (error) {
|
||||||
if (error.message.includes('Did not supply')) return message;
|
if (error.message.includes('Did not supply')) return message;
|
||||||
throw error;
|
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 reason = args.slice(1).join(' ');
|
||||||
const logInput = { username, userID, logID: uuid(), moderatorID: message.author.id, type: 4, date: new Date(), reason: null };
|
const logInput = { username, userID, logID: uuid(), moderatorID: message.author.id, type: 4, date: new Date(), reason: null };
|
||||||
if (reason) logInput.reason = reason;
|
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);
|
await this.client.util.deleteAccount(username);
|
||||||
message.delete().catch(() => {});
|
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>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>Reason:</b> ${reason}</p>
|
||||||
<p><b>Director:</b> ${message.author.username}</p>
|
<p><b>Director:</b> ${message.author.username}</p>
|
||||||
|
|
||||||
<b><i>Library of Code sp-us | Support Team</i></b>
|
<b><i>Library of Code sp-us | Support Team</i></b>
|
||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { randomBytes } from 'crypto';
|
import { randomBytes } from 'crypto';
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class EmailCode extends Command {
|
export default class EmailCode extends Command {
|
||||||
|
@ -31,7 +31,7 @@ export default class EmailCode extends Command {
|
||||||
<h3>Want to support us?</h3>
|
<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>
|
<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>
|
<b><i>Library of Code sp-us | Support Team</i></b>
|
||||||
</body>
|
</body>
|
||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
return this.success(message.channel, `Code: \`${code}\` | Email Address: ${args[0]}`);
|
return this.success(message.channel, `Code: \`${code}\` | Email Address: ${args[0]}`);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* eslint-disable no-eval */
|
/* eslint-disable no-eval */
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { inspect } from 'util';
|
import { inspect } from 'util';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ export default class Eval extends Command {
|
||||||
this.aliases = ['e'];
|
this.aliases = ['e'];
|
||||||
this.description = 'Evaluate JavaScript code';
|
this.description = 'Evaluate JavaScript code';
|
||||||
this.enabled = true;
|
this.enabled = true;
|
||||||
this.permissions = { users: ['253600545972027394', '278620217221971968', '239261547959025665'] };
|
this.permissions = { users: ['253600545972027394', '278620217221971968', '239261547959025665', '143414786913206272'] };
|
||||||
this.guildOnly = false;
|
this.guildOnly = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,22 +45,21 @@ export default class Eval extends Command {
|
||||||
evaled = error.stack;
|
evaled = error.stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
evaled = evaled.replace(new RegExp(this.client.config.token, 'gi'), 'juul');
|
evaled = evaled.replace(new RegExp(this.client.config.token, 'gi'), 'token');
|
||||||
evaled = evaled.replace(new RegExp(this.client.config.emailPass, 'gi'), 'juul');
|
evaled = evaled.replace(new RegExp(this.client.config.emailPass, 'gi'), 'emailPass');
|
||||||
evaled = evaled.replace(new RegExp(this.client.config.cloudflare, 'gi'), 'juul');
|
evaled = evaled.replace(new RegExp(this.client.config.cloudflare, 'gi'), 'cloudflare');
|
||||||
|
|
||||||
|
|
||||||
const display = this.client.util.splitString(evaled, 1975);
|
const display = this.client.util.splitString(evaled, 1975);
|
||||||
if (display[5]) {
|
if (display[5]) {
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.post('https://snippets.cloud.libraryofcode.org/documents', display.join(''));
|
const { data } = await axios.post('https://snippets.cloud.libraryofcode.org/documents', display.join(''));
|
||||||
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) {
|
} 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) {
|
} catch (error) {
|
||||||
return this.client.util.handleError(error, message, this);
|
return this.client.util.handleError(error, message, this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ export default class Exec extends Command {
|
||||||
this.description = 'Executes command';
|
this.description = 'Executes command';
|
||||||
this.aliases = ['ex'];
|
this.aliases = ['ex'];
|
||||||
this.enabled = true;
|
this.enabled = true;
|
||||||
this.permissions = { users: ['253600545972027394', '278620217221971968', '239261547959025665'] };
|
this.permissions = { users: ['253600545972027394', '278620217221971968', '239261547959025665', '143414786913206272'] };
|
||||||
this.guildOnly = false;
|
this.guildOnly = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ export default class Exec extends Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
await response.delete();
|
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) {
|
} catch (error) {
|
||||||
return this.client.util.handleError(error, message, this);
|
return this.client.util.handleError(error, message, this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class GetReferral extends Command {
|
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 });
|
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.');
|
if (!account) return this.error(message.channel, 'You do not have a Cloud Services account.');
|
||||||
|
|
||||||
return this.client.getDMChannel(message.author.id).then((chan) => {
|
return this.client.users.fetch(message.author.id).then((u) => {
|
||||||
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'}\``);
|
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(() => this.error(message.channel, 'Could not send you a DM.'));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return this.client.util.handleError(err, message, this);
|
return this.client.util.handleError(err, message, this);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { Message } from 'eris';
|
import { Message, MessageEmbed } from 'discord.js';
|
||||||
import { createPaginationEmbed } from 'eris-pagination';
|
import { Client, Command } from '../class';
|
||||||
import { Client, Command, RichEmbed } from '../class';
|
|
||||||
|
|
||||||
export default class Help extends Command {
|
export default class Help extends Command {
|
||||||
constructor(client: Client) {
|
constructor(client: Client) {
|
||||||
|
@ -23,42 +22,43 @@ export default class Help extends Command {
|
||||||
const perms: string[] = [];
|
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}>`).join(', ');
|
||||||
if (allowedRoles) { allowedRoles = `**Roles:** ${allowedRoles}`; perms.push(allowedRoles); }
|
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}>`).join(', ');
|
||||||
if (allowedUsers) { allowedUsers = `**Users:** ${allowedUsers}`; perms.push(allowedUsers); }
|
if (allowedUsers) { allowedUsers = `**Users:** ${allowedUsers}`; perms.push(allowedUsers); }
|
||||||
const displayedPerms = perms.length ? `**Permissions:**\n${perms.join('\n')}` : '';
|
const displayedPerms = perms.length ? `**Permissions:**\n${perms.join('\n')}` : '';
|
||||||
return { name: `${this.client.config.prefix}${c.name}`, value: `**Description:** ${c.description}\n**Aliases:** ${aliases}\n**Usage:** ${c.usage}\n${displayedPerms}`, inline: false };
|
return { name: `${this.client.config.prefix}${c.name}`, value: `**Description:** ${c.description}\n**Aliases:** ${aliases}\n**Usage:** ${c.usage}\n${displayedPerms}`, inline: false };
|
||||||
});
|
});
|
||||||
|
|
||||||
const splitCommands = this.client.util.splitFields(commands);
|
const splitCommands = this.client.util.splitFields(commands);
|
||||||
const cmdPages: RichEmbed[] = [];
|
const cmdPages: MessageEmbed[] = [];
|
||||||
splitCommands.forEach((splitCmd) => {
|
splitCommands.forEach((splitCmd) => {
|
||||||
const embed = new RichEmbed();
|
const embed = new MessageEmbed();
|
||||||
embed.setTimestamp(); embed.setFooter(`Requested by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL);
|
embed.setTimestamp();
|
||||||
embed.setAuthor(`${this.client.user.username}#${this.client.user.discriminator}`, this.client.user.avatarURL);
|
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}`);
|
embed.setDescription(`Command list for ${this.client.user.username}`);
|
||||||
splitCmd.forEach((c) => embed.addField(c.name, c.value, c.inline));
|
splitCmd.forEach((c) => embed.addField(c.name, c.value, c.inline));
|
||||||
return cmdPages.push(embed);
|
return cmdPages.push(embed);
|
||||||
});
|
});
|
||||||
if (cmdPages.length === 1) return message.channel.createMessage({ embed: cmdPages[0] });
|
if (cmdPages.length === 1) return message.channel.send({ embeds: [cmdPages[0]] });
|
||||||
return createPaginationEmbed(message, cmdPages);
|
return this.client.util.createPaginationEmbed(message, cmdPages);
|
||||||
}
|
}
|
||||||
const resolved = await this.client.util.resolveCommand(args, message);
|
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 { cmd } = resolved;
|
||||||
const perms: string[] = [];
|
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}>`).join(', ');
|
||||||
if (allowedRoles) { allowedRoles = `**Roles:** ${allowedRoles}`; perms.push(allowedRoles); }
|
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}>`).join(', ');
|
||||||
if (allowedUsers) { allowedUsers = `**Users:** ${allowedUsers}`; perms.push(allowedUsers); }
|
if (allowedUsers) { allowedUsers = `**Users:** ${allowedUsers}`; perms.push(allowedUsers); }
|
||||||
const displayedPerms = perms.length ? `\n**Permissions:**\n${perms.join('\n')}` : '';
|
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 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 subcommands = cmd.subcommands.size ? `\n**Subcommands:** ${cmd.subcommands.map((s) => `${cmd.name} ${s.name}`).join(', ')}` : '';
|
||||||
const embed = new RichEmbed();
|
const embed = new MessageEmbed();
|
||||||
embed.setTimestamp(); embed.setFooter(`Requested by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL);
|
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);
|
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}`;
|
const description = `**Description**: ${cmd.description}\n**Usage:** ${cmd.usage}${aliases}${displayedPerms}${subcommands}`;
|
||||||
embed.setDescription(description);
|
embed.setDescription(description);
|
||||||
message.channel.createMessage({ embed });
|
message.channel.send({ embeds: [embed] });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.client.util.handleError(error, message, this);
|
this.client.util.handleError(error, message, this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ export { default as systemd } from './systemd';
|
||||||
export { default as tier } from './tier';
|
export { default as tier } from './tier';
|
||||||
export { default as unban } from './unban';
|
export { default as unban } from './unban';
|
||||||
export { default as unlock } from './unlock';
|
export { default as unlock } from './unlock';
|
||||||
|
export { default as usermod } from './usermod';
|
||||||
export { default as users } from './users';
|
export { default as users } from './users';
|
||||||
export { default as warn } from './warn';
|
export { default as warn } from './warn';
|
||||||
export { default as whois } from './whois';
|
export { default as whois } from './whois';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Message } from 'eris';
|
import { Message, MessageEmbed } from 'discord.js';
|
||||||
import { totalmem } from 'os';
|
import { totalmem } from 'os';
|
||||||
import { Client, Command, RichEmbed } from '../class';
|
import { Client, Command } from '../class';
|
||||||
import { version as erisVersion } from '../../node_modules/eris/package.json';
|
import { version as discordjsVersion } from '../../node_modules/discord.js/package.json';
|
||||||
import { version as expressVersion } from '../../node_modules/express/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 mongooseVersion } from '../../node_modules/mongoose/package.json';
|
||||||
import { version as ioredisVersion } from '../../node_modules/ioredis/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) {
|
public async run(message: Message) {
|
||||||
try {
|
try {
|
||||||
const embed = new RichEmbed();
|
const embed = new MessageEmbed();
|
||||||
embed.setTitle('Information');
|
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('Language(s)', '<:ts:604565354554982401> TypeScript, <:Go:703449475405971466> Go', true);
|
||||||
embed.addField('Runtime', `Node (${process.version})`, 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('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('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('HTTP Server Library', `Express (${expressVersion})`, true);
|
||||||
embed.addField('Database Library', `MongoDB w/ Mongoose ODM (${mongooseVersion})`, true);
|
embed.addField('Database Library', `MongoDB w/ Mongoose ODM (${mongooseVersion})`, true);
|
||||||
embed.addField('Cache Library', `Redis w/ IORedis (${ioredisVersion})`, 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('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);
|
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) {
|
} catch (err) {
|
||||||
return this.client.util.handleError(err, message, this);
|
return this.client.util.handleError(err, message, this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Message } from 'eris';
|
import { Message, MessageEmbed } from 'discord.js';
|
||||||
import { Client, Command, RichEmbed } from '../class';
|
import { Client, Command } from '../class';
|
||||||
import { dataConversion } from '../functions';
|
import { dataConversion } from '../functions';
|
||||||
import setRamNotification from './limits_setramnotification';
|
import setRamNotification from './limits_setramnotification';
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ export default class Limits extends Command {
|
||||||
public async run(message: Message) {
|
public async run(message: Message) {
|
||||||
try {
|
try {
|
||||||
const tiers = await this.client.db.Tier.find();
|
const tiers = await this.client.db.Tier.find();
|
||||||
const embed = new RichEmbed();
|
const embed = new MessageEmbed();
|
||||||
embed.setTitle('Resource Limit Information');
|
embed.setTitle('Resource Limit Information');
|
||||||
const account = await this.client.db.Account.findOne({ userID: message.author.id });
|
const account = await this.client.db.Account.findOne({ userID: message.author.id });
|
||||||
if (account) {
|
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.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();
|
embed.setTimestamp();
|
||||||
for (const tier of tiers.sort((a, b) => a.id - b.id)) {
|
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'}`);
|
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) {
|
} catch (error) {
|
||||||
return this.client.util.handleError(error, message, this);
|
return this.client.util.handleError(error, message, this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { Message } from 'eris';
|
import { Message, TextChannel } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class Limits_SetRAMNotification extends Command {
|
export default class Limits_SetRAMNotification extends Command {
|
||||||
constructor(client: Client) {
|
constructor(client: Client) {
|
||||||
super(client);
|
super(client);
|
||||||
this.name = 'set-ram-notification';
|
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.usage = `${this.client.config.prefix}limits set-ram-notification <limit in mb>`;
|
||||||
this.enabled = true;
|
this.enabled = true;
|
||||||
}
|
}
|
||||||
|
@ -13,16 +13,16 @@ export default class Limits_SetRAMNotification extends Command {
|
||||||
public async run(message: Message, args: string[]) {
|
public async run(message: Message, args: string[]) {
|
||||||
try {
|
try {
|
||||||
const account = await this.client.db.Account.findOne({ userID: message.author.id });
|
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 });
|
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]) >= 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, 'You cannot set your notification limit to a negative number.');
|
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) {
|
if (Number(args[0]) === 0) {
|
||||||
await account.updateOne({ $set: { ramLimitNotification: 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]) } });
|
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) {
|
} catch (error) {
|
||||||
return this.client.util.handleError(error, message, this);
|
return this.client.util.handleError(error, message, this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class Load extends Command {
|
export default class Load extends Command {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import moment, { unitOfTime } from 'moment';
|
import moment, { unitOfTime } from 'moment';
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class Lock extends Command {
|
export default class Lock extends Command {
|
||||||
|
@ -12,7 +12,7 @@ export default class Lock extends Command {
|
||||||
this.usage = `${this.client.config.prefix}lock [User Name | User ID/Mention] <Time> [Reason]`;
|
this.usage = `${this.client.config.prefix}lock [User Name | User ID/Mention] <Time> [Reason]`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async run(message: Message, args: string[]) { // eslint-disable-line
|
public async run(message: Message, args: string[]) {
|
||||||
try {
|
try {
|
||||||
if (!args.length) return this.client.commands.get('help').run(message, [this.name]);
|
if (!args.length) return this.client.commands.get('help').run(message, [this.name]);
|
||||||
const account = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { userID: args[0].replace(/[<@!>]/gi, '') }] });
|
const account = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { userID: args[0].replace(/[<@!>]/gi, '') }] });
|
||||||
|
@ -29,7 +29,7 @@ export default class Lock extends Command {
|
||||||
const momentMilliseconds = moment.duration(Number(length), unit as unitOfTime.Base).asMilliseconds();
|
const momentMilliseconds = moment.duration(Number(length), unit as unitOfTime.Base).asMilliseconds();
|
||||||
const reason = momentMilliseconds ? args.slice(2).join(' ') : args.slice(1).join(' ');
|
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}.***`);
|
edit.edit(`***${this.client.stores.emojis.success} Account ${account.username} has been locked by Technician ${message.author.username}#${message.author.discriminator}.***`);
|
||||||
message.delete();
|
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>Reason:</b> ${momentMilliseconds ? args.slice(2).join(' ') : args.slice(1).join(' ')}</p>
|
||||||
<p><b>Technician:</b> ${message.author.username}</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>
|
<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>
|
<b><i>Library of Code sp-us | Support Team</i></b>
|
||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { Message } from 'eris';
|
import { Message, MessageEmbed } from 'discord.js';
|
||||||
import { createPaginationEmbed } from 'eris-pagination';
|
import { Client, Command } from '../class';
|
||||||
import { Client, Command, RichEmbed } from '../class';
|
|
||||||
|
|
||||||
export default class Modlogs extends Command {
|
export default class Modlogs extends Command {
|
||||||
constructor(client: Client) {
|
constructor(client: Client) {
|
||||||
|
@ -19,8 +18,8 @@ export default class Modlogs extends Command {
|
||||||
const query = await this.client.db.Moderation.find({ $or: [{ username: args.join(' ') }, { userID: args[0] }] });
|
const query = await this.client.db.Moderation.find({ $or: [{ username: args.join(' ') }, { userID: args[0] }] });
|
||||||
if (!query.length) return msg.edit(`***${this.client.stores.emojis.error} Cannot locate modlogs for ${args.join(' ')}***`);
|
if (!query.length) return msg.edit(`***${this.client.stores.emojis.error} Cannot locate modlogs for ${args.join(' ')}***`);
|
||||||
|
|
||||||
const formatted = query.sort((a, b) => a.date.getTime() - b.date.getTime()).map((log) => {
|
const formatted = await Promise.all(query.sort((a, b) => a.date.getTime() - b.date.getTime()).map(async (log) => {
|
||||||
const { username, moderatorID, reason, type, date, logID } = log;
|
const { username, moderatorID, type, date, reason, logID } = log;
|
||||||
let name: string;
|
let name: string;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
default: name = 'Generic'; break;
|
default: name = 'Generic'; break;
|
||||||
|
@ -30,20 +29,21 @@ export default class Modlogs extends Command {
|
||||||
case 3: name = 'Unlock'; break;
|
case 3: name = 'Unlock'; break;
|
||||||
case 4: name = 'Delete'; break;
|
case 4: name = 'Delete'; break;
|
||||||
}
|
}
|
||||||
const value = `**ID:** ${logID}\n**Account name:** ${username}\n**Moderator:** <@${moderatorID}>\n**Reason:** ${reason || 'Not supplied'}\n**Date:** ${date.toLocaleString('en-us')} EST`;
|
let value = `**ID:** ${logID}\n**Account name:** ${username}\n**Moderator:** <@${moderatorID}>\n**Reason:** ${reason || 'Not supplied'}\n**Date:** ${date.toLocaleString('en-us')} EST`;
|
||||||
|
if (value.length > 1024) value = value.replace(reason, await this.client.util.upload(reason));
|
||||||
const inline = true;
|
const inline = true;
|
||||||
return { name, value, inline };
|
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 logs = this.client.util.splitFields(formatted);
|
||||||
|
|
||||||
const embeds = logs.map((l) => {
|
const embeds = logs.map((l) => {
|
||||||
const embed = new RichEmbed();
|
const embed = new MessageEmbed();
|
||||||
embed.setDescription(`List of Cloud moderation logs for ${users.join(', ')}`);
|
embed.setDescription(`List of Cloud moderation logs for ${users.join(', ')}`);
|
||||||
embed.setAuthor('Library of Code | Cloud Services', this.client.user.avatarURL, 'https://libraryofcode.org/');
|
embed.setAuthor('Library of Code | Cloud Services', this.client.user.avatarURL(), 'https://libraryofcode.org/');
|
||||||
embed.setTitle('Cloud Modlogs/Infractions');
|
embed.setTitle('Cloud Modlogs/Infractions');
|
||||||
embed.setFooter(`Requested by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL);
|
embed.setFooter(`Requested by ${message.author.username}#${message.author.discriminator}`, message.author.avatarURL());
|
||||||
l.forEach((f) => embed.addField(f.name, f.value, f.inline));
|
l.forEach((f) => embed.addField(f.name, f.value, f.inline));
|
||||||
embed.setTimestamp();
|
embed.setTimestamp();
|
||||||
embed.setColor(3447003);
|
embed.setColor(3447003);
|
||||||
|
@ -51,9 +51,9 @@ export default class Modlogs extends Command {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (embeds.length === 1) {
|
if (embeds.length === 1) {
|
||||||
msg.edit({ content: '', embed: embeds[0] });
|
msg.edit({ content: null, embeds: [embeds[0]] });
|
||||||
} else {
|
} else {
|
||||||
createPaginationEmbed(message, embeds, {});
|
this.client.util.createPaginationEmbed(message, embeds);
|
||||||
}
|
}
|
||||||
return msg;
|
return msg;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { Message } from 'eris';
|
import { Message, MessageEmbed, TextChannel } from 'discord.js';
|
||||||
import { Client, Command, RichEmbed } from '../class';
|
import axios from 'axios';
|
||||||
|
import { Converter } from 'showdown';
|
||||||
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class Notify extends Command {
|
export default class Notify extends Command {
|
||||||
constructor(client: Client) {
|
constructor(client: Client) {
|
||||||
|
@ -17,17 +19,20 @@ export default class Notify extends Command {
|
||||||
const edit = await this.loading(message.channel, 'Sending notification...');
|
const edit = await this.loading(message.channel, 'Sending notification...');
|
||||||
const account = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { userID: args[0].replace(/[<@!>]/gi, '') }] });
|
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) return edit.edit(`***${this.client.stores.emojis.error} Cannot find user.***`);
|
||||||
const embed = new RichEmbed()
|
const embed = new MessageEmbed()
|
||||||
.setTitle('Cloud Account | Notification')
|
.setTitle('Cloud Account | Notification')
|
||||||
.setDescription(args.slice(1).join(' '))
|
.setDescription(args.slice(1).join(' '))
|
||||||
.addField('Technician', `<@${message.author.id}>`, true)
|
.setFooter(this.client.user.username, this.client.user.avatarURL())
|
||||||
.setFooter(this.client.user.username, this.client.user.avatarURL)
|
|
||||||
.setTimestamp();
|
.setTimestamp();
|
||||||
this.client.getDMChannel(account.userID).then((channel) => {
|
this.client.users.fetch(account.userID).then((u) => {
|
||||||
channel.createMessage({ embed });
|
u.send({ embeds: [embed] });
|
||||||
});
|
});
|
||||||
embed.addField('User', `${account.username} | <@${account.userID}>`, true);
|
embed.addField('User', `${account.username} | <@${account.userID}>`, true);
|
||||||
this.client.createMessage('580950455581147146', { embed });
|
const req = await axios.get('https://loc.sh/int/directory');
|
||||||
|
const technician = req.data.find((mem) => mem.userID === message.author.id);
|
||||||
|
embed.addField('Technician', message.author.id === this.client.user.id ? 'SYSTEM' : `${message.author.username}${technician.isManager ? ' [k] ' : ' '}(<@${message.author.id}>)`, true);
|
||||||
|
const ch = this.client.channels.cache.get('580950455581147146') as TextChannel;
|
||||||
|
ch.send({ embeds: [embed] });
|
||||||
this.client.util.transport.sendMail({
|
this.client.util.transport.sendMail({
|
||||||
to: account.emailAddress,
|
to: account.emailAddress,
|
||||||
from: 'Library of Code sp-us | Cloud Services <help@libraryofcode.org>',
|
from: 'Library of Code sp-us | Cloud Services <help@libraryofcode.org>',
|
||||||
|
@ -35,7 +40,7 @@ export default class Notify extends Command {
|
||||||
subject: 'Notification',
|
subject: 'Notification',
|
||||||
html: `
|
html: `
|
||||||
<h1>Library of Code sp-us | Cloud Services</h1>
|
<h1>Library of Code sp-us | Cloud Services</h1>
|
||||||
<p>${args.slice(1).join(' ')}</p>
|
<p>${new Converter().makeHtml(args.slice(1).join(' '))}</p>
|
||||||
<p><strong>Technician:</strong> ${message.author.username}</p>
|
<p><strong>Technician:</strong> ${message.author.username}</p>
|
||||||
|
|
||||||
<b><i>Library of Code sp-us | Support Team</i></b>
|
<b><i>Library of Code sp-us | Support Team</i></b>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class Ping extends Command {
|
export default class Ping extends Command {
|
||||||
|
@ -12,8 +12,8 @@ export default class Ping extends Command {
|
||||||
public async run(message: Message) {
|
public async run(message: Message) {
|
||||||
try {
|
try {
|
||||||
const clientStart: number = Date.now();
|
const clientStart: number = Date.now();
|
||||||
const msg: Message = await message.channel.createMessage('🏓 Pong!');
|
const msg: Message = await message.channel.send('🏓 Pong!');
|
||||||
msg.edit(`🏓 Pong!\nClient: \`${Date.now() - clientStart}ms\`\nResponse: \`${msg.createdAt - message.createdAt}ms\``);
|
msg.edit(`🏓 Pong!\nClient: \`${Date.now() - clientStart}ms\`\nResponse: \`${msg.createdTimestamp - message.createdTimestamp}ms\``);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.client.util.handleError(error, message, this);
|
this.client.util.handleError(error, message, this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
|
@ -41,7 +41,6 @@ export default class Pull extends Command {
|
||||||
+ `***${this.client.stores.emojis.loading} Reinstalling dependencies...***`);
|
+ `***${this.client.stores.emojis.loading} Reinstalling dependencies...***`);
|
||||||
const passedPull = await updateMessage.edit(continueMessage);
|
const passedPull = await updateMessage.edit(continueMessage);
|
||||||
|
|
||||||
|
|
||||||
let install: string;
|
let install: string;
|
||||||
try {
|
try {
|
||||||
install = await this.client.util.exec('yarn install', { cwd: '/opt/CloudServices' });
|
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}`;
|
updatedMessage += `${this.client.stores.emojis.error} Could not upload error: ${e}`;
|
||||||
}
|
}
|
||||||
} else {
|
} 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;
|
this.client.buildError = true;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class ResetPassword extends Command {
|
export default class ResetPassword extends Command {
|
||||||
|
@ -25,14 +25,13 @@ export default class ResetPassword extends Command {
|
||||||
await this.client.util.exec(`echo '${account.username}:${tempPass}@' | chpasswd && chage -d0 ${account.username}`);
|
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}@\`***`;
|
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 {
|
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`
|
+ `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.');
|
+ '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) {
|
} catch (error) {
|
||||||
if (error.code === 50007) completeMessage += '\n*Unable to DM user*';
|
completeMessage += '\n*Unable to DM user*';
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
return msg.edit(completeMessage);
|
return msg.edit(completeMessage);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class Restart extends Command {
|
export default class Restart extends Command {
|
||||||
|
@ -6,7 +6,7 @@ export default class Restart extends Command {
|
||||||
super(client);
|
super(client);
|
||||||
this.name = 'restart';
|
this.name = 'restart';
|
||||||
this.description = 'Restart the bot';
|
this.description = 'Restart the bot';
|
||||||
this.permissions = { users: ['253600545972027394', '278620217221971968'] };
|
this.permissions = { users: ['253600545972027394', '278620217221971968', '143414786913206272'] };
|
||||||
this.enabled = true;
|
this.enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
import SetLimit_RAM from './setlimit_ram';
|
import SetLimit_RAM from './setlimit_ram';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class SetLimit_RAM extends Command {
|
export default class SetLimit_RAM extends Command {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { Message } from 'eris';
|
import { Message, MessageEmbed } from 'discord.js';
|
||||||
import os, { totalmem } from 'os';
|
import os from 'os';
|
||||||
import { Client, Command, RichEmbed } from '../class';
|
import { Client, Command } from '../class';
|
||||||
import { dataConversion } from '../functions';
|
import { dataConversion } from '../functions';
|
||||||
|
|
||||||
export default class SysInfo extends Command {
|
export default class SysInfo extends Command {
|
||||||
|
@ -14,21 +14,21 @@ export default class SysInfo extends Command {
|
||||||
|
|
||||||
public async run(message: Message) {
|
public async run(message: Message) {
|
||||||
const availableMemory: string = await this.client.util.exec('free -b');
|
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();
|
const date = new Date();
|
||||||
date.setMilliseconds(-(moment.duration(os.uptime(), 's').asMilliseconds()));
|
date.setMilliseconds(-(moment.duration(os.uptime(), 's').asMilliseconds()));
|
||||||
|
|
||||||
const embed = new RichEmbed();
|
const embed = new MessageEmbed();
|
||||||
embed.setTitle('System Information & Statistics');
|
embed.setTitle('System Information & Statistics');
|
||||||
embed.addField('Hostname', os.hostname(), true);
|
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('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('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('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 (IPv4)', os.networkInterfaces().enp0s3.filter((r) => r.family === 'IPv4')[0].address, true);
|
||||||
// embed.addField('Network Interfaces (IPv6)', os.networkInterfaces().enp0s3.filter((r) => r.family === 'IPv6')[0].address.replace(/:/gi, '\:'), true); // eslint-disable-line
|
// embed.addField('Network Interfaces (IPv6)', os.networkInterfaces().enp0s3.filter((r) => r.family === 'IPv6')[0].address.replace(/:/gi, '\:'), true); // eslint-disable-line
|
||||||
embed.setFooter(this.client.user.username, this.client.user.avatarURL);
|
embed.setFooter(this.client.user.username, this.client.user.avatarURL());
|
||||||
embed.setTimestamp();
|
embed.setTimestamp();
|
||||||
message.channel.createMessage({ embed });
|
message.channel.send({ embeds: [embed] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
import SystemD_Linger from './systemd_linger';
|
import SystemD_Linger from './systemd_linger';
|
||||||
import SystemD_Status from './systemd_status';
|
import SystemD_Status from './systemd_status';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* eslint-disable consistent-return */
|
/* eslint-disable consistent-return */
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class SystemdD_Linger extends Command {
|
export default class SystemdD_Linger extends Command {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class SystemD_Restart extends Command {
|
export default class SystemD_Restart extends Command {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class SystemD_Start extends Command {
|
export default class SystemD_Start extends Command {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Message } from 'eris';
|
import { Message, MessageEmbed } from 'discord.js';
|
||||||
import { Client, Command, RichEmbed } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class SystemD_Status extends Command {
|
export default class SystemD_Status extends Command {
|
||||||
constructor(client: Client) {
|
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.');
|
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());
|
return this.error(message.channel, err.toString());
|
||||||
}
|
}
|
||||||
const embed = new RichEmbed();
|
const embed = new MessageEmbed();
|
||||||
embed.setTitle(`SystemD Status | ${args[0]}`);
|
embed.setTitle(`SystemD Status | ${args[0]}`);
|
||||||
embed.setDescription(`\`\`\`sh\n${cmd}\n\`\`\``);
|
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();
|
embed.setTimestamp();
|
||||||
return message.channel.createMessage({ embed });
|
return message.channel.send({ embeds: [embed] });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return this.client.util.handleError(err, message, this);
|
return this.client.util.handleError(err, message, this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class SystemD_Stop extends Command {
|
export default class SystemD_Stop extends Command {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Message } from 'eris';
|
import { Message, MessageEmbed, TextChannel } from 'discord.js';
|
||||||
import { Client, Command, RichEmbed } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class Tier extends Command {
|
export default class Tier extends Command {
|
||||||
constructor(client: Client) {
|
constructor(client: Client) {
|
||||||
|
@ -18,8 +18,8 @@ export default class Tier extends Command {
|
||||||
const account = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { userID: args[0].replace(/[<@!>]/gi, '') }] });
|
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) return edit.edit(`***${this.client.stores.emojis.error} Cannot find user.***`);
|
||||||
if (account.root) return edit.edit(`***${this.client.stores.emojis.error} Permission denied.***`);
|
if (account.root) return edit.edit(`***${this.client.stores.emojis.error} Permission denied.***`);
|
||||||
if (Number.isNaN(Number(args[1]))) return edit.edit(`***${this.client.stores.emojis.error} The tier you provided is not a valid number. It should be between 1 and 3.***`);
|
if (Number.isNaN(Number(args[1]))) return edit.edit(`***${this.client.stores.emojis.error} The tier you provided is not a valid number. It should be between 0 and 3.***`);
|
||||||
if (Number(args[1]) > 3 || Number(args[1]) < 1) return edit.edit(`***${this.client.stores.emojis.error} You can only choose a Tier between 1 and 3.***`);
|
if (Number(args[1]) > 3 || Number(args[1]) < 0) return edit.edit(`***${this.client.stores.emojis.error} You can only choose a Tier between 0 and 3.***`);
|
||||||
message.delete();
|
message.delete();
|
||||||
const tier = await this.client.db.Tier.findOne({ id: Number(args[1]) });
|
const tier = await this.client.db.Tier.findOne({ id: Number(args[1]) });
|
||||||
if (account.ramLimitNotification !== -1) {
|
if (account.ramLimitNotification !== -1) {
|
||||||
|
@ -28,16 +28,18 @@ export default class Tier extends Command {
|
||||||
await account.updateOne({ $set: { tier: Number(args[1]) } });
|
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]}.***`);
|
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.setTitle('Cloud Account | Tier Change');
|
||||||
embed.setColor('#0099ff');
|
embed.setColor('#0099ff');
|
||||||
embed.addField('User', `${account.username} | <@${account.userID}>`, true);
|
embed.addField('User', `${account.username} | <@${account.userID}>`, true);
|
||||||
embed.addField('Technician', `<@${message.author.id}>`, true);
|
embed.addField('Technician', await this.client.util.getTechnicianName(message.author), true);
|
||||||
embed.addField('Old Tier -> New Tier', `${account.tier} -> ${args[1]}`, 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();
|
embed.setTimestamp();
|
||||||
await this.client.util.sendMessageToUserTerminal(account.username, `A technician has changed your tier to ${args[1]}`).catch(() => { });
|
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) {
|
} catch (error) {
|
||||||
return this.client.util.handleError(error, message, this);
|
return this.client.util.handleError(error, message, this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class Unban extends Command {
|
export default class Unban extends Command {
|
||||||
|
@ -16,7 +16,7 @@ export default class Unban extends Command {
|
||||||
public async run(message: Message, args: string[]) {
|
public async run(message: Message, args: string[]) {
|
||||||
try {
|
try {
|
||||||
if (!args[1]) return this.client.commands.get('help').run(message, [this.name]);
|
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 {
|
try {
|
||||||
await this.client.util.exec(`sudo fail2ban-client set ${args[0]} unbanip ${args[1]}`);
|
await this.client.util.exec(`sudo fail2ban-client set ${args[0]} unbanip ${args[1]}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class Unlock extends Command {
|
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 this.client.util.exec(`unlock ${account.username}`);
|
||||||
await account.updateOne({ locked: false });
|
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();
|
message.delete();
|
||||||
edit.edit(`***${this.client.stores.emojis.success} Account ${account.username} has been unlocked by Technician ${message.author.username}#${message.author.discriminator}.***`);
|
edit.edit(`***${this.client.stores.emojis.success} Account ${account.username} has been unlocked by Technician ${message.author.username}#${message.author.discriminator}.***`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
import { Message } from 'discord.js';
|
||||||
|
import { rename } from 'fs/promises';
|
||||||
|
import { Client, Command } from '../class';
|
||||||
|
import { LINUX_USERNAME_REGEX } from '../class/AccountUtil';
|
||||||
|
|
||||||
|
export default class Usermod extends Command {
|
||||||
|
constructor(client: Client) {
|
||||||
|
super(client);
|
||||||
|
this.name = 'usermod';
|
||||||
|
this.description = 'Modifies properties of a user\'s cloud account';
|
||||||
|
this.usage = `${this.client.config.prefix}usermod [account] [property: username | email] [value]`;
|
||||||
|
this.permissions = { roles: ['662163685439045632', '701454780828221450'] };
|
||||||
|
this.enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async run(message: Message, args: string[]) {
|
||||||
|
if (!args.length) return this.client.commands.get('help').run(message, [this.name]);
|
||||||
|
const [, property, value] = args;
|
||||||
|
|
||||||
|
const account = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { userID: args[0] }] });
|
||||||
|
if (!account) return this.error(message.channel, 'Cannot find user.');
|
||||||
|
if (account.root || account.username === 'matthew') return this.error(message.channel, 'You cannot modify properties of root accounts.');
|
||||||
|
|
||||||
|
switch (property) {
|
||||||
|
case 'email': {
|
||||||
|
if (value === account.emailAddress) return this.error(message.channel, 'The new email address cannot be the same as the old one.');
|
||||||
|
if (await this.client.db.Account.exists({ emailAddress: value })) return this.error(message.channel, 'An account with this email address already exists.');
|
||||||
|
if (!this.client.util.isValidEmail(value)) return this.error(message.channel, 'The supplied email address is invalid.');
|
||||||
|
|
||||||
|
const modifyingPropertyResponse = await this.loading(message.channel, `Modifying \`${property}\` of \`${account.username}\`'s account...`);
|
||||||
|
await this.client.commands.get('notify')
|
||||||
|
.run(message, [account.username, ...`Your email address has been changed from ${account.emailAddress} to ${value}.`.split(' ')]);
|
||||||
|
await account.updateOne({ emailAddress: value });
|
||||||
|
|
||||||
|
modifyingPropertyResponse.delete();
|
||||||
|
this.success(message.channel, `Successfully updated \`${account.username}\`'s email address.`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'username': {
|
||||||
|
if (value === account.username) return this.error(message.channel, 'The username cannot be the same as the old one.');
|
||||||
|
if (await this.client.db.Account.exists({ username: value })) return this.error(message.channel, 'An account with this username already exists.');
|
||||||
|
|
||||||
|
if (!LINUX_USERNAME_REGEX.test(value)) return this.error(message.channel, 'Please supply a valid username.');
|
||||||
|
if (value === 'root') return this.error(message.channel, 'This username is unavailable.');
|
||||||
|
|
||||||
|
const modifyingPropertyResponse = await this.loading(message.channel, `Modifying \`${property}\` of \`${account.username}\`'s account...`);
|
||||||
|
try {
|
||||||
|
await this.client.commands.get('notify')
|
||||||
|
.run(message, [account.username, ...`Changing your username from \`${account.username}\` to \`${value}\`.`.split(' ')]);
|
||||||
|
if (!account.locked) await this.client.util.exec(`lock ${account.username}`);
|
||||||
|
|
||||||
|
await this.client.util.exec(`usermod -l ${value} ${account.username}`);
|
||||||
|
await rename(account.homepath, `/home/${value}`);
|
||||||
|
await this.client.util.exec(`usermod -d /home/${value} ${value}`);
|
||||||
|
await this.client.util.exec(`groupmod -n ${value} ${account.username}`);
|
||||||
|
|
||||||
|
if (!account.locked) await this.client.util.exec(`unlock ${value}`);
|
||||||
|
await account.updateOne({
|
||||||
|
username: value,
|
||||||
|
homepath: `/home/${value}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.client.db.Moderation.updateMany(
|
||||||
|
{
|
||||||
|
username: account.username,
|
||||||
|
date: { $gt: account.createdAt },
|
||||||
|
},
|
||||||
|
{ username: value },
|
||||||
|
);
|
||||||
|
await this.client.commands.get('notify')
|
||||||
|
.run(message, [value, ...`Your username has been successfully changed. Remember to use \`ssh ${value}@cloud.libraryofcode.org\` when logging in.`.split(' ')]);
|
||||||
|
} catch (error) {
|
||||||
|
if (!account.locked) await this.client.util.exec(`unlock ${account.username}`);
|
||||||
|
|
||||||
|
await this.client.commands.get('notify')
|
||||||
|
.run(message, [account.username, ...'Your username change was unsuccessful. Please contact a Technician for more details.'.split(' ')]);
|
||||||
|
await this.client.util.handleError(error);
|
||||||
|
|
||||||
|
return this.error(message.channel, 'Failed to modify username. Please check <#595788220764127272> for more information.');
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyingPropertyResponse.delete();
|
||||||
|
this.success(message.channel, 'Successfully modified username.');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
this.error(message.channel, 'Please specify a valid option.');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
import { Message } from 'eris';
|
import { Message, MessageEmbed } from 'discord.js';
|
||||||
import { createPaginationEmbed } from 'eris-pagination';
|
import { Client, Command } from '../class';
|
||||||
import { Client, Command, RichEmbed } from '../class';
|
|
||||||
|
|
||||||
export default class Users extends Command {
|
export default class Users extends Command {
|
||||||
constructor(client: Client) {
|
constructor(client: Client) {
|
||||||
|
@ -47,10 +46,10 @@ export default class Users extends Command {
|
||||||
const data = this.client.util.splitFields(embedFields);
|
const data = this.client.util.splitFields(embedFields);
|
||||||
|
|
||||||
const embeds = data.map((l) => {
|
const embeds = data.map((l) => {
|
||||||
const embed = new RichEmbed();
|
const embed = new MessageEmbed();
|
||||||
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 Accounts');
|
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));
|
l.forEach((f) => embed.addField(f.name, f.value, true));
|
||||||
embed.setTimestamp();
|
embed.setTimestamp();
|
||||||
embed.setColor(3447003);
|
embed.setColor(3447003);
|
||||||
|
@ -58,10 +57,10 @@ export default class Users extends Command {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (embeds.length === 1) {
|
if (embeds.length === 1) {
|
||||||
msg.edit({ content: '', embed: embeds[0] });
|
msg.edit({ content: null, embeds: [embeds[0]] });
|
||||||
} else {
|
} else {
|
||||||
msg.delete();
|
msg.delete();
|
||||||
createPaginationEmbed(message, embeds, {});
|
this.client.util.createPaginationEmbed(message, embeds);
|
||||||
}
|
}
|
||||||
return msg;
|
return msg;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Message } from 'eris';
|
import { Message } from 'discord.js';
|
||||||
|
import { Converter } from 'showdown';
|
||||||
import { Client, Command } from '../class';
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
export default class Warn extends Command {
|
export default class Warn extends Command {
|
||||||
|
@ -18,7 +19,7 @@ export default class Warn extends Command {
|
||||||
const account = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { userID: args[0].replace(/[<@!>]/gi, '') }] });
|
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) return edit.edit(`***${this.client.stores.emojis.error} Cannot find user.***`);
|
||||||
if (account.root) return edit.edit(`***${this.client.stores.emojis.error} Permission denied.***`);
|
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();
|
message.delete();
|
||||||
edit.edit(`***${this.client.stores.emojis.success} Account ${account.username} has been warned by Technician ${message.author.username}#${message.author.discriminator}.***`);
|
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(() => { });
|
await this.client.util.sendMessageToUserTerminal(account.username, `WARNING FROM TECHNICIAN: ${args.slice(1).join(' ')}`).catch(() => { });
|
||||||
|
@ -30,7 +31,7 @@ export default class Warn extends Command {
|
||||||
html: `
|
html: `
|
||||||
<h1>Library of Code sp-us | Cloud Services</h1>
|
<h1>Library of Code sp-us | Cloud Services</h1>
|
||||||
<p>Your account has received an official warning from a Moderator. Please get the underlying issue resolved to avoid <i>possible</i> moderative action.</p>
|
<p>Your account has received an official warning from a Moderator. Please get the underlying issue resolved to avoid <i>possible</i> moderative action.</p>
|
||||||
<p><strong>Reason:</strong> ${args.slice(1).join(' ') ? args.slice(1).join(' ') : 'Not Specified'}</p>
|
<p><strong>Reason:</strong> ${args.slice(1).length ? new Converter().makeHtml(args.slice(1).join(' ')) : 'Not Specified'}</p>
|
||||||
<p><strong>Technician:</strong> ${message.author.username}</p>
|
<p><strong>Technician:</strong> ${message.author.username}</p>
|
||||||
|
|
||||||
<b><i>Library of Code sp-us | Support Team</i></b>
|
<b><i>Library of Code sp-us | Support Team</i></b>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { Message, GuildTextableChannel, Member, Role } from 'eris';
|
import { Message, TextChannel, Role, MessageEmbed, GuildMember } from 'discord.js';
|
||||||
import { Client, Command, Report, RichEmbed } from '../class';
|
import { Client, Command } from '../class';
|
||||||
import { dataConversion } from '../functions';
|
import { dataConversion } from '../functions';
|
||||||
import { AccountInterface } from '../models';
|
import { AccountInterface } from '../models';
|
||||||
|
|
||||||
|
@ -16,22 +16,26 @@ export default class Whois extends Command {
|
||||||
|
|
||||||
public fullRoles = ['662163685439045632', '701454780828221450'];
|
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 {
|
try {
|
||||||
let full = false;
|
let full = false;
|
||||||
let account: AccountInterface;
|
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;
|
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] }] });
|
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 }] });
|
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.setTitle('Account Information');
|
||||||
embed.setThumbnail(thumbnail);
|
embed.setThumbnail(thumbnail);
|
||||||
if (full) await this.full(account, embed, message.member);
|
if (full) await this.full(account, embed, message.member);
|
||||||
|
@ -42,33 +46,34 @@ export default class Whois extends Command {
|
||||||
if (account.locked) details += '__This account is currently locked.__\n';
|
if (account.locked) details += '__This account is currently locked.__\n';
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case account.permissions.director:
|
case account.permissions.director:
|
||||||
details += 'This account belongs to a Director.\n';
|
details += 'This account belongs to a Manager.\n';
|
||||||
role = message.member.guild.roles.get('662163685439045632');
|
role = await message.member.guild.roles.fetch('662163685439045632');
|
||||||
break;
|
break;
|
||||||
case account.permissions.technician:
|
case account.permissions.technician:
|
||||||
details += 'This account belongs to a Technician.\n';
|
details += 'This account belongs to a Technician.\n';
|
||||||
role = message.member.guild.roles.get('701454780828221450');
|
role = await message.member.guild.roles.fetch('701454780828221450');
|
||||||
break;
|
break;
|
||||||
case account.permissions.staff:
|
case account.permissions.staff:
|
||||||
details += 'This account belongs to a Staff member.\n';
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
role = message.member.guild.roles.get(message.member.guild.id);
|
role = await message.member.guild.roles.fetch(message.member.guild.id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (account.root) details += '**This account has root/administrative privileges.**\n';
|
if (account.root) details += '**This account has administrative privileges.**\n';
|
||||||
embed.setColor(role.color || 0x36393f);
|
embed.setColor(role.color || 0x36393f);
|
||||||
if (details) embed.addField('Additional Details', details, true);
|
if (details) embed.addField('Additional Details', details, true);
|
||||||
|
|
||||||
embed.setTimestamp();
|
embed.setTimestamp();
|
||||||
return message.channel.createMessage({ embed });
|
embed.setFooter('Library of Code | Cloud Services', message.guild.iconURL());
|
||||||
|
return message.channel.send({ embeds: [embed] });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return this.client.util.handleError(error, message, this);
|
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([
|
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.util.exec(`top -b -n 1 -u ${account.username} | awk 'NR>7 { sum += $9; } END { print sum; }'`),
|
||||||
this.client.redis.get(`storage-${account.username}`),
|
this.client.redis.get(`storage-${account.username}`),
|
||||||
|
@ -76,33 +81,34 @@ export default class Whois extends Command {
|
||||||
this.client.util.exec(`chage -l ${account.username}`),
|
this.client.util.exec(`chage -l ${account.username}`),
|
||||||
this.client.util.exec(`memory ${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.setDescription(`${finger}\n${chage}`);
|
||||||
embed.addField('Username', `${account.username} | <@${account.userID}>`, true);
|
embed.addField('Username', `${account.username} <<@${account.userID}>>`, true);
|
||||||
embed.addField('ID', account.userID, true);
|
embed.addField('Discord ID', account.userID, true);
|
||||||
embed.addField('Email Address', account.emailAddress, true);
|
embed.addField('Email Address', account.emailAddress, true);
|
||||||
embed.addField('Tier', String(account.tier), true);
|
embed.addField('Tier', String(account.tier), true);
|
||||||
embed.addField('Support Key', account.supportKey, true);
|
embed.addField('Support Key', account.supportKey, true);
|
||||||
embed.addField('Referral Code & Total', `${account.referralCode} | ${account.totalReferrals}`, 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.util.getTechnicianName(await this.client.users.fetch(account.createdBy))), true);
|
||||||
embed.addField('Created At', moment(account.createdAt).format('dddd, MMMM Do YYYY, h:mm:ss A'), 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('CPU Usage', `${cpuUsage.split('\n')[0] || '0'}%`, true);
|
||||||
embed.addField('Memory', dataConversion(Number(memory) * 1000), true);
|
embed.addField('Memory', dataConversion(Number(memory) * 1000), true);
|
||||||
embed.addField('Storage', data ? dataConversion(Number(data)) : 'N/A', 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([
|
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.util.exec(`top -b -n 1 -u ${account.username} | awk 'NR>7 { sum += $9; } END { print sum; }'`),
|
||||||
this.client.redis.get(`storage-${account.username}`),
|
this.client.redis.get(`storage-${account.username}`),
|
||||||
this.client.util.exec(`memory ${account.username}`),
|
this.client.util.exec(`memory ${account.username}`),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
embed.addField('Username', `${account.username} | <@${account.userID}>`, true);
|
embed.setDescription('*CPU Usage and Memory are fetched in real-time, storage information is cached.*');
|
||||||
embed.addField('ID', account.userID, true);
|
embed.addField('Username', `${account.username} <<@${account.userID}>>`, true);
|
||||||
|
embed.addField('Discord ID', account.userID, true);
|
||||||
embed.addField('Tier', String(account.tier), 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.util.getTechnicianName(await this.client.users.fetch(account.createdBy), 2)), true);
|
||||||
embed.addField('Created At', moment(account.createdAt).format('dddd, MMMM Do YYYY, h:mm:ss A'), 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('CPU Usage', `${cpuUsage.split('\n')[0] || '0'}%`, true);
|
||||||
embed.addField('Memory', dataConversion(Number(memory) * 1000), true);
|
embed.addField('Memory', dataConversion(Number(memory) * 1000), true);
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { Handler, Context } from '../class';
|
import { Handler, Context } from '../class';
|
||||||
import { dataConversion } from '../functions';
|
import { dataConversion } from '../functions';
|
||||||
|
|
||||||
export default class RAM extends Handler {
|
export default class RAM extends Handler {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.endpoint = 'ram';
|
this.endpoint = 'ram';
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handle(ctx: Context) {
|
public async handle(ctx: Context) {
|
||||||
const memoryConversion = dataConversion(Number(await ctx.client.util.exec(`memory ${ctx.data.username}`)) * 1000);
|
const memoryConversion = dataConversion(Number(await ctx.client.util.exec(`memory ${ctx.data.username}`)) * 1000);
|
||||||
return ctx.send(memoryConversion);
|
return ctx.send(memoryConversion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Message, TextChannel } from 'eris';
|
import { Message, TextChannel, MessageEmbed } from 'discord.js';
|
||||||
import { Client, Event, RichEmbed } from '../class';
|
import { Client, Event } from '../class';
|
||||||
|
|
||||||
export default class extends Event {
|
export default class extends Event {
|
||||||
public client: Client
|
public client: Client
|
||||||
|
@ -10,9 +10,6 @@ export default class extends Event {
|
||||||
this.event = 'messageCreate';
|
this.event = 'messageCreate';
|
||||||
}
|
}
|
||||||
|
|
||||||
public info(message: Message) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public async run(message: Message) {
|
public async run(message: Message) {
|
||||||
try {
|
try {
|
||||||
if (message.author.bot && message.author.id !== '554168666938277889') return;
|
if (message.author.bot && message.author.id !== '554168666938277889') return;
|
||||||
|
@ -28,8 +25,9 @@ export default class extends Event {
|
||||||
let hasRolePerms: boolean = false;
|
let hasRolePerms: boolean = false;
|
||||||
if (resolved.cmd.permissions.roles) {
|
if (resolved.cmd.permissions.roles) {
|
||||||
for (const role of resolved.cmd.permissions.roles) {
|
for (const role of resolved.cmd.permissions.roles) {
|
||||||
if (message.member && message.member.roles.includes(role)) {
|
if (message.member && message.member.roles.cache.has(role)) {
|
||||||
hasRolePerms = true; break;
|
hasRolePerms = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,9 +35,15 @@ export default class extends Event {
|
||||||
hasUserPerms = true;
|
hasUserPerms = true;
|
||||||
hasRolePerms = 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 (!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);
|
await resolved.cmd.run(message, resolved.args);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.client.util.handleError(error, message);
|
this.client.util.handleError(error, message);
|
||||||
|
|
|
@ -59,13 +59,6 @@ func main() {
|
||||||
fmt.Printf("Connected to Redis [GO]\n")
|
fmt.Printf("Connected to Redis [GO]\n")
|
||||||
HandleError(err, 1)
|
HandleError(err, 1)
|
||||||
|
|
||||||
/*for {
|
|
||||||
fmt.Printf("Calling handler func [GO]\n")
|
|
||||||
if status == false {
|
|
||||||
handler(*config)
|
|
||||||
time.Sleep(1000000 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
handler(*config)
|
handler(*config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +74,7 @@ func handler(config ConfigStruct) {
|
||||||
collection := mongoClient.Database("cloudservices").Collection("accounts")
|
collection := mongoClient.Database("cloudservices").Collection("accounts")
|
||||||
cur, err := collection.Find(context.TODO(), bson.D{})
|
cur, err := collection.Find(context.TODO(), bson.D{})
|
||||||
HandleError(err, 0)
|
HandleError(err, 0)
|
||||||
|
|
||||||
for cur.Next(context.TODO()) {
|
for cur.Next(context.TODO()) {
|
||||||
checkAccountSizeAndUpdate(cur.Current.Lookup("username").String(), cur.Current.Lookup("id").String())
|
checkAccountSizeAndUpdate(cur.Current.Lookup("username").String(), cur.Current.Lookup("id").String())
|
||||||
fmt.Printf("Checking account information for %s\n", cur.Current.Lookup("username").String())
|
fmt.Printf("Checking account information for %s\n", cur.Current.Lookup("username").String())
|
||||||
|
|
|
@ -1,60 +1,72 @@
|
||||||
/* eslint-disable no-await-in-loop */
|
/* 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) {
|
export default function checkStaffStatus(client: Client) {
|
||||||
setInterval(async () => {
|
setInterval(async () => {
|
||||||
const accounts = await client.db.Account.find();
|
const accounts = await client.db.Account.find();
|
||||||
for (const acc of accounts) {
|
for (const acc of accounts) {
|
||||||
const tier3 = await client.db.Tier.findOne({ id: 3 });
|
const tier3 = await client.db.Tier.findOne({ id: 3 });
|
||||||
|
let user = client.guilds.cache.get('446067825673633794').members.cache.get(acc.userID);
|
||||||
let user = client.guilds.get('446067825673633794').members.get(acc.userID);
|
|
||||||
try {
|
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) {
|
} catch (error) {
|
||||||
continue; // eslint-disable-line no-continue
|
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 } });
|
await client.db.Account.updateOne({ username: acc.username }, { $set: { 'permissions.director': true } });
|
||||||
if (acc.ramLimitNotification !== -1) {
|
if (acc.ramLimitNotification !== -1) {
|
||||||
await client.db.Account.updateOne({ username: acc.username }, { $set: { ramLimitNotification: tier3.resourceLimits.ram - 20 } });
|
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 } });
|
await client.db.Account.updateOne({ username: acc.username }, { $set: { 'permissions.technician': true } });
|
||||||
if (acc.ramLimitNotification !== -1) {
|
if (acc.ramLimitNotification !== -1) {
|
||||||
await client.db.Account.updateOne({ username: acc.username }, { $set: { ramLimitNotification: tier3.resourceLimits.ram - 20 } });
|
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 } });
|
await client.db.Account.updateOne({ username: acc.username }, { $set: { 'permissions.staff': true } });
|
||||||
if (acc.ramLimitNotification !== -1) {
|
if (acc.ramLimitNotification !== -1) {
|
||||||
await client.db.Account.updateOne({ username: acc.username }, { $set: { ramLimitNotification: tier3.resourceLimits.ram - 20 } });
|
await client.db.Account.updateOne({ username: acc.username }, { $set: { ramLimitNotification: tier3.resourceLimits.ram - 20 } });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!acc.permissions.intern && user.roles.cache.has('701481967149121627')) {
|
||||||
|
await client.db.Account.updateOne({ username: acc.username }, { $set: { 'permissions.intern': 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 } });
|
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 } });
|
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 } });
|
await client.db.Account.updateOne({ username: acc.username }, { $set: { 'permissions.staff': false } });
|
||||||
}
|
}
|
||||||
|
if (acc.permissions.intern && !user.roles.cache.has('701481967149121627')) {
|
||||||
|
await client.db.Account.updateOne({ username: acc.username }, { $set: { 'permissions.intern': false } });
|
||||||
|
}
|
||||||
|
|
||||||
if (acc.permissions.staff && acc.tier < 3) {
|
if ((acc.permissions.staff || acc.permissions.intern || acc.permissions.technician || acc.permissions.director || user.roles.cache.has('858049948401401866')) && (acc.tier < 3 && acc.tier > 0)) {
|
||||||
await client.db.Account.updateOne({ username: acc.username }, { $set: { 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.setTitle('Cloud Account | Tier Change');
|
||||||
embed.setColor('#0099ff');
|
embed.setColor('#0099ff');
|
||||||
embed.addField('User', `${acc.username} | <@${acc.userID}>`, true);
|
embed.addField('User', `${acc.username} | <@${acc.userID}>`, true);
|
||||||
embed.addField('Moderator', `<@${client.user.id}>`, true);
|
embed.addField('Technician', 'SYSTEM', true);
|
||||||
embed.addField('Old Tier -> New Tier', `${acc.tier} -> 3`, true);
|
embed.addField('Old Tier -> New Tier', `${acc.tier} -> 3`, true);
|
||||||
embed.setFooter(client.user.username, client.user.avatarURL);
|
embed.addField('Reason', 'T3 Staff Benefit', true);
|
||||||
|
embed.setFooter(client.user.username, client.user.avatarURL());
|
||||||
embed.setTimestamp();
|
embed.setTimestamp();
|
||||||
client.createMessage('580950455581147146', { embed });
|
const ch = await client.channels.fetch('580950455581147146') as TextChannel;
|
||||||
client.getDMChannel(acc.userID).then((chan) => {
|
ch.send({ embeds: [embed] });
|
||||||
chan.createMessage('***Your account has automatically been upgraded to Tier 3 since you are a Staff member.***');
|
client.users.fetch(acc.userID).then((chan) => {
|
||||||
|
chan.send('***Your CS Account tier has been upgraded to `Tier 3` automatically as apart of your staff benefit.***');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
/* eslint-disable no-useless-escape */
|
/* eslint-disable no-useless-escape */
|
||||||
/* eslint-disable no-continue */
|
/* eslint-disable no-continue */
|
||||||
/* eslint-disable no-await-in-loop */
|
/* 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';
|
import { Tiers } from '../models';
|
||||||
|
|
||||||
const channelID = '691824484230889546';
|
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`);
|
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');
|
await client.util.sendMessageToUserTerminal(acc.username, 'REACHED RAM LIMIT; SENDING KILL SIGNAL');
|
||||||
client.util.exec(`killall -9 -u ${acc.username}`);
|
client.util.exec(`killall -9 -u ${acc.username}`);
|
||||||
const embed = new RichEmbed();
|
const embed = new MessageEmbed();
|
||||||
embed.setTitle('Resource Enforcement Notification');
|
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.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('User', `${acc.username} | <@${acc.userID}> | ${acc.userID}`, true);
|
||||||
embed.addField('Tier', String(acc.tier), true);
|
embed.addField('Tier', String(acc.tier), true);
|
||||||
embed.addField('Memory Usage', `${String(memoryConversion)} MB`, true);
|
embed.addField('Memory Usage', `${String(memoryConversion)} MB`, true);
|
||||||
embed.addField('Memory Limit', `${String(userLimits.hard)} MB`, true);
|
embed.addField('Memory Limit', `${String(userLimits.hard)} MB`, true);
|
||||||
client.createMessage(channelID, { embed });
|
const ch = client.channels.cache.get(channelID) as TextChannel;
|
||||||
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.`);
|
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. DN/C`);
|
||||||
client.util.transport.sendMail({
|
client.util.transport.sendMail({
|
||||||
to: acc.emailAddress,
|
to: acc.emailAddress,
|
||||||
from: 'Library of Code sp-us | Cloud Services <help@libraryofcode.org>',
|
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>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>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>
|
<p><strong>Moderator:</strong> SYSTEM</p>
|
||||||
|
|
||||||
<b><i>Library of Code sp-us | Support Team</i></b>
|
<b><i>Library of Code sp-us | Support Team</i></b>
|
||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
client.createMessage(channelID, { embed });
|
ch.send({ embeds: [embed] });
|
||||||
set.delete(acc.username);
|
set.delete(acc.username);
|
||||||
} else if ((memoryConversion >= userLimits.soft) && !set.has(acc.username)) {
|
} else if ((memoryConversion >= userLimits.soft) && !set.has(acc.username)) {
|
||||||
client.signale.info(`RAM Soft Limit Reached | ${acc.username} | ${memoryConversion}/${userLimits.soft} MB`);
|
client.signale.info(`RAM Soft Limit Reached | ${acc.username} | ${memoryConversion}/${userLimits.soft} MB`);
|
||||||
const embed = new RichEmbed();
|
const embed = new MessageEmbed();
|
||||||
if (client.users.get(acc.userID)) embed.setThumbnail(client.users.get(acc.userID).avatarURL);
|
const user = await client.users.fetch(acc.userID);
|
||||||
|
if (user) embed.setThumbnail(user.avatarURL());
|
||||||
embed.setTitle('Resource Limit Notification');
|
embed.setTitle('Resource Limit Notification');
|
||||||
embed.setDescription('Someone has reached the (soft) resource limit for their tier on RAM.');
|
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('User', `${acc.username} | <@${acc.userID}> | ${acc.userID}`, true);
|
||||||
embed.addField('Tier', String(acc.tier), true);
|
embed.addField('Tier', String(acc.tier), true);
|
||||||
embed.addField('Memory Usage', `${String(memoryConversion)} MB`, true);
|
embed.addField('Memory Usage', `${String(memoryConversion)} MB`, true);
|
||||||
embed.addField('Memory Limit', `${String(userLimits.hard)} 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();
|
embed.setTimestamp();
|
||||||
if (acc.ramLimitNotification !== 0) {
|
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)) {
|
if ((memoryConversion >= acc.ramLimitNotification) && (acc.ramLimitNotification !== 0)) {
|
||||||
const notifyEmbed = new RichEmbed()
|
const notifyEmbed = new MessageEmbed()
|
||||||
.setTitle('Cloud Account | Notification')
|
.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.`)
|
.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('User', `${acc.username} | <@${acc.userID}>`, true)
|
||||||
.addField('Technician', 'SYSTEM', 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\`.')
|
.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();
|
.setTimestamp();
|
||||||
client.getDMChannel(acc.userID).then((channel) => {
|
client.users.fetch(acc.userID).then((u) => {
|
||||||
channel.createMessage({ embed: notifyEmbed });
|
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(() => { });
|
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({
|
client.util.transport.sendMail({
|
||||||
|
@ -92,11 +96,12 @@ export default function memory(client: Client) {
|
||||||
<h1>Library of Code sp-us | Cloud Services</h1>
|
<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>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>
|
<p><strong>Technician:</strong> SYSTEM</p>
|
||||||
|
|
||||||
<b><i>Library of Code sp-us | Support Team</i></b>
|
<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);
|
set.add(acc.username);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
|
/* eslint-disable no-unreachable */
|
||||||
// import fs from 'fs-extra';
|
// import fs from 'fs-extra';
|
||||||
import { spawn } from 'child_process';
|
import { spawn } from 'child_process';
|
||||||
import { Client } from '../class';
|
import { Client } from '../class';
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
/* eslint-disable no-await-in-loop */
|
||||||
|
import axios, { AxiosError } from 'axios';
|
||||||
|
import cron from 'cron';
|
||||||
|
import { MessageEmbed, TextChannel } from 'discord.js';
|
||||||
|
import { Client } from '../class';
|
||||||
|
|
||||||
|
async function checkt2(auth: string, userID: string) {
|
||||||
|
try {
|
||||||
|
const { data } = await axios({
|
||||||
|
method: 'get',
|
||||||
|
url: `https://eds.libraryofcode.org/cs/t2rev/?userID=${userID}&auth=${auth}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 'SUCCESS',
|
||||||
|
decision: data.decision,
|
||||||
|
id: data.id,
|
||||||
|
processedBy: data.processedBy,
|
||||||
|
token: data.token,
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
const error = <AxiosError>err;
|
||||||
|
if (error.response?.status === 404 || error.response.status === 400 || error.response.status === 401) return { id: 'N/A', processedBy: 'N/A', status: 'CLIENT_ERROR', decision: 'PRE-DECLINED' };
|
||||||
|
return { id: 'N/A', processedBy: 'N/A', status: 'SERVER_ERROR', decision: 'PRE-DECLINED' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function t2checkCron(client: Client) {
|
||||||
|
const checkTier2StatusJob = new cron.CronJob('13 21 * * *', async () => {
|
||||||
|
const users = await client.db.Account.find({ tier: 2 }).lean();
|
||||||
|
|
||||||
|
for (const user of users) {
|
||||||
|
const check = await checkt2(client.config.internalKey, user.userID);
|
||||||
|
const member = client.guilds.cache.get('446067825673633794').members.cache.get(user.userID);
|
||||||
|
if (member.roles.cache.has('585600289747369987') || member.roles.cache.has('858049948401401866') || member.roles.cache.has('995554888836718612')) continue;
|
||||||
|
if (check.decision === 'DECLINED') {
|
||||||
|
const embed = new MessageEmbed();
|
||||||
|
embed.setTitle('Cloud Account | Tier Change');
|
||||||
|
embed.setColor('#0099ff');
|
||||||
|
embed.addField('User', `${user.username} | <@${user.userID}>`, true);
|
||||||
|
embed.addField('Technician', 'SYSTEM', true);
|
||||||
|
embed.addField('Old Tier -> New Tier', `${user.tier} -> 1`, true);
|
||||||
|
embed.setFooter(client.user.username, client.user.avatarURL());
|
||||||
|
embed.setTimestamp();
|
||||||
|
const ch = client.channels.cache.get('580950455581147146') as TextChannel;
|
||||||
|
await client.db.Account.updateOne({ userID: user.userID }, { $set: { tier: 1 } });
|
||||||
|
ch.send({ embeds: [embed] });
|
||||||
|
return client.users.fetch(user.userID).then((u) => u.send({ embeds: [embed], content: `__**Tier 2 Account Review**__\n\nHello, <@${user.userID}>.\nWe periodically run automatic account review inquiries for users with Tier 2. This means we request an account review from EDS to determine if you're still eligible to keep Tier 2.\nUnfortunately, according to EDS' records, you are currently ineligible for Tier 2. Because of this, we're going to automatically revoke your Tier 2 status. You can re-apply to Tier 2 at any time if you feel that you meet the requirements. If you have any questions, or if you believe this may be a mistake, please contact our support team as soon as possible. Thank you.` })).catch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
checkTier2StatusJob.start();
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ export interface AccountInterface extends Document {
|
||||||
referralCode: string,
|
referralCode: string,
|
||||||
totalReferrals: number,
|
totalReferrals: number,
|
||||||
permissions: {
|
permissions: {
|
||||||
|
intern: boolean,
|
||||||
staff: boolean,
|
staff: boolean,
|
||||||
technician: boolean,
|
technician: boolean,
|
||||||
director: boolean,
|
director: boolean,
|
||||||
|
@ -38,6 +39,7 @@ const Account = new Schema<AccountInterface>({
|
||||||
referralCode: String,
|
referralCode: String,
|
||||||
totalReferrals: Number,
|
totalReferrals: Number,
|
||||||
permissions: {
|
permissions: {
|
||||||
|
intern: Boolean,
|
||||||
staff: Boolean,
|
staff: Boolean,
|
||||||
technician: Boolean,
|
technician: Boolean,
|
||||||
director: Boolean,
|
director: Boolean,
|
||||||
|
|
|
@ -11,6 +11,7 @@ export interface DomainInterface extends Document {
|
||||||
key: string
|
key: string
|
||||||
},
|
},
|
||||||
enabled: true
|
enabled: true
|
||||||
|
created: Date,
|
||||||
}
|
}
|
||||||
|
|
||||||
const Domain = new Schema<DomainInterface>({
|
const Domain = new Schema<DomainInterface>({
|
||||||
|
@ -19,6 +20,7 @@ const Domain = new Schema<DomainInterface>({
|
||||||
port: Number,
|
port: Number,
|
||||||
x509: { cert: String, key: String },
|
x509: { cert: String, key: String },
|
||||||
enabled: Boolean,
|
enabled: Boolean,
|
||||||
|
created: Date,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default model<DomainInterface>('Domain', Domain);
|
export default model<DomainInterface>('Domain', Domain);
|
||||||
|
|
118
tsconfig.json
118
tsconfig.json
|
@ -1,64 +1,62 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
/* Basic Options */
|
// "incremental": true, /* Enable incremental compilation */
|
||||||
// "incremental": true, /* Enable incremental compilation */
|
"target": "ES2019", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
||||||
"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'. */
|
||||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
"lib": [
|
||||||
"lib": ["ES2019.Object", "ES2020.Promise"], /* Specify library files to be included in the compilation. */
|
"ES2019.Object",
|
||||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
"ES2020.Promise",
|
||||||
// "checkJs": true, /* Report errors in .js files. */
|
"es2015.promise"
|
||||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
], /* Specify library files to be included in the compilation. */
|
||||||
"declaration": false, /* Generates corresponding '.d.ts' file. */
|
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||||
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
// "checkJs": true, /* Report errors in .js files. */
|
||||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
"declaration": false, /* Generates corresponding '.d.ts' file. */
|
||||||
"outDir": "./dist", /* Redirect output structure to the directory. */
|
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||||
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||||
// "composite": true, /* Enable project compilation */
|
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
"outDir": "./dist", /* Redirect output structure to the directory. */
|
||||||
"removeComments": true, /* Do not emit comments to output. */
|
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||||
// "noEmit": true, /* Do not emit outputs. */
|
// "composite": true, /* Enable project compilation */
|
||||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
||||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
"removeComments": true, /* Do not emit comments to output. */
|
||||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
// "noEmit": true, /* Do not emit outputs. */
|
||||||
|
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||||
/* Strict Type-Checking Options */
|
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||||
"strict": false, /* Enable all strict type-checking options. */
|
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
/* Strict Type-Checking Options */
|
||||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
"strict": true, /* Enable all strict type-checking options. */
|
||||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
"skipLibCheck": true,
|
||||||
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
"noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||||
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
"strictNullChecks": false, /* Enable strict null checks. */
|
||||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
"strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
"strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||||
|
"strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */
|
||||||
/* Additional Checks */
|
"noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||||
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
"alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
/* Additional Checks */
|
||||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||||
|
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||||
/* Module Resolution Options */
|
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||||
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
/* Module Resolution Options */
|
||||||
"resolveJsonModule": true,
|
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
"resolveJsonModule": true,
|
||||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||||
// "types": [], /* Type declaration files to be included in compilation. */
|
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
// "types": [], /* Type declaration files to be included in compilation. */
|
||||||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from 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. */
|
||||||
// "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. */
|
||||||
// "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. */
|
||||||
// "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. */
|
||||||
// "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. */
|
||||||
/* Experimental Options */
|
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
|
||||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 };
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,54 +1,51 @@
|
||||||
declare module 'x509' {
|
export interface Certificate {
|
||||||
namespace Certificate {
|
status: true | false,
|
||||||
interface Issuer {
|
subject: {
|
||||||
countryName: string,
|
commonName: string,
|
||||||
stateOrProvinceName: string,
|
organization: string[],
|
||||||
localityName: string,
|
organizationalUnit: string[],
|
||||||
organizationName: string,
|
locality: string[],
|
||||||
organizationalUnitName: string,
|
country: string[],
|
||||||
commonName: string,
|
},
|
||||||
emailAddress: string
|
issuer: {
|
||||||
}
|
commonName: string,
|
||||||
interface Subject {
|
organization: string[],
|
||||||
countryName: string,
|
organizationalUnit: string[],
|
||||||
postalCode: string,
|
locality: string[],
|
||||||
stateOrProvinceName: string,
|
country: string[],
|
||||||
localityName: string,
|
},
|
||||||
streetAddress: string,
|
aia: {
|
||||||
organizationName: string,
|
issuingCertificateURL: string,
|
||||||
organizationalUnitName: string,
|
ocspServer: string,
|
||||||
commonName: string,
|
},
|
||||||
emailAddress: string
|
validationType: 'DV' | 'OV' | 'EV',
|
||||||
}
|
signatureAlgorithm: string,
|
||||||
interface Extensions {
|
publicKeyAlgorithm: string,
|
||||||
keyUsage: string,
|
serialNumber: number,
|
||||||
authorityInformationAccess: string,
|
notAfter: Date,
|
||||||
certificatePolicies: string,
|
/**
|
||||||
basicConstraints: string,
|
- 0: KeyUsageCRLSign
|
||||||
cRLDistributionPoints: string,
|
- 1: KeyUsageCertificateSign
|
||||||
subjectAlternativeName: string,
|
- 2: KeyUsageContentCommitment
|
||||||
extendedKeyUsage: string,
|
- 3: KeyUsageDataEncipherment
|
||||||
authorityKeyIdentifier: string,
|
- 4: KeyUsageDecipherOnly
|
||||||
subjectKeyIdentifier: string,
|
- 5: KeyUsageDigitalSignature
|
||||||
cTPrecertificateSCTs: string
|
- 6: KeyUsageEncipherOnly
|
||||||
}
|
- 7: KeyUsageKeyAgreement
|
||||||
}
|
- 8: KeyUsageKeyEncipherment
|
||||||
interface FullCertificate {
|
*/
|
||||||
version: number,
|
keyUsage: number[],
|
||||||
subject: Certificate.Subject,
|
keyUsageAsText: ['CRL Signing', 'Certificate Signing', 'Content Commitment', 'Data Encipherment', 'Decipher Only', 'Digital Signature', 'Encipher Only', 'Key Agreement', 'Key Encipherment'],
|
||||||
issuer: Certificate.Issuer,
|
/**
|
||||||
fingerPrint: string,
|
- 0: Any/All Usage
|
||||||
serial: string,
|
- 1: TLS Web Server Auth
|
||||||
notBefore: Date,
|
- 2: TLS Web Client Auth
|
||||||
notAfter: Date,
|
- 3: Code Signing
|
||||||
subjectHash: string,
|
- 4: Email Protection (S/MIME)
|
||||||
signatureAlgorithm: string,
|
*/
|
||||||
publicKey: { algorithm: string };
|
extendedKeyUsage: number[],
|
||||||
altNames: string[]
|
extendedKeyUsageAsText: ['All/Any Usages', 'TLS Web Server Authentication', 'TLS Web Client Authentication', 'Code Signing', 'E-mail Protection (S/MIME)'],
|
||||||
extensions: Certificate.Extensions
|
san: string,
|
||||||
}
|
emailAddresses: string,
|
||||||
function getAltNames(cert: string): string[];
|
fingerprint: string,
|
||||||
function getIssuer(cert: string): Certificate.Issuer;
|
|
||||||
function getSubject(cert: string): Certificate.Subject;
|
|
||||||
function parseCert(cert: string): FullCertificate
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue