Resubmission of MR #19 #33

Merged
ghost merged 28 commits from profile into dev 2021-03-05 22:41:03 -05:00
52 changed files with 1411 additions and 7287 deletions

View File

@ -2,7 +2,7 @@
We accept contributions from the community, however there's a few steps you need to do first before you're able to fork the project.
1. Join the [Discord Server](https://discord.gg/F4ztpQh).
1. Join the [Discord Server](https://loc.sh/discord).
2. Send a DM to @Ramirez in the server, provide your GitLab username.
3. We'll let you know when you'll be able to fork the project.

4889
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
{
"name": "loccommunityrelations",
"name": "loccr",
"version": "1.0.0",
"description": "The official system for handling Community Relations in the LOC Discord server.",
"main": "build/main.js",
@ -7,7 +7,7 @@
"lint": "eslint -c ./.eslintrc.json src --ext ts"
},
"repository": "https://gitlab.libraryofcode.org/engineering/communityrelations.git",
"author": "Matthew R <matthew@staff.libraryofcode.org>",
"author": "Matthew R, AD, FSEN <matthew@staff.libraryofcode.org>",
"license": "AGPL-3.0",
"private": false,
"devDependencies": {
@ -29,7 +29,8 @@
"eslint": "^7.19.0",
"eslint-config-airbnb-base": "^14.1.0",
"eslint-plugin-import": "^2.20.2",
"typescript": "^3.9.8"
"tslib": "^2.1.0",
"typescript": "^3.9.2"
},
"dependencies": {
"@google-cloud/text-to-speech": "^3.1.2",
@ -42,7 +43,7 @@
"bull": "^3.20.1",
"cheerio": "^1.0.0-rc.5",
"cron": "^1.8.2",
"eris": "^0.13.3",
"eris": "^0.14.0",
"eris-pagination": "github:bsian03/eris-pagination",
"express": "^4.17.1",
"helmet": "^3.22.0",
@ -55,7 +56,8 @@
"puppeteer": "^5.5.0",
"sd-notify": "^2.8.0",
"signale": "^1.4.0",
"stripe": "^8.135.0",
"stock-info": "^1.2.0",
"stripe": "^8.120.0",
"uuid": "^8.0.0",
"yaml": "^1.9.2"
}

View File

@ -1,3 +0,0 @@
import { Server, ServerManagement } from '../../class';
export default (management: ServerManagement) => new Server(management, 3892, `${__dirname}/routes`);

View File

@ -1 +0,0 @@
export { default as Root } from './root';

View File

@ -1,777 +0,0 @@
import { TextChannel } from 'eris';
import { v4 as genUUID } from 'uuid';
import { Request, Response } from 'express';
import { RichEmbed, Route, Server } from '../../../class';
export default class Root extends Route {
constructor(server: Server) {
super(server);
this.conf = {
path: '/',
};
}
public bind() {
this.router.post('/eo', async (req, res) => {
if (!req.body.pin) {
return res.status(401).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
const director = await this.server.client.db.Score.findOne({ pin: req.body.pin });
const staffGuild = this.server.client.guilds.get(this.server.client.config.guildID) || await this.server.client.getRESTGuild(this.server.client.config.guildID);
if (!director || !staffGuild.members.get(director.userID)?.roles?.includes('662163685439045632')) {
return res.status(403).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
if (typeof req.body.subject !== 'string' || typeof req.body.body !== 'string' || req.body.subject.length > 1024 || req.body.body.length > 1024) {
return res.status(400).json({
code: this.constants.codes.CLIENT_ERROR,
message: this.constants.messages.CLIENT_ERROR,
});
}
const eoID = genUUID();
const directorDiscord = this.server.client.users.get(director.userID) || await this.server.client.getRESTUser(director.userID);
const directorInformation = await this.server.client.db.Staff.findOne({ userID: director.userID });
const embed = new RichEmbed();
embed.setTitle('Executive Order');
embed.setAuthor(`${directorDiscord.username}#${directorDiscord.discriminator}, ${directorInformation.pn.join(', ')}`, directorDiscord.avatarURL);
embed.setColor('#dd3acd');
embed.addField('Subject', req.body.subject);
embed.addField('Body', req.body.body);
embed.addField('Director', directorDiscord.mention);
embed.setDescription(eoID);
embed.setTimestamp(new Date());
const channel = <TextChannel>this.server.client.getChannel('807444198969835550');
const msg = await channel.createMessage({ embed });
const executiveOrder = await this.server.client.db.ExecutiveOrder.create({
issuer: director.userID,
subject: req.body.subject,
body: req.body.body,
at: new Date(),
oID: eoID,
msg: msg.id,
});
res.status(200).json({
code: this.constants.codes.SUCCESS,
message: `Created new Executive Order with ID ${executiveOrder.oID} by ${directorDiscord.username}#${directorDiscord.discriminator}, ${directorInformation.pn.join(', ')}.`,
});
});
this.router.post('/proc', async (req: Request, res: Response) => {
if (!req.body.pin) {
return res.status(401).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
const director = await this.server.client.db.Score.findOne({ pin: req.body.pin });
const staffGuild = this.server.client.guilds.get(this.server.client.config.guildID) || await this.server.client.getRESTGuild(this.server.client.config.guildID);
if (!director || !staffGuild.members.get(director.userID)?.roles?.includes('662163685439045632')) {
return res.status(401).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
if (typeof req.body.subject !== 'string' || typeof req.body.body !== 'string' || req.body.subject.length > 1024 || req.body.body.length > 1024) {
return res.status(400).json({
code: this.constants.codes.CLIENT_ERROR,
message: this.constants.messages.CLIENT_ERROR,
});
}
const proclamationID = genUUID();
const directorDiscord = this.server.client.users.get(director.userID) || await this.server.client.getRESTUser(director.userID);
const directorInformation = await this.server.client.db.Staff.findOne({ userID: director.userID });
const embed = new RichEmbed();
embed.setTitle('Proclamation');
embed.setAuthor(`${directorDiscord.username}#${directorDiscord.discriminator}, ${directorInformation.pn.join(', ')}`, directorDiscord.avatarURL);
embed.setColor('#66e1ff');
embed.addField('Subject', req.body.subject);
embed.addField('Body', req.body.body);
embed.addField('ID', proclamationID);
embed.addField('Director', directorDiscord.mention);
embed.setTimestamp(new Date());
const channel = <TextChannel>this.server.client.getChannel('807444198969835550');
const procMessage = await channel.createMessage({ embed });
await procMessage.addReaction(this.server.client.util.emojis.SUCCESS);
await procMessage.addReaction(this.server.client.util.emojis.ERROR);
await procMessage.getReaction('🙋');
const proclamation = await this.server.client.db.Proclamation.create({
issuer: director.userID,
subject: req.body.subject,
body: req.body.body,
at: new Date(),
oID: proclamationID,
processed: false,
msg: procMessage.id,
});
res.status(200).json({
code: this.constants.codes.SUCCESS,
message: `Created new Proclamation with ID ${proclamation.oID} by ${directorDiscord.username}#${directorDiscord.discriminator}, ${directorInformation.pn.join(', ')}.`,
});
});
this.router.delete('/eo/:id', async (req, res) => {
if (!req.body.pin) {
return res.status(401).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
const director = await this.server.client.db.Score.findOne({ pin: req.body.pin });
const staffGuild = this.server.client.guilds.get(this.server.client.config.guildID) || await this.server.client.getRESTGuild(this.server.client.config.guildID);
if (!director || !staffGuild.members.get(director.userID)?.roles?.includes('662163685439045632')) {
return res.status(403).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
if (!req.params.id) {
return res.status(400).json({
code: this.constants.codes.CLIENT_ERROR,
message: this.constants.messages.CLIENT_ERROR,
});
}
if (!(await this.server.client.db.ExecutiveOrder.exists({ oID: req.params.id }))) {
return res.status(404).json({
code: this.constants.codes.NOT_FOUND,
message: this.constants.messages.NOT_FOUND,
});
}
const executiveOrder = await this.server.client.db.ExecutiveOrder.findOne({ oID: req.params.id });
if (!['278620217221971968', executiveOrder.issuer].includes(director.userID)) {
return res.status(403).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
await executiveOrder.delete();
res.status(200).json({ message: `Executive Order with ID ${req.params.id} deleted.` });
});
this.router.delete('/motion/:id', async (req, res) => {
if (!req.body.pin) {
return res.status(401).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
const director = await this.server.client.db.Score.findOne({ pin: req.body.pin });
const staffGuild = this.server.client.guilds.get(this.server.client.config.guildID) || await this.server.client.getRESTGuild(this.server.client.config.guildID);
if (!director || !staffGuild.members.get(director.userID)?.roles?.includes('662163685439045632')) {
return res.status(403).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
if (!req.params.id) {
return res.status(400).json({
code: this.constants.codes.CLIENT_ERROR,
message: this.constants.messages.CLIENT_ERROR,
});
}
if (!(await this.server.client.db.Motion.exists({ oID: req.params.id }))) {
return res.status(404).json({
code: this.constants.codes.NOT_FOUND,
message: this.constants.messages.NOT_FOUND,
});
}
const motion = await this.server.client.db.Motion.findOne({ oID: req.params.id });
if (!['278620217221971968', motion.issuer].includes(director.userID)) {
return res.status(403).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
await motion.delete();
res.status(200).json({ message: `Motion with ID ${req.params.id} deleted.` });
});
this.router.delete('/proc/:id', async (req, res) => {
if (!req.body.pin) {
return res.status(401).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
const director = await this.server.client.db.Score.findOne({ pin: req.body.pin });
const staffGuild = this.server.client.guilds.get(this.server.client.config.guildID) || await this.server.client.getRESTGuild(this.server.client.config.guildID);
if (!director || !staffGuild.members.get(director.userID)?.roles?.includes('662163685439045632')) {
return res.status(403).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
if (!req.params.id) {
return res.status(400).json({
code: this.constants.codes.CLIENT_ERROR,
message: this.constants.messages.CLIENT_ERROR,
});
}
if (!(await this.server.client.db.Proclamation.exists({ oID: req.params.id }))) {
return res.status(404).json({
code: this.constants.codes.NOT_FOUND,
message: this.constants.messages.NOT_FOUND,
});
}
const proclamation = await this.server.client.db.Proclamation.findOne({ oID: req.params.id });
if (!['278620217221971968', proclamation.issuer].includes(director.userID)) {
return res.status(403).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
await proclamation.delete();
res.status(200).json({ message: `Proclamation with ID ${req.params.id} deleted.` });
});
this.router.get('/eo/:id', async (req, res) => {
if (!req.params.id) {
return res.status(400).json({
code: this.constants.codes.CLIENT_ERROR,
message: this.constants.messages.CLIENT_ERROR,
});
}
if (!(await this.server.client.db.ExecutiveOrder.exists({ oID: req.params.id }))) {
return res.status(404).json({
code: this.constants.codes.NOT_FOUND,
message: this.constants.messages.NOT_FOUND,
});
}
const executiveOrder = await this.server.client.db.ExecutiveOrder.findOne({ oID: req.params.id }).lean();
res.status(200).json(executiveOrder);
});
this.router.get('/motion/:id', async (req, res) => {
if (!req.params.id) {
return res.status(400).json({
code: this.constants.codes.CLIENT_ERROR,
message: this.constants.messages.CLIENT_ERROR,
});
}
if (!(await this.server.client.db.Motion.exists({ oID: req.params.id }))) {
return res.status(404).json({
code: this.constants.codes.NOT_FOUND,
message: this.constants.messages.NOT_FOUND,
});
}
const motion = await this.server.client.db.Motion.findOne({ oID: req.params.id }).lean();
res.status(200).json(motion);
});
this.router.get('/proc/:id', async (req: Request, res: Response) => {
if (!req.params.id) {
return res.status(400).json({
code: this.constants.codes.CLIENT_ERROR,
message: this.constants.messages.CLIENT_ERROR,
});
}
if (!(await this.server.client.db.Proclamation.exists({ oID: req.params.id }))) {
return res.status(404).json({
code: this.constants.codes.NOT_FOUND,
message: this.constants.messages.NOT_FOUND,
});
}
const proclamation = await this.server.client.db.Proclamation.findOne({ oID: req.params.id }).lean();
res.status(200).json(proclamation);
});
this.router.get('/resolution/:id', async (req, res) => {
if (!req.params.id) {
return res.status(400).json({
code: this.constants.codes.CLIENT_ERROR,
message: this.constants.messages.CLIENT_ERROR,
});
}
if (!(await this.server.client.db.Resolution.exists({ oID: req.params.id }))) {
return res.status(404).json({
code: this.constants.codes.NOT_FOUND,
message: this.constants.messages.NOT_FOUND,
});
}
const resolution = await this.server.client.db.Resolution.findOne({ oID: req.params.id }).lean();
res.status(200).json(resolution);
});
this.router.patch('/eo/:id', async (req, res) => {
if (!req.body.pin) {
return res.status(401).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
const director = await this.server.client.db.Score.findOne({ pin: req.body.pin });
const staffGuild = this.server.client.guilds.get(this.server.client.config.guildID) || await this.server.client.getRESTGuild(this.server.client.config.guildID);
const directorDiscord = staffGuild.members.get(director.userID);
if (!director || !directorDiscord?.roles?.includes('662163685439045632')) {
return res.status(403).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
if (!req.params.id) {
return res.status(400).json({
code: this.constants.codes.CLIENT_ERROR,
message: this.constants.messages.CLIENT_ERROR,
});
}
if (!(await this.server.client.db.ExecutiveOrder.exists({ oID: req.params.id }))) {
return res.status(404).json({
code: this.constants.codes.NOT_FOUND,
message: this.constants.messages.NOT_FOUND,
});
}
if (typeof req.body.subject !== 'string' || typeof req.body.body !== 'string' || req.body.subject.length > 1024 || req.body.body.length > 1024) {
return res.status(400).json({
code: this.constants.codes.CLIENT_ERROR,
message: this.constants.messages.CLIENT_ERROR,
});
}
const executiveOrder = await this.server.client.db.ExecutiveOrder.findOne({ oID: req.params.id });
if (!['278620217221971968', executiveOrder.issuer].includes(director.userID)) {
return res.status(403).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
await executiveOrder.updateOne({
subject: req.body.subject || executiveOrder.subject,
body: req.body.body || executiveOrder.body,
});
if (executiveOrder.subject !== req.body.subject || executiveOrder.body !== req.body.body) {
const directorStaffProfile = await this.server.client.db.Staff.findOne({ userID: directorDiscord.id });
const embed = new RichEmbed();
embed.setTitle('Executive Order');
embed.setAuthor(`${directorDiscord.username}#${directorDiscord.discriminator}, ${directorStaffProfile.pn.join(', ')}`, directorDiscord.avatarURL);
embed.setColor('#66e1ff');
embed.addField('Subject', req.body.subject || executiveOrder.subject);
embed.addField('Body', req.body.body || executiveOrder.body);
embed.addField('Director', `<@!${executiveOrder.issuer}>`);
embed.setDescription(req.params.id);
embed.setTimestamp(new Date());
const channel = <TextChannel>this.server.client.getChannel('807444198969835550');
const eoMessage = await channel.getMessage(executiveOrder.msg);
await eoMessage.edit({ embed });
}
res.status(200).json({ message: `Updated Executive Order with ID ${executiveOrder.oID}.` });
});
this.router.patch('/motion/:id', async (req, res) => {
if (!req.body.pin) {
return res.status(401).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
const director = await this.server.client.db.Score.findOne({ pin: req.body.pin });
const staffGuild = this.server.client.guilds.get(this.server.client.config.guildID) || await this.server.client.getRESTGuild(this.server.client.config.guildID);
const directorDiscord = staffGuild.members.get(director.userID);
if (!director || !directorDiscord?.roles?.includes('662163685439045632')) {
return res.status(403).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
if (!req.params.id) {
return res.status(400).json({
code: this.constants.codes.CLIENT_ERROR,
message: this.constants.messages.CLIENT_ERROR,
});
}
if (!(await this.server.client.db.Motion.exists({ oID: req.params.id }))) {
return res.status(404).json({
code: this.constants.codes.NOT_FOUND,
message: this.constants.messages.NOT_FOUND,
});
}
if (typeof req.body.subject !== 'string' || typeof req.body.body !== 'string' || req.body.subject.length > 1024 || req.body.body.length > 1024) {
return res.status(400).json({
code: this.constants.codes.CLIENT_ERROR,
message: this.constants.messages.CLIENT_ERROR,
});
}
const motion = await this.server.client.db.Motion.findOne({ oID: req.params.id });
if (!['278620217221971968', motion.issuer].includes(director.userID)) {
return res.status(403).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
await motion.updateOne({
subject: req.body.subject || motion.subject,
body: req.body.body || motion.body,
});
if (motion.subject !== req.body.subject || motion.body !== req.body.body) {
const directorStaffProfile = await this.server.client.db.Staff.findOne({ userID: directorDiscord.id });
const embed = new RichEmbed();
embed.setTitle('Motion');
embed.setAuthor(`${directorDiscord.username}#${directorDiscord.discriminator}, ${directorStaffProfile.pn.join(', ')}`, directorDiscord.avatarURL);
embed.setColor('#66e1ff');
embed.addField('Subject', req.body.subject || motion.subject);
embed.addField('Body', req.body.body || motion.body);
embed.addField('Director', `<@!${motion.issuer}>`);
embed.setDescription(req.params.id);
embed.setTimestamp(new Date());
const channel = <TextChannel>this.server.client.getChannel('807444198969835550');
const motionMessage = await channel.getMessage(motion.msg);
await motionMessage.edit({ embed });
}
res.status(200).json({ message: `Updated Motion with ID ${motion.oID}.` });
});
this.router.patch('/proc/:id', async (req, res) => {
if (!req.body.pin) {
return res.status(401).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
const director = await this.server.client.db.Score.findOne({ pin: req.body.pin });
const staffGuild = this.server.client.guilds.get(this.server.client.config.guildID) || await this.server.client.getRESTGuild(this.server.client.config.guildID);
const directorDiscord = staffGuild.members.get(director.userID);
if (!director || !directorDiscord?.roles?.includes('662163685439045632')) {
return res.status(403).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
if (!req.params.id) {
return res.status(400).json({
code: this.constants.codes.CLIENT_ERROR,
message: this.constants.messages.CLIENT_ERROR,
});
}
if (!(await this.server.client.db.Proclamation.exists({ oID: req.params.id }))) {
return res.status(404).json({
code: this.constants.codes.NOT_FOUND,
message: this.constants.messages.NOT_FOUND,
});
}
if (typeof req.body.subject !== 'string' || typeof req.body.body !== 'string' || req.body.subject.length > 1024 || req.body.body.length > 1024) {
return res.status(400).json({
code: this.constants.codes.CLIENT_ERROR,
message: this.constants.messages.CLIENT_ERROR,
});
}
const proclamation = await this.server.client.db.Proclamation.findOne({ oID: req.params.id });
if (!['278620217221971968', proclamation.issuer].includes(director.userID)) {
return res.status(403).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
await proclamation.updateOne({
subject: req.body.subject || proclamation.subject,
body: req.body.body || proclamation.body,
});
if (proclamation.subject !== req.body.subject || proclamation.body !== req.body.body) {
const directorStaffProfile = await this.server.client.db.Staff.findOne({ userID: directorDiscord.id });
const embed = new RichEmbed();
embed.setTitle('Proclamation');
embed.setAuthor(`${directorDiscord.username}#${directorDiscord.discriminator}, ${directorStaffProfile.pn.join(', ')}`, directorDiscord.avatarURL);
embed.setColor('#66e1ff');
embed.addField('Subject', req.body.subject || proclamation.subject);
embed.addField('Body', req.body.body || proclamation.body);
embed.addField('Director', `<@!${proclamation.issuer}>`);
embed.setDescription(req.params.id);
embed.setTimestamp(new Date());
const channel = <TextChannel>this.server.client.getChannel('807444198969835550');
const procMessage = await channel.getMessage(proclamation.msg);
await procMessage.edit({ embed });
}
res.status(200).json({ message: `Updated Proclamation with ID ${proclamation.oID}.` });
});
this.router.patch('/resolution/:id', async (req, res) => {
if (!req.body.pin) {
return res.status(401).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
const director = await this.server.client.db.Score.findOne({ pin: req.body.pin });
const staffGuild = this.server.client.guilds.get(this.server.client.config.guildID) || await this.server.client.getRESTGuild(this.server.client.config.guildID);
const directorDiscord = staffGuild.members.get(director.userID);
if (!director || !directorDiscord?.roles?.includes('662163685439045632')) {
return res.status(403).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
if (!req.params.id) {
return res.status(400).json({
code: this.constants.codes.CLIENT_ERROR,
message: this.constants.messages.CLIENT_ERROR,
});
}
if (!(await this.server.client.db.Resolution.exists({ oID: req.params.id }))) {
return res.status(404).json({
code: this.constants.codes.NOT_FOUND,
message: this.constants.messages.NOT_FOUND,
});
}
if (typeof req.body.subject !== 'string' || typeof req.body.body !== 'string' || req.body.subject.length > 1024 || req.body.body.length > 1024) {
return res.status(400).json({
code: this.constants.codes.CLIENT_ERROR,
message: this.constants.messages.CLIENT_ERROR,
});
}
const resolution = await this.server.client.db.Resolution.findOne({ oID: req.params.id });
if (!['278620217221971968', resolution.issuer].includes(director.userID)) {
return res.status(403).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
await resolution.updateOne({
subject: req.body.subject || resolution.subject,
body: req.body.body || resolution.body,
});
if (resolution.subject !== req.body.subject || resolution.body !== req.body.body) {
const directorStaffProfile = await this.server.client.db.Staff.findOne({ userID: directorDiscord.id });
const motion = await this.server.client.db.Motion.findOne({ oID: resolution.oID });
const embed = new RichEmbed();
embed.setTitle('Resolution');
embed.setAuthor(`${directorDiscord.username}#${directorDiscord.discriminator}, ${directorStaffProfile.pn.join(', ')}`, directorDiscord.avatarURL);
embed.setColor('#75b0ff');
embed.addField('Subject', req.body.subject || resolution.subject);
embed.addField('Body', req.body.body || resolution.body);
embed.addField('Director', `<@!${motion.issuer}>`);
embed.setDescription(req.params.id);
embed.setTimestamp(new Date());
const channel = <TextChannel>this.server.client.getChannel('807444198969835550');
const resMessage = await channel.getMessage(resolution.msg);
await resMessage.edit({
content: `<@!${resolution.issuer}>`,
embed,
});
}
res.status(200).json({ message: `Updated Resolution with ID ${resolution.oID}.` });
});
this.router.get('/eo', async (_req, res) => {
const executiveOrders = await this.server.client.db.ExecutiveOrder.find().lean();
res.status(200).json(executiveOrders);
});
this.router.get('/motion', async (_req, res) => {
const motions = await this.server.client.db.Motion.find().lean();
res.status(200).json(motions);
});
this.router.get('/proc', async (_req: Request, res: Response) => {
const proclamations = await this.server.client.db.Proclamation.find().lean();
res.status(200).send({ proclamations });
});
this.router.get('/resolution', async (_req, res) => {
const resolutions = await this.server.client.db.Resolution.find().lean();
res.status(200).json(resolutions);
});
this.router.patch('/motion/confirm', async (req, res) => {
if (!req.body.pin) {
return res.status(401).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
const director = await this.server.client.db.Score.findOne({ pin: req.body.pin });
const staffGuild = this.server.client.guilds.get(this.server.client.config.guildID) || await this.server.client.getRESTGuild(this.server.client.config.guildID);
const directorDiscord = staffGuild.members.get(director.userID);
if (!director || !directorDiscord?.roles?.includes('662163685439045632')) {
return res.status(403).json({
code: this.constants.codes.UNAUTHORIZED,
message: this.constants.messages.UNAUTHORIZED,
});
}
if (!req.params.id) {
return res.status(400).json({
code: this.constants.codes.CLIENT_ERROR,
message: this.constants.messages.CLIENT_ERROR,
});
}
if (!(await this.server.client.db.Motion.exists({ oID: req.params.id, processed: false }))) {
return res.status(404).json({
code: this.constants.codes.NOT_FOUND,
message: this.constants.messages.NOT_FOUND,
});
}
const yea = Number(req.body.yea);
const nay = Number(req.body.nay);
const present = Number(req.body.present);
const absent = Number(req.body.absent);
if (Number.isNaN(yea) || Number.isNaN(nay) || Number.isNaN(present) || Number.isNaN(absent)) {
return res.status(400).json({
code: this.constants.codes.CLIENT_ERROR,
message: this.constants.messages.CLIENT_ERROR,
});
}
const motion = await this.server.client.db.Motion.findOne({ oID: req.params.id });
await motion.updateOne({
processed: true,
results: {
yea,
nay,
present,
absent,
},
});
const directorStaffProfile = await this.server.client.db.Staff.findOne({ userID: directorDiscord.id });
const channel = <TextChannel>this.server.client.getChannel('807444198969835550');
const embed = new RichEmbed();
embed.setTitle('Motion Confirmed');
embed.setAuthor(`${directorDiscord.username}#${directorDiscord.discriminator}, ${directorStaffProfile.pn.join(', ')}`, directorDiscord.avatarURL);
embed.setColor('#27b17a');
embed.addField('Subject', motion.subject);
embed.addField('Body', motion.body);
embed.addField('Director', `<@!${motion.issuer}>`);
embed.setDescription(motion.oID);
embed.setFooter(motion.oID, directorDiscord.avatarURL);
embed.setTimestamp(new Date());
await channel.createMessage({ embed });
const excludingYea = nay + present + absent;
if (yea > excludingYea) {
const resolutionEmbed = new RichEmbed();
resolutionEmbed.setTitle('Resolution');
resolutionEmbed.setAuthor(`${directorDiscord.username}#${directorDiscord.discriminator}, ${directorStaffProfile.pn.join(', ')}`, directorDiscord.avatarURL);
resolutionEmbed.setColor('#75b0ff');
resolutionEmbed.addField('Subject', motion.subject);
resolutionEmbed.addField('Body', motion.body);
resolutionEmbed.addField('Director', `<@!${motion.issuer}>`);
resolutionEmbed.setDescription(motion.oID);
resolutionEmbed.setTimestamp(new Date());
const resMsg = await channel.createMessage({
content: `<@!${motion.issuer}>`,
embed: resolutionEmbed,
});
await this.server.client.db.Resolution.create({
issuer: motion.issuer,
subject: motion.subject,
body: motion.body,
at: Date.now(),
oID: motion.oID,
results: {
yea,
nay,
present,
absent,
},
msg: resMsg.id,
});
}
res.status(200).json({ message: `Confirmed results of motion with ID ${motion.oID}.` });
});
}
}

View File

@ -1,9 +1,8 @@
/* eslint-disable no-bitwise */
/* eslint-disable no-continue */
import jwt from 'jsonwebtoken';
import { v4 as uuid } from 'uuid';
import { TextChannel } from 'eris';
import { LocalStorage, Route, Server, RichEmbed } from '../../../class';
import { LocalStorage, Route, Server } from '../../../class';
import { ScoreHistoricalRaw } from '../../../models/ScoreHistorical';
import { getTotalMessageCount } from '../../../intervals/score';
@ -128,11 +127,12 @@ export default class Report extends Route {
else if (member.cloudServices > 10) cloudServicesScore = 10;
else cloudServicesScore = Math.round(member.cloudServices);
const inqs = await this.server.client.db.Inquiry.find({ userID: member.userID }).lean().exec();
const inquiries: [{ id?: string, name: string, date: Date }?] = [];
if (member.inquiries?.length > 0) {
for (const inq of member.inquiries) {
if (inqs?.length > 0) {
for (const inq of inqs) {
const testDate = (new Date(new Date(inq.date).setHours(1460)));
if (testDate > new Date()) inquiries.push({ id: inq.id, name: inq.name, date: inq.date });
if (testDate > new Date()) inquiries.push({ id: inq.iid, name: inq.name, date: inq.date });
}
}
@ -173,35 +173,14 @@ export default class Report extends Route {
array.push(data);
}
if (member.notify) {
const chan = await this.server.client.getDMChannel(member.userID);
try {
chan.createMessage(`__**Community Score - Hard Pull Notification**__\n*You have signed up to be notified whenever your hard score has been pulled. See \`?score\` for more information.*\n\n**Department/Service:** ${merchant.name.toUpperCase()}`);
} catch (err) {
this.server.client.util.signale.error(`Unable to DM user: ${member.userID} | ${err}`);
}
}
const inq = await this.server.client.report.createInquiry(member.userID, merchant.name, 0, req.body.reason);
await this.server.client.db.Merchant.updateOne({ key: req.headers.authorization }, { $addToSet: { pulls: { type: 0, reason: req.body.reason, date: new Date() } } });
const reportID = uuid();
await this.server.client.db.Score.updateOne({ userID: member.userID }, { $addToSet: { inquiries: { id: reportID, name: merchant.name, reason: req.body.reason, date: new Date(), report: member } } });
const embed = new RichEmbed();
embed.setTitle('Inquiry Notification');
embed.setDescription(reportID);
embed.setColor('#800080');
embed.addField('Member', `${mem.user.username}#${mem.user.discriminator} | <@${member.userID}>`, true);
embed.addField('Type', 'HARD', true);
embed.addField('Department/Service', merchant.name.toUpperCase(), true);
embed.addField('Reason', req.body.reason ?? 'N/A', true);
embed.setTimestamp();
embed.setFooter(this.server.client.user.username, this.server.client.user.avatarURL);
const chan = <TextChannel>this.server.client.guilds.get(this.server.client.config.guildID).channels.get('611584771356622849');
chan.createMessage({ embed }).catch(() => { });
return res.status(200).json({
code: this.constants.codes.SUCCESS,
message: {
id: reportID,
id: inq.id,
userID: member.userID,
memberInformation: {
username: mem.user.username,
@ -237,35 +216,24 @@ export default class Report extends Route {
const merchant = await this.server.client.db.Merchant.findOne({ key: req.headers.authorization }).lean().exec();
if (!merchant) return res.status(401).json({ code: this.constants.codes.UNAUTHORIZED, message: this.constants.messages.UNAUTHORIZED });
const member = await this.server.client.db.Score.findOne({ userID: req.body.userID }).lean().exec();
if (!member) return res.status(401).json({ code: this.constants.codes.UNAUTHORIZED, message: this.constants.messages.UNAUTHORIZED });
const report = await this.server.client.db.Score.findOne({ userID: req.body.userID }).lean().exec();
if (!report) return res.status(401).json({ code: this.constants.codes.UNAUTHORIZED, message: this.constants.messages.UNAUTHORIZED });
let totalScore: number;
if (member.total < 200) totalScore = 0;
else if (member.total > 800) totalScore = 800;
else totalScore = Math.round(member.total);
if (report.total < 200) totalScore = 0;
else if (report.total > 800) totalScore = 800;
else totalScore = Math.round(report.total);
await this.server.client.db.Merchant.updateOne({ key: req.headers.authorization }, { $addToSet: { pulls: { type: 1, reason: 'N/A', date: new Date() } } });
await this.server.client.db.Score.updateOne({ userID: member.userID }, { $addToSet: { softInquiries: { name: merchant.name, date: new Date() } } });
const embed = new RichEmbed();
embed.setTitle('Inquiry Notification');
embed.setColor('#00FFFF');
const mem = this.server.client.util.resolveMember(member.userID, this.server.client.guilds.get(this.server.client.config.guildID));
if (!mem) return res.status(404).json({ code: this.constants.codes.NOT_FOUND, message: this.constants.codes.NOT_FOUND });
embed.addField('Member', `${mem.user.username}#${mem.user.discriminator} | <@${member.userID}>`, true);
embed.addField('Type', 'SOFT', true);
embed.addField('Department/Service', merchant.name.toUpperCase(), true);
embed.setTimestamp();
embed.setFooter(this.server.client.user.username, this.server.client.user.avatarURL);
const chan = <TextChannel>this.server.client.guilds.get(this.server.client.config.guildID).channels.get('611584771356622849');
chan.createMessage({ embed }).catch(() => { });
const mem = this.server.client.util.resolveMember(report.userID, this.server.client.guilds.get(this.server.client.config.guildID));
await this.server.client.report.createInquiry(report.userID, merchant.name.toUpperCase(), 1);
if (!merchant.privileged) {
return res.status(200).json({
code: this.constants.codes.SUCCESS,
message: {
userID: member.userID,
userID: report.userID,
totalScore,
},
});
@ -287,36 +255,38 @@ export default class Report extends Route {
if ((mem.user.publicFlags & (1 << 17)) === 1 << 17) flags.push('EARLY_VERIFIED_BOT_DEVELOPER');
}
const set = [];
if (req.query.p?.toString() === 'true') {
const accounts = await this.server.client.db.Score.find().lean().exec();
for (const sc of accounts) {
if (sc.total < 200) { continue; }
if (sc.total > 800) { set.push(800); continue; }
set.push(sc.total);
}
}
let activityScore: number;
const moderationScore = Math.round(member.moderation);
const moderationScore = Math.round(report.moderation);
let roleScore: number;
let cloudServicesScore: number;
const otherScore = Math.round(member.other);
const otherScore = Math.round(report.other);
let miscScore: number;
if (member.activity < 10) activityScore = 0;
else if (member.activity > Math.floor((Math.log1p(getTotalMessageCount(this.server.client)) * 12))) activityScore = Math.floor((Math.log1p(getTotalMessageCount(this.server.client)) * 12));
else activityScore = Math.round(member.activity);
if (report.activity < 10) activityScore = 0;
else if (report.activity > Math.floor((Math.log1p(getTotalMessageCount(this.server.client)) * 12))) activityScore = Math.floor((Math.log1p(getTotalMessageCount(this.server.client)) * 12));
else activityScore = Math.round(report.activity);
if (member.roles <= 0) roleScore = 0;
else if (member.roles > 54) roleScore = 54;
else roleScore = Math.round(member.roles);
if (report.roles <= 0) roleScore = 0;
else if (report.roles > 54) roleScore = 54;
else roleScore = Math.round(report.roles);
if (member.staff <= 0) miscScore = 0;
else miscScore = Math.round(member.staff);
if (report.staff <= 0) miscScore = 0;
else miscScore = Math.round(report.staff);
if (member.cloudServices === 0) cloudServicesScore = 0;
else if (member.cloudServices > 10) cloudServicesScore = 10;
else cloudServicesScore = Math.round(member.cloudServices);
if (report.cloudServices === 0) cloudServicesScore = 0;
else if (report.cloudServices > 10) cloudServicesScore = 10;
else cloudServicesScore = Math.round(report.cloudServices);
const historicalData = await this.server.client.db.ScoreHistorical.find({ userID: member.userID }).lean().exec();
const historicalData = await this.server.client.db.ScoreHistorical.find({ userID: report.userID }).lean().exec();
const array: ScoreHistoricalRaw[] = [];
for (const data of historicalData) {
delete data.report?.softInquiries;
@ -357,7 +327,7 @@ export default class Report extends Route {
return res.status(200).json({
code: this.constants.codes.SUCCESS,
message: {
userID: member.userID,
userID: report.userID,
memberInformation: {
username: mem.user.username,
discriminator: mem.user.discriminator,
@ -368,7 +338,7 @@ export default class Report extends Route {
nitroBoost: mem.premiumSince === null,
},
totalScore,
percentile: Math.round(this.server.client.util.percentile(set, member.total)),
percentile: Math.round(this.server.client.util.percentile(set, report.total)),
activityScore,
moderationScore,
roleScore,
@ -403,17 +373,7 @@ export default class Report extends Route {
else totalScore = Math.round(member.total);
await this.server.client.db.Merchant.updateOne({ key: req.headers.authorization }, { $addToSet: { pulls: { type: 1, reason: 'N/A', date: new Date() } } });
await this.server.client.db.Score.updateOne({ userID: member.userID }, { $addToSet: { softInquiries: { name: merchant.name, date: new Date() } } });
const embed = new RichEmbed();
embed.setTitle('Inquiry Notification');
embed.setColor('#00FFFF');
embed.addField('Member', `${mem.user.username}#${mem.user.discriminator} | <@${member.userID}>`, true);
embed.addField('Type', 'SOFT', true);
embed.addField('Department/Service', merchant.name.toUpperCase(), true);
embed.setTimestamp();
embed.setFooter(this.server.client.user.username, this.server.client.user.avatarURL);
const chan = <TextChannel>this.server.client.guilds.get(this.server.client.config.guildID).channels.get('611584771356622849');
chan.createMessage({ embed }).catch(() => { });
await this.server.client.report.createInquiry(member.userID, merchant.name.toUpperCase(), 1);
if (!merchant.privileged) {
return res.status(200).json({
@ -486,42 +446,12 @@ export default class Report extends Route {
this.timeout.delete(req.ip);
if (staffScore.userID === score.userID) {
updated = true;
await this.server.client.db.Score.updateOne({ userID: score.userID }, { $addToSet: { softInquiries: { name: `${member.username} via report.libraryofcode.org @ IP ${req.ip}`, date: new Date() } } });
const embed = new RichEmbed();
embed.setTitle('Inquiry Notification');
embed.setColor('#00FFFF');
embed.addField('Member', `${member.user.username}#${member.user.discriminator} | <@${member.user.id}>`, true);
embed.addField('Type', 'SOFT', true);
embed.addField('Department/Service', `${member.username} via report.libraryofcode.org @ IP ${req.ip}`.toUpperCase(), true);
embed.setTimestamp();
embed.setFooter(this.server.client.user.username, this.server.client.user.avatarURL);
const chan = <TextChannel>this.server.client.guilds.get(this.server.client.config.guildID).channels.get('611584771356622849');
chan.createMessage({ embed }).catch(() => { });
await this.server.client.report.createInquiry(member.user.id, `${member.username} via report.libraryofcode.org @ IP ${req.ip}`, 1);
} else {
await this.server.client.db.Score.updateOne({ userID: score.userID }, { $addToSet: { softInquiries: { name: 'Library of Code sp-us | Staff Team via report.libraryofcode.org', date: new Date() } } });
const embed = new RichEmbed();
embed.setTitle('Inquiry Notification');
embed.setColor('#00FFFF');
embed.addField('Member', `${member.user.username}#${member.user.discriminator} | <@${member.user.id}>`, true);
embed.addField('Type', 'SOFT', true);
embed.addField('Department/Service', 'Library of Code sp-us | Staff Team via report.libraryofcode.org'.toUpperCase(), true);
embed.setTimestamp();
embed.setFooter(this.server.client.user.username, this.server.client.user.avatarURL);
const chan = <TextChannel>this.server.client.guilds.get(this.server.client.config.guildID).channels.get('611584771356622849');
chan.createMessage({ embed }).catch(() => { });
await this.server.client.report.createInquiry(member.user.id, 'Library of Code sp-us | Staff Team via report.libraryofcode.org', 1);
}
} else if (!updated) {
await this.server.client.db.Score.updateOne({ userID: score.userID }, { $addToSet: { softInquiries: { name: `${member.username} via report.libraryofcode.org @ IP ${req.ip}`, date: new Date() } } });
const embed = new RichEmbed();
embed.setTitle('Inquiry Notification');
embed.setColor('#00FFFF');
embed.addField('Member', `${member.user.username}#${member.user.discriminator} | <@${member.user.id}>`, true);
embed.addField('Type', 'SOFT', true);
embed.addField('Department/Service', `${member.username} via report.libraryofcode.org @ IP ${req.ip}`.toUpperCase(), true);
embed.setTimestamp();
embed.setFooter(this.server.client.user.username, this.server.client.user.avatarURL);
const chan = <TextChannel>this.server.client.guilds.get(this.server.client.config.guildID).channels.get('611584771356622849');
chan.createMessage({ embed }).catch(() => { });
await this.server.client.report.createInquiry(member.user.id, `${member.username} via report.libraryofcode.org @ IP ${req.ip}`, 1);
}
score = await this.server.client.db.Score.findOne({ pin: [Number(args.split('-')[0]), Number(args.split('-')[1]), Number(args.split('-')[2])] }).lean().exec();
@ -557,7 +487,7 @@ export default class Report extends Route {
else if (score.cloudServices > 10) cloudServicesScore = '10';
else cloudServicesScore = `${score.cloudServices}`;
const moderations = await this.server.client.db.Moderation.find({ userID: score.userID });
const moderations = await this.server.client.db.Moderation.find({ userID: score.userID }).lean().exec();
const historical = await this.server.client.db.ScoreHistorical.find({ userID: score.userID }).lean().exec();
@ -592,6 +522,17 @@ export default class Report extends Route {
data.report.total = total; data.report.activity = activity; data.report.moderation = moderation; data.report.roles = role; data.report.cloudServices = cloud; data.report.other = other; data.report.staff = misc;
}
const inqs = await this.server.client.db.Inquiry.find({ userID: score.userID }).lean().exec();
const hardInquiries: [{ id?: string, name: string, reason: string, date: Date }?] = [];
const softInquiries: [{ id?: string, name: string, date: Date }?] = [];
for (const inq of inqs) {
if (inq.type === 0) {
hardInquiries.push({ id: inq.iid, name: inq.name, reason: inq.reason, date: inq.date });
} else if (inq.type === 1) {
softInquiries.push({ name: inq.name, date: inq.date });
}
}
return res.status(200).json({
name: `${member.username}#${member.discriminator}`,
avatarURL: member.avatarURL,
@ -607,8 +548,8 @@ export default class Report extends Route {
notify: score.notify,
locked: !!score.locked,
totalModerations: moderations?.length > 0 ? moderations.length : 0,
inquiries: score.inquiries?.length > 0 ? score.inquiries : [],
softInquiries: score.softInquiries?.length > 0 ? score.softInquiries : [],
inquiries: hardInquiries?.length > 0 ? hardInquiries.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()) : [],
softInquiries: softInquiries?.length > 0 ? softInquiries.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()) : [],
historical: historical ?? [],
lastUpdated: score.lastUpdate,
});

View File

@ -1,10 +1,8 @@
import locsh from './loc.sh/main';
import crins from './cr.ins/main';
import commlibraryofcodeorg from './comm.libraryofcode.org/main';
import boardins from './board.ins/main';
export default {
'board.ins': boardins,
'loc.sh': locsh,
'cr.ins': crins,
'comm.libraryofcode.org': commlibraryofcodeorg,

View File

@ -7,20 +7,17 @@ import { Collection, Command, LocalStorage, Queue, Util, ServerManagement, Event
import {
Customer, CustomerInterface,
CustomerPortal, CustomerPortalInterface,
ExecutiveOrder, ExecutiveOrderInterface,
File, FileInterface,
Inquiry, InquiryInterface,
Member, MemberInterface,
Merchant, MerchantInterface,
Moderation, ModerationInterface,
Motion, MotionInterface,
NNTrainingData, NNTrainingDataInterface,
Note, NoteInterface,
PagerNumber, PagerNumberInterface,
Proclamation, ProclamationInterface,
Promo, PromoInterface,
Rank, RankInterface,
Redirect, RedirectInterface,
Resolution, ResolutionInterface,
Score, ScoreInterface,
ScoreHistorical, ScoreHistoricalInterface,
Staff, StaffInterface,
@ -47,29 +44,7 @@ export default class Client extends eris.Client {
public stripe: Stripe;
public db: {
Customer: mongoose.Model<CustomerInterface>,
CustomerPortal: mongoose.Model<CustomerPortalInterface>,
ExecutiveOrder: mongoose.Model<ExecutiveOrderInterface>,
File: mongoose.Model<FileInterface>,
Member: mongoose.Model<MemberInterface>,
Merchant: mongoose.Model<MerchantInterface>,
Moderation: mongoose.Model<ModerationInterface>,
Motion: mongoose.Model<MotionInterface>,
NNTrainingData: mongoose.Model<NNTrainingDataInterface>,
Note: mongoose.Model<NoteInterface>,
PagerNumber: mongoose.Model<PagerNumberInterface>,
Proclamation: mongoose.Model<ProclamationInterface>,
Promo: mongoose.Model<PromoInterface>,
Rank: mongoose.Model<RankInterface>,
Redirect: mongoose.Model<RedirectInterface>,
Resolution: mongoose.Model<ResolutionInterface>,
Score: mongoose.Model<ScoreInterface>,
ScoreHistorical: mongoose.Model<ScoreHistoricalInterface>,
Staff: mongoose.Model<StaffInterface>,
Stat: mongoose.Model<StatInterface>,
local: { muted: LocalStorage }
};
public db: { Customer: mongoose.Model<CustomerInterface>, CustomerPortal: mongoose.Model<CustomerPortalInterface>, File: mongoose.Model<FileInterface>, Inquiry: mongoose.Model<InquiryInterface>, Member: mongoose.Model<MemberInterface>, Merchant: mongoose.Model<MerchantInterface>, Moderation: mongoose.Model<ModerationInterface>, NNTrainingData: mongoose.Model<NNTrainingDataInterface>, Note: mongoose.Model<NoteInterface>, PagerNumber: mongoose.Model<PagerNumberInterface>, Promo: mongoose.Model<PromoInterface>, Rank: mongoose.Model<RankInterface>, Redirect: mongoose.Model<RedirectInterface>, Score: mongoose.Model<ScoreInterface>, ScoreHistorical: mongoose.Model<ScoreHistoricalInterface>, Staff: mongoose.Model<StaffInterface>, Stat: mongoose.Model<StatInterface>, local: { muted: LocalStorage } };
constructor(token: string, options?: eris.ClientOptions) {
super(token, options);
@ -77,9 +52,18 @@ export default class Client extends eris.Client {
this.events = new Collection<Event>();
this.intervals = new Collection<NodeJS.Timeout>();
this.queue = new Queue(this);
this.db = { Customer, CustomerPortal, ExecutiveOrder, File, Member, Merchant, Moderation, Motion, NNTrainingData, Note, PagerNumber, Proclamation, Promo, Rank, Redirect, Resolution, Score, ScoreHistorical, Staff, Stat, local: { muted: new LocalStorage('muted') } };
this.db = { Customer, CustomerPortal, File, Inquiry, Member, Merchant, Moderation, NNTrainingData, Note, PagerNumber, Promo, Rank, Redirect, Score, ScoreHistorical, Staff, Stat, local: { muted: new LocalStorage('muted') } };
}
get report() {
return this.util.report;
}
get pbx() {
return this.util.pbx;
}
public async loadDatabase() {
await mongoose.connect(this.config.mongoDB, { useNewUrlParser: true, useUnifiedTopology: true, poolSize: 50 });

View File

@ -1,6 +1,7 @@
/* eslint-disable no-constant-condition */
import { promises as fs, accessSync, constants, writeFileSync } from 'fs';
import { promisify } from 'util';
import { join } from 'path';
import { gzip, gzipSync, unzip } from 'zlib';
type JSONData = [{ key: string, value: any }?];
@ -18,7 +19,7 @@ export default class LocalStorage {
private locked: boolean = false;
constructor(dbName: string, dir = `${__dirname}/../../localstorage`) {
this.storagePath = `${dir}/${dbName}.json.gz`;
this.storagePath = join(__dirname, '../../localstorage') || dir;
this.init();
}

View File

@ -1,9 +1,10 @@
/* eslint-disable no-await-in-loop */
/* eslint-disable no-eval */
import Bull from 'bull';
import cron from 'cron';
import { TextableChannel } from 'eris';
import { TextableChannel, TextChannel } from 'eris';
import { Client, RichEmbed } from '.';
import { ScoreInterface } from '../models';
import { ScoreInterface, InqType as InquiryType } from '../models';
import { apply as Apply } from '../commands';
@ -28,12 +29,13 @@ export default class Queue {
const startDate = new Date();
for (const report of reports) {
const inqs = await this.client.db.Inquiry.find({ userID: report.userID });
const data = new this.client.db.ScoreHistorical({
userID: report.userID,
report,
inquiries: inqs.map((inq) => inq._id),
date: startDate,
});
// eslint-disable-next-line no-await-in-loop
await data.save();
}
} catch (err) {
@ -77,6 +79,37 @@ export default class Queue {
}
protected setProcessors() {
this.queues.score.process('score::inquiry', async (job: Bull.Job<{ inqID: string, userID: string, name: string, type: InquiryType, reason?: string }>) => {
const member = this.client.util.resolveMember(job.data.userID, this.client.guilds.get(this.client.config.guildID));
const report = await this.client.db.Score.findOne({ userID: job.data.userID }).lean().exec();
const embed = new RichEmbed();
if (job.data.type === InquiryType.HARD) {
embed.setTitle('Inquiry Notification');
embed.setDescription(job.data.inqID);
embed.setColor('#800080');
embed.addField('Member', `${member.user.username}#${member.user.discriminator} | <@${job.data.userID}>`, true);
embed.addField('Type', 'HARD', true);
embed.addField('Department/Service', job.data.name.toUpperCase(), true);
embed.addField('Reason', job.data.reason ?? 'N/A', true);
embed.setTimestamp();
embed.setFooter(this.client.user.username, this.client.user.avatarURL);
if (report.notify === true) {
await this.client.getDMChannel(job.data.userID).then((chan) => {
chan.createMessage(`__**Community Score - Hard Pull Notification**__\n*You have signed up to be notified whenever your hard score has been pulled. See \`?score\` for more information.*\n\n**Department/Service:** ${job.data.name.toUpperCase()}`);
}).catch(() => {});
}
} else {
embed.setTitle('Inquiry Notification');
embed.setColor('#00FFFF');
embed.addField('Member', `${member.user.username}#${member.user.discriminator} | <@${job.data.userID}>`, true);
embed.addField('Type', 'SOFT', true);
embed.addField('Department/Service', job.data.name.toUpperCase(), true);
embed.setTimestamp();
embed.setFooter(this.client.user.username, this.client.user.avatarURL);
}
const log = <TextChannel> this.client.guilds.get(this.client.config.guildID).channels.get('611584771356622849');
log.createMessage({ embed }).catch(() => {});
});
this.queues.score.process('score::update', async (job: Bull.Job<{ score: ScoreInterface, total: number, activity: number, roles: number, moderation: number, cloudServices: number, other: number, staff: number }>) => {
await this.client.db.Score.updateOne({ userID: job.data.score.userID }, { $set: { total: job.data.total, activity: job.data.activity, roles: job.data.roles, moderation: job.data.moderation, cloudServices: job.data.cloudServices, other: job.data.other, staff: job.data.staff, lastUpdate: new Date() } });
if (!job.data.score.pin || job.data.score.pin?.length < 1) {
@ -123,8 +156,12 @@ export default class Queue {
});
}
public addInquiry(inqID: string, userID: string, name: string, type: InquiryType, reason?: string) {
return this.queues.score.add('score::inquiry', { inqID, userID, name, type, reason });
}
public updateScore(score: ScoreInterface, total: number, activity: number, roles: number, moderation: number, cloudServices: number, other: number, staff: number) {
this.queues.score.add('score::update', { score, total, activity, roles, moderation, cloudServices, other, staff });
return this.queues.score.add('score::update', { score, total, activity, roles, moderation, cloudServices, other, staff });
}
public processApplication(channelInformation: { messageID: string, guildID: string, channelID: string }, url: string, userID: string, func?: string) {

97
src/class/Report.ts Normal file
View File

@ -0,0 +1,97 @@
import { v4 as uuid } from 'uuid';
import { Client } from '.';
import { InqType } from '../models';
export default class Report {
public client: Client;
constructor(client: Client) {
this.client = client;
}
public async createInquiry(userID: string, name: string, type: InqType, reason?: string) {
const report = await this.client.db.Score.findOne({ userID }).lean().exec();
const member = this.client.util.resolveMember(userID, this.client.guilds.get(this.client.config.guildID));
if (!report || !member) return null;
if (type === InqType.HARD && report.locked) return null;
const mod = await (new this.client.db.Inquiry({
iid: uuid(),
userID,
name,
type,
reason,
date: new Date(),
report,
}).save());
this.client.queue.addInquiry(mod.iid, userID, name, type, reason);
return mod;
}
/* public async soft(userID: string) {
const report = await this.client.db.Score.findOne({ userID });
if (!report) return null;
let totalScore: number;
let activityScore: number;
const moderationScore = Math.round(report.moderation);
let roleScore: number;
let cloudServicesScore: number;
const otherScore = Math.round(report.other);
let miscScore: number;
if (report.total < 200) totalScore = 0;
else if (report.total > 800) totalScore = 800;
else totalScore = Math.round(report.total);
if (report.activity < 10) activityScore = 0;
else if (report.activity > Math.floor((Math.log1p(3000 + 300 + 200 + 100) * 12))) activityScore = Math.floor((Math.log1p(3000 + 300 + 200 + 100) * 12));
else activityScore = Math.round(report.activity);
if (report.roles <= 0) roleScore = 0;
else if (report.roles > 54) roleScore = 54;
else roleScore = Math.round(report.roles);
if (report.staff <= 0) miscScore = 0;
else miscScore = Math.round(report.staff);
if (report.cloudServices === 0) cloudServicesScore = 0;
else if (report.cloudServices > 10) cloudServicesScore = 10;
else cloudServicesScore = Math.round(report.cloudServices);
const historicalData = await this.client.db.ScoreHistorical.find({ userID: member.userID }).lean().exec();
const array: ScoreHistoricalRaw[] = [];
for (const data of historicalData) {
let total: number;
let activity: number;
const moderation = Math.round(data.report.moderation);
let role: number;
let cloud: number;
const other = Math.round(data.report.other);
let misc: number;
if (data.report.total < 200) total = 0;
else if (data.report.total > 800) total = 800;
else total = Math.round(data.report.total);
if (data.report.activity < 10) activity = 0;
else if (data.report.activity > Math.floor((Math.log1p(3000 + 300 + 200 + 100) * 12))) activity = Math.floor((Math.log1p(3000 + 300 + 200 + 100) * 12));
else activity = Math.round(data.report.activity);
if (data.report.roles <= 0) role = 0;
else if (data.report.roles > 54) role = 54;
else role = Math.round(data.report.roles);
if (data.report.staff <= 0) role = 0;
else misc = Math.round(data.report.staff);
if (data.report.cloudServices === 0) cloud = 0;
else if (data.report.cloudServices > 10) cloud = 10;
else cloud = Math.round(data.report.cloudServices);
data.report.total = total; data.report.activity = activity; data.report.moderation = moderation; data.report.roles = role; data.report.cloudServices = cloud; data.report.other = other; data.report.staff = misc;
array.push(data);
}
} */
}

View File

@ -56,6 +56,7 @@ export default class Server {
public init() {
if (this.parse) {
this.app.use(bodyParser.json());
this.app.use(bodyParser.urlencoded({ extended: true }));
}
this.app.set('trust proxy', 'loopback');
this.app.use(helmet({

View File

@ -4,7 +4,7 @@ import childProcess from 'child_process';
import { promisify } from 'util';
import signale from 'signale';
import { Member, Message, Guild, PrivateChannel, GroupChannel, Role, AnyGuildChannel, WebhookPayload } from 'eris';
import { Client, Command, Moderation, PBX, RichEmbed } from '.';
import { Client, Command, Moderation, PBX, Report, RichEmbed } from '.';
import { statusMessages as emotes } from '../configs/emotes.json';
export default class Util {
@ -18,6 +18,8 @@ export default class Util {
public pbx: PBX;
public report: Report;
constructor(client: Client) {
this.client = client;
this.moderation = new Moderation(this.client);
@ -33,6 +35,7 @@ export default class Util {
auth: { user: 'internal', pass: this.client.config.emailPass },
});
this.pbx = new PBX(this.client);
this.report = new Report(this.client);
}
get emojis() {
@ -43,6 +46,46 @@ export default class Util {
};
}
public hrn(number: any, fixed: number, formatter: any | any[]) {
const builtInFormatters = {
en: ['KMGTPEZY'.split(''), 1e3],
zh_CN: ['百千万亿兆京垓秭'.split(''), [100, 10, 10, 1e4, 1e4, 1e4, 1e4, 1e4]],
};
number = Math.abs(number);
if (!fixed && fixed !== 0) fixed = 1;
if (typeof formatter === 'object') {
const name = `${new Date().getTime()}`;
builtInFormatters[name] = formatter;
formatter = name;
}
if (!builtInFormatters[formatter]) formatter = 'en';
formatter = builtInFormatters[formatter];
let power = 1;
const texts = formatter[0];
const powers = formatter[1];
let loop = 0;
let is_array = false;
if (typeof powers === 'object') is_array = true;
// eslint-disable-next-line no-constant-condition
while (1) {
if (is_array) power = powers[loop];
else power = powers;
if (number >= power && loop < texts.length) number /= power;
else {
number = number.toFixed(fixed);
return loop === 0 ? number : `${number} ${texts[loop - 1]}`;
}
// eslint-disable-next-line no-plusplus
++loop;
}
}
/**
* Resolves a command
* @param query Command input

View File

@ -7,6 +7,7 @@ export { default as LocalStorage } from './LocalStorage';
export { default as Moderation } from './Moderation';
export { default as PBX } from './PBX';
export { default as Queue } from './Queue';
export { default as Report } from './Report';
export { default as RichEmbed } from './RichEmbed';
export { default as Route } from './Route';
export { default as Server } from './Server';

View File

@ -7,7 +7,7 @@ export default class Callback extends Command {
constructor(client: Client) {
super(client);
this.name = 'callback';
this.description = 'Requests a Callback from a Technican.\nPlease use `-` to separate the number if needed. E.x. 202-750-2585.\nDo note, we are unable to dial international numbers outside of the US and Canada.';
this.description = 'Requests a Callback from a Technican.\nPlease use `-` to separate the number if needed. E.x. 202-750-2585.\nDo note, we are unable to dial international numbers outside of the US and Canada.\n\n*We recommend you run this command in your DMs for privacy.*';
this.usage = 'callback <the number you want us to call>';
this.aliases = ['cb'];
this.permissions = 0;
@ -31,17 +31,7 @@ export default class Callback extends Command {
embed.addField('Phone Number Type', phone.getType(), true);
const communityReport = await this.client.db.Score.findOne({ userID: message.author.id }).lean().exec();
if (communityReport) {
await this.client.db.Score.updateOne({ userID: message.author.id }, { $addToSet: { softInquiries: { name: 'LIBRARY OF CODE SP-US | VOIP/PBX MEMBER SUPPORT SVCS', date: new Date() } } });
const embed2 = new RichEmbed();
embed2.setTitle('Inquiry Notification');
embed2.setColor('#00FFFF');
embed2.addField('Member', `${member.user.username}#${member.user.discriminator} | <@${member.user.id}>`, true);
embed2.addField('Type', 'SOFT', true);
embed2.addField('Department/Service', 'Library of Code sp-us | VOIP/PBX Member Support SVCS'.toUpperCase(), true);
embed2.setTimestamp();
embed2.setFooter(this.client.user.username, this.client.user.avatarURL);
const log = <TextChannel> this.mainGuild.channels.get('611584771356622849');
log.createMessage({ embed: embed2 }).catch(() => {});
await this.client.report.createInquiry(member.user.id, 'Library of Code sp-us | VOIP/PBX Member Support SVCS', 1);
embed.addField('PIN', `${communityReport.pin[0]}-${communityReport.pin[1]}-${communityReport.pin[2]}`, true);
}
try {

View File

@ -1,5 +1,4 @@
import { Message } from 'eris';
import { randomBytes } from 'crypto';
import { Client, Command } from '../class';
export default class DelMerchant extends Command {

View File

@ -21,6 +21,7 @@ export { default as info } from './info';
export { default as intercom } from './intercom';
export { default as kick } from './kick';
export { default as listredirects } from './listredirects';
export { default as market } from './market';
export { default as members } from './members';
export { default as mute } from './mute';
export { default as notes } from './notes';
@ -32,6 +33,7 @@ export { default as pulldata } from './pulldata';
export { default as rank } from './rank';
export { default as roleinfo } from './roleinfo';
export { default as score } from './score';
export { default as sip } from './sip';
export { default as site } from './site';
export { default as stats } from './stats';
export { default as storemessages } from './storemessages';

View File

@ -25,7 +25,7 @@ export default class Intercom extends Command {
channel: `PJSIP/${args[0]}`,
exten: args[0],
context: 'from-internal',
CallerID: `TTS PAGE FRM ${message.author.username} <*0>`,
CallerID: `TTS INTC FRM ${message.author.username} <000>`,
application: 'PlayBack',
priority: '1',
data: `beep&${recordingLocation.split(':')[1]}`,

View File

@ -1,4 +1,4 @@
import { Message, User, Member } from 'eris';
import { Member, Message } from 'eris';
import { Client, Command } from '../class';
export default class Kick extends Command {

59
src/commands/market.ts Normal file
View File

@ -0,0 +1,59 @@
import { Message } from 'eris';
import stockInfo from 'stock-info';
import { Client, Command, RichEmbed } from '../class';
export default class Market extends Command {
constructor(client: Client) {
super(client);
this.name = 'market';
this.description = 'Fetches information from a ticker for a stock or fund.';
this.usage = `${this.client.config.prefix}market <ticker>`;
this.permissions = 0;
this.guildOnly = true;
this.enabled = true;
}
public async run(message: Message, args: string[]) {
try {
if (!args[0]) return this.client.commands.get('help').run(message, [this.name]);
let stock: stockInfo.Stock;
try {
stock = await stockInfo.getSingleStockInfo(args[0]);
} catch (err) {
return this.error(message.channel, `Unable to fetch information for that ticker. | ${err}`);
}
const embed = new RichEmbed();
embed.setTitle(stock.longName ?? stock.symbol);
let type: string;
switch (stock.quoteType) {
case 'EQUITY':
type = 'Common/Preferred Stock';
break;
case 'ETF':
type = 'Exchange Traded Fund (ETF)';
break;
case 'MUTUALFUND':
type = 'Mutual Fund';
break;
default:
type = 'N/A or Unknown';
break;
}
embed.addField('Type', type, true);
embed.addField('Market Cap', `${stock.marketCap ? this.client.util.hrn(stock.marketCap, undefined, undefined) : 'N/A'}`, true);
embed.addField('Day Quote Price', stock.regularMarketPrice ? `$${Number(stock.regularMarketPrice).toFixed(2)}` : 'N/A', true);
embed.addField('Day G/L', stock.regularMarketChange ? `$${Number(stock.regularMarketChange.toFixed(2))}` : 'N/A', true);
embed.addField('Day Range', stock.regularMarketDayRange ?? 'N/A', true);
embed.addField('Bid/Ask', `Bid: $${stock.bid?.toFixed(2) ?? 'N/A'} | Ask: $${stock.ask?.toFixed(2) ?? 'N/A'}`, true);
embed.addField('Forward P/E (Price/Earnings)', `${stock.forwardPE?.toFixed(2) ?? 'N/A'}`, true);
embed.addField('Forward EPS (Earnings Per Share)', `${stock.epsForward?.toFixed(2) ?? 'N/A'}`, true);
embed.addField('Exchange', `${stock.fullExchangeName?.toUpperCase() ?? 'N/A'}`, true);
embed.setFooter(this.client.user.username, this.client.user.avatarURL);
embed.setTimestamp();
return message.channel.createMessage({ embed });
} catch (err) {
return this.client.util.handleError(err, message, this, false);
}
}
}

View File

@ -205,7 +205,7 @@ export default class Page extends Command {
const chan = await this.client.util.pbx.ari.channels.originate({
endpoint: `PJSIP/${member.extension}`,
extension: `${member.extension}`,
callerId: `LOC PBX - PAGE FRM ${senderNumber} <00>`,
callerId: `PAGE FRM ${senderNumber} <000>`,
context: 'from-internal',
priority: 1,
app: 'cr-zero',

View File

@ -12,42 +12,10 @@ export default class Profile extends Command {
}
public async run(message: Message, args: string[]) {
const profile = await this.client.db.Member.findOne({ userID: message.author.id });
if (!profile) return this.error(message.channel, 'Please try again later. We do not currently have a member profile for you.');
if (!args[1]) return this.error(message.channel, 'No new value was provided.');
const newValue = args.slice(1).join(' ');
if (newValue.length >= 512) return this.error(message.channel, 'Please shorten your input.');
if (!await this.client.db.Member.exists({ userID: message.author.id })) {
await this.client.db.Member.create({ userID: message.author.id });
}
switch (args[0]) {
case 'github':
await profile.updateOne({
additional: {
...profile.additional,
github: args[1],
},
});
this.success(message.channel, 'Updated GitHub.');
break;
case 'gitlab':
await profile.updateOne({
additional: {
...profile.additional,
gitlab: args[1],
},
});
this.success(message.channel, 'Updated GitLab.');
break;
case 'bio':
await profile.updateOne({
additional: {
...profile.additional,
bio: newValue,
},
});
this.success(message.channel, 'Updated bio.');
break;
default:
return this.error(message.channel, 'Please specify a valid option to change.');
}
this.error(message.channel, `Please specify a valid option to change. Choose from \`github\`, \`bio\` and \`gitlab\`. You can view your profile with \`${this.client.config.prefix}whois\`.`);
}
}

View File

@ -0,0 +1,30 @@
import { Message } from 'eris';
import { Client, Command } from '../class';
export default class Profile_Bio extends Command {
constructor(client: Client) {
super(client);
this.name = 'bio';
this.description = 'Updates your bio on your profile.';
this.usage = `${this.client.config.prefix}bio <new bio>`;
this.permissions = 0;
this.enabled = true;
}
public async run(message: Message, args: string[]) {
if (!await this.client.db.Member.exists({ userID: message.author.id })) {
await this.client.db.Member.create({ userID: message.author.id });
}
if (!args[0]) return this.error(message.channel, 'No new bio content was provided.');
const bio = args.join(' ');
if (bio.length >= 256) return this.error(message.channel, 'Bio too long. It must be less than or equal to 256 characters.');
const member = await this.client.db.Member.findOne({ userID: message.author.id });
await member.updateOne({
additional: {
...member.additional,
bio,
},
});
}
}

View File

@ -0,0 +1,38 @@
import { Message } from 'eris';
import { Client, Command } from '../class';
export default class Profile_GitHub extends Command {
constructor(client: Client) {
super(client);
this.name = 'github';
this.description = 'Updates your GitHub information on your profile.';
this.usage = `${this.client.config.prefix}github <GitHub profile URL>`;
this.permissions = 0;
this.enabled = true;
}
public async run(message: Message, args: string[]) {
if (!await this.client.db.Member.exists({ userID: message.author.id })) {
await this.client.db.Member.create({ userID: message.author.id });
}
if (!args[0]) return this.error(message.channel, 'No GitHub profile URL was provided.');
const urlRegex = new RegExp(
'^(https?:\\/\\/)?'
+ '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'
+ '((\\d{1,3}\\.){3}\\d{1,3}))'
+ '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'
+ '(\\?[;&a-z\\d%_.~+=-]*)?'
+ '(\\#[-a-z\\d_]*)?$',
'i',
);
if (!urlRegex.test(args[0]) || !args[0].startsWith('https://github.com/')) return this.error(message.channel, 'Invalid GitHub profile URL.');
const member = await this.client.db.Member.findOne({ userID: message.author.id });
await member.updateOne({
additional: {
...member.additional,
github: args[0],
},
});
}
}

View File

@ -0,0 +1,38 @@
import { Message } from 'eris';
import { Client, Command } from '../class';
export default class Profile_GitLab extends Command {
constructor(client: Client) {
super(client);
this.name = 'gitlab';
this.description = 'Updates your GitLab information on your profile.';
this.usage = `${this.client.config.prefix}gitlab <GitLab profile URL>`;
this.permissions = 0;
this.enabled = true;
}
public async run(message: Message, args: string[]) {
if (!await this.client.db.Member.exists({ userID: message.author.id })) {
await this.client.db.Member.create({ userID: message.author.id });
}
if (!args[0]) return this.error(message.channel, 'No GitLab profile URL was provided.');
const urlRegex = new RegExp(
'^(https?:\\/\\/)?'
+ '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'
+ '((\\d{1,3}\\.){3}\\d{1,3}))'
+ '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'
+ '(\\?[;&a-z\\d%_.~+=-]*)?'
+ '(\\#[-a-z\\d_]*)?$',
'i',
);
if (!urlRegex.test(args[0]) || !args[0].startsWith('https://gitlab.com/')) return this.error(message.channel, 'Invalid GitLab profile URL.');
const member = await this.client.db.Member.findOne({ userID: message.author.id });
await member.updateOne({
additional: {
...member.additional,
gitlab: args[0],
},
});
}
}

View File

@ -27,18 +27,7 @@ export default class Score extends Command {
const report = score.inquiries.find((inq) => inq.id === args[1]);
if (!report) return this.error(message.channel, 'Could not find inquiry information.');
await this.client.db.Score.updateOne({ userID: member.id }, { $addToSet: { softInquiries: { name: 'Library of Code sp-us | Bureau of Community Reports', date: new Date() } } });
const embed2 = new RichEmbed();
embed2.setTitle('Inquiry Notification');
embed2.setColor('#00FFFF');
const mem = this.client.util.resolveMember(score.userID, this.client.guilds.get(this.client.config.guildID));
embed2.addField('Member', `${mem.user.username}#${mem.user.discriminator} | <@${score.userID}>`, true);
embed2.addField('Type', 'SOFT', true);
embed2.addField('Department/Service', 'Library of Code sp-us | Bureau of Community Reports'.toUpperCase(), true);
embed2.setTimestamp();
embed2.setFooter(this.client.user.username, this.client.user.avatarURL);
const log = <TextChannel> this.client.guilds.get(this.client.config.guildID).channels.get('611584771356622849');
log.createMessage({ embed: embed2 }).catch(() => {});
await this.client.report.createInquiry(member.id, 'Library of Code sp-us | Bureau of Community Reports', 1);
const embed = new RichEmbed();
embed.setTitle(`Hard Inquiry Information - ${report.id}`);

View File

@ -1,8 +1,8 @@
/* eslint-disable no-plusplus */
/* eslint-disable no-continue */
/* eslint-disable default-case */
import moment from 'moment';
import { v4 as uuid } from 'uuid';
import { Message, User, TextChannel } from 'eris';
import { Message, User } from 'eris';
import { Client, Command, RichEmbed } from '../class';
import { getTotalMessageCount } from '../intervals/score';
import Score_Hist from './score_hist';
@ -15,6 +15,7 @@ export default class Score extends Command {
this.name = 'score';
this.description = 'Retrieves your Community Report';
this.usage = `${this.client.config.prefix}score\n${this.client.config.prefix}score <member> <type: 'hard' | 'soft'> <reporting department: ex. Library of Code sp-us | Cloud Account Services>:<reason>`;
this.aliases = ['report'];
this.subcmds = [Score_Hist, Score_Notify, Score_Pref];
this.permissions = 0;
this.guildOnly = false;
@ -28,18 +29,7 @@ export default class Score extends Command {
if (!args[0] || !this.checkCustomPermissions(this.client.util.resolveMember(message.author.id, this.mainGuild), 4)) {
user = message.author;
if (!user) return this.error(message.channel, 'Member not found.');
await this.client.db.Score.updateOne({ userID: user.id }, { $addToSet: { softInquiries: { name: `${user.username} via Discord`, date: new Date() } } });
const embed = new RichEmbed();
embed.setTitle('Inquiry Notification');
embed.setColor('#00FFFF');
const mem = this.client.util.resolveMember(user.id, this.client.guilds.get(this.client.config.guildID));
embed.addField('Member', `${mem.user.username}#${mem.user.discriminator} | <@${user.id}>`, true);
embed.addField('Type', 'SOFT', true);
embed.addField('Department/Service', `${user.username} via Discord`.toUpperCase(), true);
embed.setTimestamp();
embed.setFooter(this.client.user.username, this.client.user.avatarURL);
const log = <TextChannel>this.client.guilds.get(this.client.config.guildID).channels.get('611584771356622849');
log.createMessage({ embed }).catch(() => { });
await this.client.report.createInquiry(user.id, `${user.username} via Discord`, 1);
check = true;
} else {
user = this.client.util.resolveMember(args[0], this.mainGuild)?.user;
@ -57,30 +47,12 @@ export default class Score extends Command {
const score = await this.client.db.Score.findOne({ userID: user.id });
if (!score) return this.error(message.channel, 'Score not calculated yet.');
if (score.locked) return this.error(message.channel, 'The score report you have requested has been locked.');
const reportID = uuid();
await this.client.db.Score.updateOne({ userID: user.id }, { $addToSet: { inquiries: { id: reportID, name, reason, date: new Date(), report: score.toObject() } } });
const embed = new RichEmbed();
embed.setTitle('Inquiry Notification');
embed.setDescription(reportID);
embed.setColor('#800080');
const mem = this.client.util.resolveMember(score.userID, this.client.guilds.get(this.client.config.guildID));
embed.addField('Member', `${mem.user.username}#${mem.user.discriminator} | <@${score.userID}>`, true);
embed.addField('Type', 'HARD', true);
embed.addField('Department/Service', name.toUpperCase(), true);
embed.addField('Reason', reason ?? 'N/A', true);
embed.setTimestamp();
embed.setFooter(this.client.user.username, this.client.user.avatarURL);
const log = <TextChannel>this.client.guilds.get(this.client.config.guildID).channels.get('611584771356622849');
log.createMessage({ embed }).catch(() => { });
if (score.notify === true) {
await this.client.getDMChannel(user.id).then((chan) => {
chan.createMessage(`__**Community Score - Hard Pull Notification**__\n*You have signed up to be notified whenever your hard score has been pulled. See \`?score\` for more information.*\n\n**Department/Service:** ${name.toUpperCase()}`);
}).catch(() => { });
}
await this.client.report.createInquiry(score.userID, name, 0, reason);
}
}
if (!user) return this.error(message.channel, 'Member not found.');
const score = await this.client.db.Score.findOne({ userID: user.id });
const inqs = await this.client.db.Inquiry.find({ userID: user.id, type: 0 });
if (!score) return this.error(message.channel, 'Community Report has not been generated yet.');
let totalScore = '0';
let activityScore = '0';
@ -134,13 +106,18 @@ export default class Score extends Command {
break;
}
} */
if (score?.inquiries?.length > 0) {
let inqAdded = 0;
if (inqs?.length > 0) {
let desc = '__**Hard Inquiries**__\n*These inquiries will fall off your report within 2 months, try to keep your hard inquiries to a minimum. If you want to file a dispute, please DM Ramirez.*\n\n';
score.inquiries.forEach((inq) => {
inqs.forEach((inq) => {
const testDate = (new Date(new Date(inq.date).setHours(1460)));
// eslint-disable-next-line no-useless-escape
if (testDate > new Date()) desc += `${inq.id ? `__[${inq.id}]__\n` : ''}**Department/Service:** ${inq.name.replace(/\*/gmi, '')}\n**Reason:** ${inq.reason}\n**Date:** ${inq.date.toLocaleString('en-us')} ET\n**Expires:** ${moment(testDate).calendar()}\n\n`;
if (testDate > new Date()) {
desc += `${inq.iid ? `__[${inq.iid}]__\n` : ''}**Department/Service:** ${inq.name.replace(/\*/gmi, '')}\n**Reason:** ${inq.reason}\n**Date:** ${moment(inq.date).format('MMMM Do YYYY, h:mm:ss')}\n**Expires:** ${moment(testDate).calendar()}\n\n`;
inqAdded++;
}
});
if (inqAdded <= 0) desc = '';
if (desc.length >= 1900) {
embed.setDescription('__***Too many Hard Inquiries to show, please see https://report.libraryofcode.org***__');
} else {
@ -183,18 +160,7 @@ export default class Score extends Command {
name = `Library of Code sp-us | ${role.name}`;
break;
}
await this.client.db.Score.updateOne({ userID: user.id }, { $addToSet: { softInquiries: { name, date: new Date() } } });
const embed2 = new RichEmbed();
embed2.setTitle('Inquiry Notification');
embed2.setColor('#00FFFF');
const mem = this.client.util.resolveMember(score.userID, this.client.guilds.get(this.client.config.guildID));
embed2.addField('Member', `${mem.user.username}#${mem.user.discriminator} | <@${score.userID}>`, true);
embed2.addField('Type', 'SOFT', true);
embed2.addField('Department/Service', name.toUpperCase(), true);
embed2.setTimestamp();
embed2.setFooter(this.client.user.username, this.client.user.avatarURL);
const log = <TextChannel>this.client.guilds.get(this.client.config.guildID).channels.get('611584771356622849');
log.createMessage({ embed: embed2 }).catch(() => { });
await this.client.report.createInquiry(user.id, name, 1);
}
}
if (args[1] === 'hard' && this.checkCustomPermissions(this.client.util.resolveMember(message.author.id, this.mainGuild), 6)) {

View File

@ -1,3 +1,4 @@
/* eslint-disable no-await-in-loop */
/* eslint-disable no-continue */
/* eslint-disable default-case */
import moment from 'moment';
@ -6,6 +7,7 @@ import { createPaginationEmbed } from 'eris-pagination';
import { Message, User, TextChannel } from 'eris';
import { Client, Command, RichEmbed } from '../class';
import { getTotalMessageCount } from '../intervals/score';
import { InquiryInterface } from '../models';
export default class Score_Hist extends Command {
constructor(client: Client) {
@ -78,8 +80,15 @@ export default class Score_Hist extends Command {
let data = '';
let hardInquiries = 0;
if (hist.report?.inquiries?.length > 0) {
hist.report.inquiries.forEach((inq) => {
const inquiries: InquiryInterface[] = [];
if (hist.inquiries?.length > 0) {
for (const h of hist.inquiries) {
const inq = await this.client.db.Inquiry.findOne({ _id: h });
inquiries.push(inq);
}
}
if (inquiries?.length > 0) {
inquiries.forEach((inq) => {
const testDate = (new Date(new Date(inq.date).setHours(1460)));
// eslint-disable-next-line no-plusplus
if (testDate > new Date()) hardInquiries++;
@ -156,17 +165,7 @@ export default class Score_Hist extends Command {
return cmdPages.push(embed);
});
const name = `${user.username} VIA DISCORD - [HISTORICAL]`;
await this.client.db.Score.updateOne({ userID: user.id }, { $addToSet: { softInquiries: { name: name.toUpperCase(), date: new Date() } } });
const embed2 = new RichEmbed();
embed2.setTitle('Inquiry Notification');
embed2.setColor('#00FFFF');
embed2.addField('Member', `${user.username}#${user.discriminator} | <@${user.id}>`, true);
embed2.addField('Type', 'SOFT', true);
embed2.addField('Department/Service', name.toUpperCase(), true);
embed2.setTimestamp();
embed2.setFooter(this.client.user.username, this.client.user.avatarURL);
const log = <TextChannel> this.client.guilds.get(this.client.config.guildID).channels.get('611584771356622849');
log.createMessage({ embed: embed2 }).catch(() => {});
await this.client.report.createInquiry(user.id, name, 1);
if (cmdPages.length === 1) return message.channel.createMessage({ embed: cmdPages[0] });
@ -231,8 +230,15 @@ export default class Score_Hist extends Command {
let data = '';
let hardInquiries = 0;
if (hist.report?.inquiries?.length > 0) {
hist.report.inquiries.forEach((inq) => {
const inquiries: InquiryInterface[] = [];
if (hist.inquiries?.length > 0) {
for (const h of hist.inquiries) {
const inq = await this.client.db.Inquiry.findOne({ _id: h });
inquiries.push(inq);
}
}
if (inquiries?.length > 0) {
inquiries.forEach((inq) => {
const testDate = (new Date(new Date(inq.date).setHours(1460)));
// eslint-disable-next-line no-plusplus
if (testDate > new Date()) hardInquiries++;
@ -314,18 +320,7 @@ export default class Score_Hist extends Command {
name = `Library of Code sp-us | ${role.name} - [HISTORICAL]`;
break;
}
await this.client.db.Score.updateOne({ userID: user.id }, { $addToSet: { softInquiries: { name, date: new Date() } } });
const embed2 = new RichEmbed();
embed2.setTitle('Inquiry Notification');
embed2.setColor('#00FFFF');
const mem = this.client.util.resolveMember(user.id, this.client.guilds.get(this.client.config.guildID));
embed2.addField('Member', `${mem.user.username}#${mem.user.discriminator} | <@${user.id}>`, true);
embed2.addField('Type', 'SOFT', true);
embed2.addField('Department/Service', name.toUpperCase(), true);
embed2.setTimestamp();
embed2.setFooter(this.client.user.username, this.client.user.avatarURL);
const log = <TextChannel> this.client.guilds.get(this.client.config.guildID).channels.get('611584771356622849');
log.createMessage({ embed: embed2 }).catch(() => {});
await this.client.report.createInquiry(user.id, name, 1);
if (cmdPages.length === 1) return message.channel.createMessage({ embed: cmdPages[0] });

81
src/commands/sip.ts Normal file
View File

@ -0,0 +1,81 @@
/* eslint-disable consistent-return */
import { Message } from 'eris';
import type { Channel } from 'ari-client';
import { Client, Command } from '../class';
import { Misc } from '../pbx';
export default class SIP extends Command {
constructor(client: Client) {
super(client);
this.name = 'sip';
this.description = 'Dials a SIP URI.';
this.usage = `${this.client.config.prefix}sip <uri>`;
this.permissions = 1;
this.guildOnly = true;
this.enabled = true;
}
public async run(message: Message, args: string[]) {
try {
const staff = await this.client.db.Staff.findOne({ userID: message.author.id });
if (!staff || !staff?.extension) return this.error(message.channel, 'You must have an extension to complete this action.');
this.success(message.channel, 'Queued call.');
const bridge = await this.client.pbx.ari.Bridge().create();
let receiver: Channel = this.client.pbx.ari.Channel();
const sender = await this.client.pbx.ari.channels.originate({
endpoint: `PJSIP/${staff.extension}`,
extension: staff.extension,
callerId: 'LOC PBX AUTO OPERATOR <operator>',
context: 'from-internal',
priority: 1,
app: 'cr-zero',
});
sender.once('StasisStart', async () => {
await Misc.play(this.client.pbx, sender, 'sound:pls-hold-while-try');
await sender.ring();
try {
receiver = await receiver.originate({
endpoint: `SIP/${args.join(' ')}`,
callerId: 'LOC PBX AUTO OPERATOR <operator>',
context: 'from-internal',
priority: 1,
app: 'cr-zero',
});
} catch {
await Misc.play(this.client.pbx, sender, 'sound:discon-or-out-of-service');
await sender.hangup().catch(() => {});
return false;
}
// receiver.once('StasisStart', )
});
receiver.once('StasisStart', async () => {
await sender.ringStop();
await bridge.addChannel({ channel: [receiver.id, sender.id] });
await bridge.play({ media: 'sound:beep' });
});
receiver.once('ChannelDestroyed', async () => {
if (!sender.connected) return;
await Misc.play(this.client.pbx, sender, ['sound:the-number-u-dialed', 'sound:T-is-not-available', 'sound:please-try-again-later']);
await sender.hangup().catch(() => {});
await bridge.destroy().catch(() => {});
});
receiver.once('StasisEnd', async () => {
await sender.hangup().catch(() => {});
await bridge.destroy().catch(() => {});
});
sender.once('StasisEnd', async () => {
await receiver.hangup().catch(() => {});
await bridge.destroy().catch(() => {});
});
} catch (err) {
return this.client.util.handleError(err, message, this);
}
}
}

View File

@ -62,7 +62,7 @@ export default class SiteInfo extends Command {
this.aliases = ['ssl', 'cert', 'certinfo', 'ci', 'tls', 'si', 'siteinfo'];
this.permissions = 0;
this.guildOnly = true;
this.enabled = true;
this.enabled = false;
}
public async run(message: Message, args: string[]) {
@ -157,6 +157,7 @@ export default class SiteInfo extends Command {
const page = await browser.newPage();
await page.goto(`https://${args[0]}`);
screenshotData = await page.screenshot();
browser.close();
} catch (e) {
this.client.util.signale.error(e);
}

View File

@ -1,8 +1,7 @@
/* eslint-disable no-bitwise */
import moment from 'moment';
import { Message, Member, TextChannel } from 'eris';
import { Message, Member } from 'eris';
import { Client, Command, RichEmbed } from '../class';
// import acknowledgements from '../configs/acknowledgements.json';
import { whois as emotes } from '../configs/emotes.json';
export default class Whois extends Command {
@ -34,7 +33,6 @@ export default class Whois extends Command {
}
const embed = new RichEmbed();
embed.setThumbnail(member.avatarURL);
// const ackResolve = this.resolveStaffInformation(member.id);
const ackResolve = await this.client.db.Staff.findOne({ userID: member.id }).lean().exec();
let title = `${member.user.username}#${member.user.discriminator}`;
if (ackResolve?.pn?.length > 0) title += `, ${ackResolve.pn.join(', ')}`;
@ -53,14 +51,12 @@ export default class Whois extends Command {
}
const pager = await this.client.db.PagerNumber.findOne({ individualAssignID: member.user.id }).lean().exec();
if (pager?.num) {
description += `📡 ${pager.num}\n`;
description += `📟 ${pager.num}\n`;
}
if (ackResolve?.extension) {
description += `☎️ ${ackResolve.extension}\n`;
}
const memberProfile = await this.client.db.Member.findOne({ userID: message.author.id });
if (memberProfile?.additional?.gitlab) {
description += `${emotes.gitlab} ${memberProfile?.additional.gitlab}\n`;
}
@ -80,24 +76,11 @@ export default class Whois extends Command {
}
}
embed.addField('Status', member.status === 'dnd' ? 'Do Not Disturb' : this.capsFirstLetter(member.status) || 'Offline', true);
// const platform = member.bot && member.status !== 'offline' ? 'API/WebSocket' : Object.entries(message.member.clientStatus).filter((a) => a[1] !== 'offline').map((a) => this.capsFirstLetter(a[0])).join(', ');
// if (platform) embed.addField('Platform', platform, true);
embed.addField('Joined At', `${moment(new Date(member.joinedAt)).format('dddd, MMMM Do YYYY, h:mm:ss A')} ET`, true);
embed.addField('Created At', `${moment(new Date(member.user.createdAt)).format('dddd, MMMM Do YYYY, h:mm:ss A')} ET`, true);
const score = await this.client.db.Score.findOne({ userID: member.id }).lean().exec();
if (score) {
await this.client.db.Score.updateOne({ userID: member.id }, { $addToSet: { softInquiries: { name: 'Library of Code sp-us | Bureau of Community Reports', date: new Date() } } });
const embed2 = new RichEmbed();
embed2.setTitle('Inquiry Notification');
embed2.setColor('#00FFFF');
const mem = this.client.util.resolveMember(score.userID, this.client.guilds.get(this.client.config.guildID));
embed2.addField('Member', `${mem.user.username}#${mem.user.discriminator} | <@${score.userID}>`, true);
embed2.addField('Type', 'SOFT', true);
embed2.addField('Department/Service', 'Library of Code sp-us | Bureau of Community Reports'.toUpperCase(), true);
embed2.setTimestamp();
embed2.setFooter(this.client.user.username, this.client.user.avatarURL);
const log = <TextChannel>this.client.guilds.get(this.client.config.guildID).channels.get('611584771356622849');
log.createMessage({ embed: embed2 }).catch(() => { });
await this.client.report.createInquiry(member.id, 'Library of Code sp-us | Bureau of Community Reports', 1);
let totalScore = '0';
if (score.total < 200) totalScore = '---';
else if (score.total > 800) totalScore = '800';
@ -231,8 +214,8 @@ export default class Whois extends Command {
if (serverAcknowledgements.length > 0) {
embed.addField('Acknowledgements', serverAcknowledgements[0]);
}
if (ackResolve?.acknowledgements) {
embed.addField('Bot Acknowledgements', ackResolve.acknowledgements.join(', '));
if (ackResolve?.additionalRoles?.length > 0) {
embed.addField('Additional Acknowledgements', ackResolve.additionalRoles.join(', '));
}
embed.setFooter(this.client.user.username, this.client.user.avatarURL);
embed.setTimestamp();

View File

@ -12,8 +12,8 @@ export default class GuildMemberAdd extends Event {
public async run(_, member: Member) {
try {
const search = await this.client.db.local.muted.get<boolean>(`muted-${member.user.id}`);
if (search) {
await member.addRole('478373942638149643');
if (search === true) {
member.addRole('478373942638149643', 'muted user left server and joined back');
}
} catch (err) {
this.client.util.handleError(err);

View File

@ -1,6 +1,5 @@
export { default as CallBackHandler } from './CallBackHandler';
export { default as CommandHandler } from './CommandHandler';
export { default as guildMemberAdd } from './guildMemberAdd';
export { default as messageReactionAdd } from './messageReactionAdd';
export { default as ready } from './ready';
export { default as Training } from './Training';

View File

@ -1,57 +0,0 @@
import { Emoji, GuildTextableChannel, Member, Message } from 'eris';
import { Client, Event } from '../class';
export default class MessageReactionAdd extends Event {
public client: Client;
constructor(client: Client) {
super(client);
this.event = 'messageReactionAdd';
}
public async run(message: Message<GuildTextableChannel>, _emoji: Emoji, reactor: Member) {
if (message.channel.id !== '807444198969835550') return;
if (message.author.id !== this.client.user.id) return;
if (!reactor.roles[0]) {
reactor = await message.channel.guild.getRESTMember(reactor.id);
}
if (!reactor.roles.includes('662163685439045632')) return;
if ((await this.client.db.Proclamation.exists({ msg: message.id, processed: false }))) {
const yea = await message.getReaction(this.client.util.emojis.SUCCESS);
const nay = await message.getReaction(this.client.util.emojis.ERROR);
const present = await message.getReaction('🙋');
const totalDirectors = 6;
const proclamation = await this.client.db.Proclamation.findOne({ msg: message.id });
const processed = totalDirectors === (yea.length + nay.length + present.length) || Date.now() - proclamation.at > 604800000;
const absent = totalDirectors - (yea.length + nay.length + present.length);
await this.client.db.Proclamation.updateOne({ msg: message.id }, {
results: {
yea: yea.length,
nay: nay.length,
present: present.length,
absent,
},
processed,
});
const inTheMajority = yea.length > nay.length + present.length;
if (processed) {
const author = this.client.users.get(proclamation.issuer) || await this.client.getRESTUser(proclamation.issuer);
if (inTheMajority) {
await author.createMessage(`__**Proclamation Majority Vote Received**__\nThe Proclamation you created at Library of Code sp-us, titled **${proclamation.subject}** (\`${proclamation.oID}\`) received the majority vote.`);
await message.channel.createMessage(`__**Proclamation Results**__\nProclamation issued by ${author.mention} **received** the majority vote. Proclamation ID: ${proclamation.oID}\n\n__Results:__\n**Yea:** ${yea.length}\n**Nay:** ${nay.length}\n**Present:** ${present.length}\n**Absent:** ${absent}`);
} else {
await author.createMessage(`__**Proclamation Majority Vote Lost**__\nThe Proclamation you created at Library of Code sp-us, titled **${proclamation.subject}** (\`${proclamation.oID}\`) lost the majority vote.`);
await message.channel.createMessage(`__**Proclamation Results**__\nProclamation issued by ${author.mention} **lost** the majority vote. Proclamation ID: ${proclamation.oID}\n\n__Results:__\n**Yea:** ${yea.length}\n**Nay:** ${nay.length}\n**Present:** ${present.length}\n**Absent:** ${absent}`);
}
}
await reactor.user.createMessage(`__**Vote Recorded**\nYour vote on the proclamation with ID \`${proclamation.id}\` at Library of Code sp-us was successfully recorded.`);
}
}
}

View File

@ -11,7 +11,7 @@ export default function checkLock(client: Client): NodeJS.Timeout {
if (moderation.expiration.processed) return;
if (new Date() > moderation.expiration.date) {
await moderation.updateOne({ 'expiration.processed': true });
// const moderator = client.guilds.get(client.config.guildID).members.get(moderation.moderatorID);
const system = client.guilds.get(client.config.guildID).members.get(client.user.id);
switch (moderation.type) {
case 5:

View File

@ -63,8 +63,8 @@ export default async function calculateScore(client: Client): Promise<NodeJS.Tim
staff: boolean,
locked: boolean,
notify: boolean,
inquiries: [{ name: string, reason: string, date: Date }?],
softInquiries: [{name: string, date: Date }?]
// inquiries: [{ name: string, reason: string, date: Date }?],
// softInquiries: [{name: string, date: Date }?]
lastUpdated: Date,
pin: number[],
/* generalMessagesRatio: number,
@ -82,8 +82,8 @@ export default async function calculateScore(client: Client): Promise<NodeJS.Tim
staff: false,
locked: false,
notify: false,
inquiries: [],
softInquiries: [],
// inquiries: [],
// softInquiries: [],
lastUpdated: new Date(),
pin: [client.util.randomNumber(100, 999), client.util.randomNumber(10, 99), client.util.randomNumber(1000, 9999)],
};

View File

@ -4,6 +4,7 @@
import sdNotify from 'sd-notify';
import { parse } from 'yaml';
import { promises as fs } from 'fs';
import { join } from 'path';
import { Client } from './class';
import * as eventFiles from './events';
import * as commandFiles from './commands';
@ -14,7 +15,7 @@ async function main(): Promise<void> {
sdNotify.ready();
sdNotify.startWatchdogMode(2500);
}
const read = await fs.readFile('../config.yaml', 'utf8');
const read = await fs.readFile(join(__dirname, '../config.yaml'), 'utf8');
const config: Config = parse(read);
const client = new Client(config.token, { defaultImageFormat: 'png', restMode: true, intents: ['guildBans', 'guildEmojis', 'guildInvites', 'guildMembers', 'guildMessageReactions', 'guildMessages', 'guildPresences', 'guildWebhooks', 'guilds', 'directMessages'] });
client.config = config;

View File

@ -1,21 +0,0 @@
import { Document, model, Schema } from 'mongoose';
export interface ExecutiveOrderInterface extends Document {
issuer: string;
subject: string;
body: string;
at: Date;
oID: string;
msg: string;
}
const ExecutiveOrder = new Schema({
issuer: { type: String, required: true },
subject: { type: String, required: true },
body: { type: String, required: true },
at: { type: Date, required: true },
oID: { type: String, required: true, unique: true },
msg: { type: String, required: true, unique: true },
});
export default model<ExecutiveOrderInterface>('ExecutiveOrders', ExecutiveOrder);

33
src/models/Inquiry.ts Normal file
View File

@ -0,0 +1,33 @@
import { Document, Schema, model } from 'mongoose';
import { ScoreInterfaceRaw } from '.';
export enum InqType {
HARD,
SOFT,
}
export interface InquiryInterface extends Document {
iid?: string,
userID: string,
/**
* - 0: Hard
* - 1: Soft
*/
type: InqType,
name: string,
reason?: string,
date: Date,
report?: ScoreInterfaceRaw,
}
const Inquiry: Schema = new Schema({
iid: String,
userID: String,
type: Number,
name: String,
reason: String,
date: String,
report: Object,
});
export default model<InquiryInterface>('Inquiry', Inquiry);

View File

@ -1,14 +1,15 @@
import { Document, Schema, model } from 'mongoose';
export interface MemberInterface extends Document {
userID: string
userID: string,
additional: {
langs: ['js', 'py', 'rb', 'ts', 'rs', 'go', 'cfam', 'csharp', 'swift', 'java', 'kt', 'asm'],
operatingSystems: ['arch', 'deb', 'cent', 'fedora', 'manjaro', 'mdarwin', 'redhat', 'ubuntu', 'win'],
github: string;
gitlab: string;
bio: string;
github: string,
gitlab: string,
bio: string,
},
bio: string,
}
const Member: Schema = new Schema({
@ -20,6 +21,7 @@ const Member: Schema = new Schema({
gitlab: String,
bio: String,
},
bio: String,
});
export default model<MemberInterface>('Member', Member);

View File

@ -1,35 +0,0 @@
import { Document, model, Schema } from 'mongoose';
export interface MotionInterface extends Document {
issuer: string;
subject: string;
body: string;
at: Date;
oID: string;
results: {
yea: number;
nay: number;
present: number;
absent: number;
};
processed: boolean;
msg: string;
}
const Motion = new Schema({
issuer: { type: String, required: true },
subject: { type: String, required: true },
body: { type: String, required: true },
at: { type: Date, required: true },
oID: { type: String, required: true, unique: true },
results: {
yea: Number,
nay: Number,
present: Number,
absent: Number,
},
processed: Boolean,
msg: { required: true, unique: true, type: String },
});
export default model<MotionInterface>('Motions', Motion);

View File

@ -1,37 +0,0 @@
import { Document, model, Schema } from 'mongoose';
export interface ProclamationInterface extends Document {
issuer: string;
subject: string;
body: string;
at: number;
oID: string;
results: {
yea: number;
nay: number;
present: number;
absent: number;
};
acceptedAt: number;
msg: string;
processed: boolean;
}
const Proclamation = new Schema({
issuer: { type: String, required: true },
subject: { type: String, required: true },
body: { type: String, required: true },
at: { type: Number, required: true },
oID: { type: String, required: true, unique: true },
results: {
yea: Number,
nay: Number,
present: Number,
absent: Number,
},
acceptedAt: Number,
msg: { type: String, required: true, unique: true },
processed: Boolean,
});
export default model<ProclamationInterface>('Proclamations', Proclamation);

View File

@ -1,33 +0,0 @@
import { Document, model, Schema } from 'mongoose';
export interface ResolutionInterface extends Document {
issuer: string;
subject: string;
body: string;
at: number;
oID: string;
results: {
yea: number;
nay: number;
present: number;
absent: number;
};
msg: string;
}
const Resolution = new Schema({
issuer: { type: String, required: true },
subject: { type: String, required: true },
body: { type: String, required: true },
at: { type: Number, required: true },
oID: { type: String, required: true, unique: true },
results: {
yea: Number,
Nay: Number,
present: Number,
absent: Number,
},
msg: { type: String, required: true, unique: true },
});
export default model<ResolutionInterface>('Resolutions', Resolution);

View File

@ -49,30 +49,11 @@ export interface ScoreInterfaceRaw {
export interface ScoreInterface extends Document {
userID: string
/**
* total will be between 800-200 - 0 signfies "No Score", too little information is available or other variable are too low
* - CALCULATION: `(COMBINED SUBSCORES x 5) * 5.13; Math.floor()`
*/
total: number,
/**
* 10 - 55
*/
activity: number,
/**
* 0 - 54
*/
roles: number,
/**
* -50 - 2
* all users start out with 2 moderation points, the number of points decreases for each moderation.
*/
moderation: number,
/**
* -20 - 50
* processed by CSD
*/
cloudServices: number,
// 0 or 20, 20 points are added if the user is a staff member
staff: number,
other: number,
notify: boolean,

View File

@ -1,21 +1,24 @@
import { Document, Schema, model } from 'mongoose';
import { Document, Schema, model, Types } from 'mongoose';
import { ScoreInterfaceRaw } from '.';
export interface ScoreHistoricalRaw {
userID: string,
report: ScoreInterfaceRaw,
inquiries: [ Types.ObjectId ],
date: Date,
}
export interface ScoreHistoricalInterface extends Document {
userID: string,
report: ScoreInterfaceRaw,
inquiries: [ Types.ObjectId ],
date: Date
}
const ScoreHistorical: Schema = new Schema({
userID: String,
report: Object,
inquiries: Array,
date: Date,
});

View File

@ -9,6 +9,7 @@ export interface StaffInterface extends Document {
emailAddress: string,
extension: string,
acknowledgements: string[],
additionalRoles: string[]
}
const Staff: Schema = new Schema({
@ -20,6 +21,7 @@ const Staff: Schema = new Schema({
emailAddress: String,
extension: String,
acknowledgements: Array,
additionalRoles: Array,
});
export default model<StaffInterface>('Staff', Staff);

View File

@ -1,19 +1,16 @@
export { default as Customer, CustomerInterface } from './Customer';
export { default as CustomerPortal, CustomerPortalInterface } from './CustomerPortal';
export { default as ExecutiveOrder, ExecutiveOrderInterface } from './ExecutiveOrder';
export { default as File, FileInterface } from './File';
export { default as Inquiry, InquiryInterface, InqType } from './Inquiry';
export { default as Member, MemberInterface } from './Member';
export { default as Merchant, MerchantInterface } from './Merchant';
export { default as Moderation, ModerationInterface } from './Moderation';
export { default as Motion, MotionInterface } from './Motion';
export { default as NNTrainingData, NNTrainingDataInterface } from './NNTrainingData';
export { default as Note, NoteInterface } from './Note';
export { default as PagerNumber, PagerNumberInterface, PagerNumberRaw } from './PagerNumber';
export { default as Proclamation, ProclamationInterface } from './Proclamation'
export { default as Promo, PromoInterface } from './Promo';
export { default as Rank, RankInterface } from './Rank';
export { default as Redirect, RedirectInterface, RedirectRaw } from './Redirect';
export { default as Resolution, ResolutionInterface } from './Resolution';
export { default as Score, ScoreInterface, ScoreInterfaceRaw } from './Score';
export { default as ScoreHistorical, ScoreHistoricalInterface } from './ScoreHistorical';
export { default as Staff, StaffInterface } from './Staff';

View File

@ -4,11 +4,17 @@ import { promises as fs } from 'fs';
import { PBX } from '../../class';
export default class Misc {
public static async accessDenied(channel: ARI.Channel) {
const playback = await channel.play({
public static accessDenied(channel: ARI.Channel) {
return new Promise<void>((resolve, reject) => {
channel.play({
media: 'sound:access-denied',
}, undefined);
playback.once('PlaybackFinished', () => channel.hangup());
}, undefined).then((playback) => {
playback.on('PlaybackFinished', () => {
channel.hangup().catch((err) => reject(err));
resolve();
});
}).catch((err) => reject(err));
});
}
public static async TTS(pbx: PBX, text: string, gender: string | any): Promise<string> {
@ -24,7 +30,7 @@ export default class Misc {
return `sound:/tmp/${fileExtension}`;
}
public static play(pbx: PBX, channel: ARI.Channel, sound: string): Promise<ARI.Playback> {
public static play(pbx: PBX, channel: ARI.Channel, sound: string | string[]): Promise<ARI.Playback> {
const playback = pbx.ari.Playback();
return new Promise((resolve, reject) => {

View File

@ -18,7 +18,7 @@
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
"removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
"importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */

1839
yarn.lock

File diff suppressed because it is too large Load Diff