diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 14e178b..9364d83 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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. diff --git a/package.json b/package.json index 26da961..fec2220 100644 --- a/package.json +++ b/package.json @@ -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 ", + "author": "Matthew R, AD, FSEN ", "license": "AGPL-3.0", "private": false, "devDependencies": { @@ -29,6 +29,7 @@ "eslint": "^7.0.0", "eslint-config-airbnb-base": "^14.1.0", "eslint-plugin-import": "^2.20.2", + "tslib": "^2.1.0", "typescript": "^3.9.2" }, "dependencies": { @@ -55,6 +56,7 @@ "puppeteer": "^5.5.0", "sd-notify": "^2.8.0", "signale": "^1.4.0", + "stock-info": "^1.2.0", "stripe": "^8.120.0", "uuid": "^8.0.0", "yaml": "^1.9.2" diff --git a/src/api/comm.libraryofcode.org/routes/report.ts b/src/api/comm.libraryofcode.org/routes/report.ts index 706bc7b..60e3197 100644 --- a/src/api/comm.libraryofcode.org/routes/report.ts +++ b/src/api/comm.libraryofcode.org/routes/report.ts @@ -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'; @@ -129,11 +128,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 }); } } @@ -174,35 +174,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 = 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, @@ -238,35 +217,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 = 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, }, }); @@ -288,36 +256,38 @@ export default class Report extends Route { if ((mem.user.publicFlags & (1 << 17)) === 1 << 17) flags.push('EARLY_VERIFIED_BOT_DEVELOPER'); } const set = []; - 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); + 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; @@ -359,7 +329,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, @@ -370,7 +340,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, @@ -405,17 +375,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 = 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({ @@ -489,42 +449,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 = 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 = 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 = 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(); @@ -560,7 +490,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(); @@ -595,6 +525,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, @@ -610,8 +551,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 : [], + softInquiries: softInquiries?.length > 0 ? softInquiries : [], historical: historical ?? [], lastUpdated: score.lastUpdate, }); diff --git a/src/class/Client.ts b/src/class/Client.ts index efa6b4c..51e0c9d 100644 --- a/src/class/Client.ts +++ b/src/class/Client.ts @@ -8,6 +8,7 @@ import { Customer, CustomerInterface, CustomerPortal, CustomerPortalInterface, File, FileInterface, + Inquiry, InquiryInterface, Member, MemberInterface, Merchant, MerchantInterface, Moderation, ModerationInterface, @@ -43,7 +44,7 @@ export default class Client extends eris.Client { public stripe: Stripe; - public db: { Customer: mongoose.Model, CustomerPortal: mongoose.Model, File: mongoose.Model, Member: mongoose.Model, Merchant: mongoose.Model, Moderation: mongoose.Model, NNTrainingData: mongoose.Model, Note: mongoose.Model, PagerNumber: mongoose.Model, Promo: mongoose.Model, Rank: mongoose.Model, Redirect: mongoose.Model, Score: mongoose.Model, ScoreHistorical: mongoose.Model, Staff: mongoose.Model, Stat: mongoose.Model, local: { muted: LocalStorage } }; + public db: { Customer: mongoose.Model, CustomerPortal: mongoose.Model, File: mongoose.Model, Inquiry: mongoose.Model, Member: mongoose.Model, Merchant: mongoose.Model, Moderation: mongoose.Model, NNTrainingData: mongoose.Model, Note: mongoose.Model, PagerNumber: mongoose.Model, Promo: mongoose.Model, Rank: mongoose.Model, Redirect: mongoose.Model, Score: mongoose.Model, ScoreHistorical: mongoose.Model, Staff: mongoose.Model, Stat: mongoose.Model, local: { muted: LocalStorage } }; constructor(token: string, options?: eris.ClientOptions) { super(token, options); @@ -51,7 +52,15 @@ export default class Client extends eris.Client { this.events = new Collection(); this.intervals = new Collection(); this.queue = new Queue(this); - this.db = { Customer, CustomerPortal, File, Member, Merchant, Moderation, NNTrainingData, Note, PagerNumber, Promo, Rank, Redirect, 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; } diff --git a/src/class/Queue.ts b/src/class/Queue.ts index 261244c..df5fe5f 100644 --- a/src/class/Queue.ts +++ b/src/class/Queue.ts @@ -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 = 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) { diff --git a/src/class/Report.ts b/src/class/Report.ts index e14a7b7..6c4a987 100644 --- a/src/class/Report.ts +++ b/src/class/Report.ts @@ -1,6 +1,6 @@ -import { EventEmitter } from 'events'; -import { Client, RichEmbed } from '.'; -import { ScoreHistoricalRaw } from '../models/ScoreHistorical'; +import { v4 as uuid } from 'uuid'; +import { Client } from '.'; +import { InqType } from '../models'; export default class Report { public client: Client; @@ -9,7 +9,27 @@ export default class Report { this.client = client; } - public async soft(userID: string) { + 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; @@ -73,5 +93,5 @@ export default class Report { array.push(data); } - } + } */ } diff --git a/src/class/Util.ts b/src/class/Util.ts index 25c0d3f..6a06077 100644 --- a/src/class/Util.ts +++ b/src/class/Util.ts @@ -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 @@ -71,7 +114,7 @@ export default class Util { } return `${(bytes / 1024 ** i).toFixed(2)} ${sizes[i]}`; } - + public async exec(command: string, _options: childProcess.ExecOptions = {}): Promise { const ex = promisify(childProcess.exec); diff --git a/src/class/index.ts b/src/class/index.ts index 1cada86..de4df29 100644 --- a/src/class/index.ts +++ b/src/class/index.ts @@ -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'; diff --git a/src/commands/callback.ts b/src/commands/callback.ts index 4da387b..35f084d 100644 --- a/src/commands/callback.ts +++ b/src/commands/callback.ts @@ -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 '; 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 = 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 { diff --git a/src/commands/delmerchant.ts b/src/commands/delmerchant.ts index 4ed38dd..8c9ef2d 100644 --- a/src/commands/delmerchant.ts +++ b/src/commands/delmerchant.ts @@ -1,5 +1,4 @@ import { Message } from 'eris'; -import { randomBytes } from 'crypto'; import { Client, Command } from '../class'; export default class DelMerchant extends Command { diff --git a/src/commands/index.ts b/src/commands/index.ts index 8916297..603d187 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -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'; diff --git a/src/commands/kick.ts b/src/commands/kick.ts index 0a32377..5c1e8f0 100644 --- a/src/commands/kick.ts +++ b/src/commands/kick.ts @@ -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 { diff --git a/src/commands/market.ts b/src/commands/market.ts new file mode 100644 index 0000000..132edd2 --- /dev/null +++ b/src/commands/market.ts @@ -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 `; + 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); + } + } +} diff --git a/src/commands/pulldata.ts b/src/commands/pulldata.ts index d1d2c0a..a0abc3f 100644 --- a/src/commands/pulldata.ts +++ b/src/commands/pulldata.ts @@ -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 = 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}`); diff --git a/src/commands/score.ts b/src/commands/score.ts index 0449f2d..2587906 100644 --- a/src/commands/score.ts +++ b/src/commands/score.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-plusplus */ /* eslint-disable no-continue */ /* eslint-disable default-case */ import moment from 'moment'; @@ -13,8 +14,9 @@ export default class Score extends Command { constructor(client: Client) { super(client); this.name = 'score'; - this.description = 'Retreives your Community Report'; + this.description = 'Retrieves your Community Report'; this.usage = `${this.client.config.prefix}score\n${this.client.config.prefix}score :`; + this.aliases = ['report']; this.subcmds = [Score_Hist, Score_Notify, Score_Pref]; this.permissions = 0; this.guildOnly = false; @@ -28,18 +30,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 = 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 +48,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 = 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 +107,16 @@ 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:** ${inq.date.toLocaleString('en-us')} ET\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 +159,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 = 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)) { diff --git a/src/commands/score_hist.ts b/src/commands/score_hist.ts index 10da4f5..f14afe8 100644 --- a/src/commands/score_hist.ts +++ b/src/commands/score_hist.ts @@ -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 = 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 = 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] }); diff --git a/src/commands/site.ts b/src/commands/site.ts index 774ee9f..4e7384f 100644 --- a/src/commands/site.ts +++ b/src/commands/site.ts @@ -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); } diff --git a/src/commands/whois.ts b/src/commands/whois.ts index f70c2ed..658539c 100644 --- a/src/commands/whois.ts +++ b/src/commands/whois.ts @@ -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,7 +51,7 @@ 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`; @@ -77,24 +75,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 = 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'; @@ -228,7 +213,7 @@ export default class Whois extends Command { if (serverAcknowledgements.length > 0) { embed.addField('Acknowledgements', serverAcknowledgements[0]); } - if (ackResolve?.acknowledgements) { + if (ackResolve?.acknowledgements?.length > 0) { embed.addField('Bot Acknowledgements', ackResolve.acknowledgements.join(', ')); } embed.setFooter(this.client.user.username, this.client.user.avatarURL); diff --git a/src/configs/acknowledgements.json b/src/configs/acknowledgements.json deleted file mode 100644 index 93d9740..0000000 --- a/src/configs/acknowledgements.json +++ /dev/null @@ -1,149 +0,0 @@ -[ - { - "name": "Matthew", - "id": "278620217221971968", - "title": "Chief Director of Engineering", - "pn": ["AD", "FSEN", "FSO", "FSCR", "FSCE", "FSED"], - "dept": "Board of Directors", - "emailAddress": "matthew@staff.libraryofcode.org", - "gitlab": "https://gitlab.libraryofcode.org/matthew", - "github": "https://github.com/matthew119427", - "bio": "so baby come light me up, and maybe ill let you on it. a little bit dangerous, but baby thats how i want it. a little less conversation and a little more touch my body. cuz im so into you... ~ Ariana Grande, Into You - Dangerous Woman", - "acknowledgements": ["Maintainer & Lead Developer"] - }, - { - "name": "Bsian", - "id": "253600545972027394", - "title": "Director of Engineering", - "pn": ["AD", "FSEN", "FSCR", "FSED"], - "dept": "Board of Directors", - "emailAddress": "bsian@staff.libraryofcode.org", - "bio": "I also like trains", - "acknowledgements": ["Maintainer & Assistant Lead Developer"] - }, - { - "name": "NightRaven", - "id": "239261547959025665", - "title": "Director of Information Security", - "pn": ["AD", "FSEN", "FSO", "FSCR", "FSED"], - "dept": "Board of Directors", - "emailAddress": "nightraven@staff.libraryofcode.org", - "bio": "I like trains" - }, - { - "name": "Unknown", - "id": "143414786913206272", - "title": "Director of Operations", - "pn": ["AD", "FSO", "FSM", "FSCR"], - "dept": "Board of Directors", - "emailAddress": "unknown@staff.libraryofcode.org", - "gitlab": "https://gitlab.libraryofcode.org/unknown", - "bio": "im not a proffesional developer or anything, i just enjoy it as a hobby." - }, - { - "name": "TheSkele27", - "id": "213632190557192192", - "title": "Director of Community Engagement", - "pn": ["AD", "FSCE"], - "dept": "Board of Directors", - "emailAddress": "theskele27@staff.libraryofcode.org", - "gitlab": "https://gitlab.libraryofcode.org/TheSkele27", - "github": "https://github.com/TheSkele27", - "bio": "Is water wet?" - }, - { - "name": "Catbirby", - "id": "131953641371205632", - "pn": ["SP", "FSO", "FSM"], - "dept": "Supervisor", - "emailAddress": "catbirby@staff.libraryofcode.org", - "github": "https://github.com/catbirby", - "bio": "Computer Tech/Networking Nerd/SysADin/Graphic Designer/Audiophile. I don't do much coding but know my way around most languages." - }, - { - "name": "D3XTER", - "id": "468009964263178262", - "pn": ["ST", "CE-I"], - "dept": "Core Team", - "emailAddress": "dexter@staff.libraryofcode.org", - "gitlab": "https://gitlab.libraryofcode.org/D3XTER", - "bio": "Hi I'm D3XTER how are ya?" - }, - { - "name": "DedShotTM", - "id": "402154763363418142", - "pn": ["ST", "FSCR"], - "dept": "Technician & Moderator", - "emailAddress": "dedshot@staff.libraryofcode.org", - "gitlab": "https://gitlab.libraryofcode.org/DedShotTM", - "github": "https://github.com/DedShotTM", - "bio": "Imagine having a bio", - "acknowledgements": ["Contributor"] - }, - { - "name": "EdgyBoi2414", - "id": "397432516010835970", - "pn": ["ST"], - "dept": "Core Team", - "emailAddress": "edgyboi2414@gmail.com", - "gitlab": "https://gitlab.libraryofcode.org/EdgyBoi2414", - "github": "https://github.com/EdgyBoi2414", - "bio": "\"If teardrops could be bottled, there'd be swimming pools, built by model..\" - Some Philosopher" - }, - { - "name": "Hector", - "id": "377781496292835339", - "pn": ["ST", "CRS-I"], - "dept": "Technician", - "emailAddress": "hector@staff.libraryofcode.org", - "gitlab": "https://gitlab.libraryofcode.org/Hector", - "github": "https://github.com/Hector6704", - "bio": "Hi there, I'm the developer of Delta, the Discord bot. I'm a free-time French JavASript developer. I hope you'll enjoy LOC!", - "acknowledgements": ["Contributor"] - }, - { - "name": "KhaaZ", - "id": "179908288337412096", - "pn": ["ST", "FSEN"], - "dept": "Core Team", - "emailAddress": "khaaz@staff.libraryofcode.org", - "gitlab": "https://gitlab.libraryofcode.org/KhaaZ", - "github": "https://github.com/Khaazz", - "bio": "I baguette for a living and eat code for breakfast.", - "acknowledgements": ["Assistant Maintainer"] - }, - { - "name": "PlayerVMachine", - "id": "273999507174195203", - "pn": ["ST", "ED-I"], - "dept": "Instructor & Core Team", - "emailAddress": "nicolas@staff.libraryofcode.org", - "bio": "I write C++ to pay off my student loans" - }, - { - "name": "Sterben", - "id": "241361691730903040", - "pn": ["ST", "CRS-I", "ENG-I"], - "dept": "Core Team", - "emailAddress": "sterben@staff.libraryofcode.org", - "gitlab": "https://gitlab.libraryofcode.org/Sterben", - "github": "https://github.com/sterben6", - "bio": "im bored" - }, - { - "name": "Raymond", - "id": "314564798992089090", - "pn": ["AS"], - "dept": "Associate", - "emailAddress": "smashjarchive@staff.libraryofcode.org", - "gitlab": "https://gitlab.libraryofcode.org/javaarchive", - "github": "https://github.com/javaarchive", - "bio": "I don't like reinventing the wheel and don't like coding on small screens like laptops as much" - }, - { - "name": "Null", - "id": "323673862971588609", - "gitlab": "https://gitlab.libraryofcode.org/null", - "acknowledgements": ["Contributor"] - } -] diff --git a/src/events/guildMemberAdd.ts b/src/events/guildMemberAdd.ts index 13b167c..b0a405f 100644 --- a/src/events/guildMemberAdd.ts +++ b/src/events/guildMemberAdd.ts @@ -13,7 +13,7 @@ export default class GuildMemberAdd extends Event { try { const search = await this.client.db.local.muted.get(`muted-${member.user.id}`); if (search === true) { - member.addRole('478373942638149643'); + member.addRole('478373942638149643', 'muted user left server and joined back'); } } catch (err) { this.client.util.handleError(err); diff --git a/src/intervals/autoRelease.ts b/src/intervals/autoRelease.ts index 865c2dd..08ecd6a 100644 --- a/src/intervals/autoRelease.ts +++ b/src/intervals/autoRelease.ts @@ -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: diff --git a/src/intervals/score.ts b/src/intervals/score.ts index b1ced18..61bbb7a 100644 --- a/src/intervals/score.ts +++ b/src/intervals/score.ts @@ -63,8 +63,8 @@ export default async function calculateScore(client: Client): Promise('Inquiry', Inquiry); diff --git a/src/models/Member.ts b/src/models/Member.ts index e7023b5..53f8c1a 100644 --- a/src/models/Member.ts +++ b/src/models/Member.ts @@ -1,11 +1,12 @@ 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'], }, + bio: string, } const Member: Schema = new Schema({ @@ -14,6 +15,7 @@ const Member: Schema = new Schema({ langs: Array, operatingSystems: Array, }, + bio: String, }); export default model('Member', Member); diff --git a/src/models/Score.ts b/src/models/Score.ts index f7e0bc6..96820d2 100644 --- a/src/models/Score.ts +++ b/src/models/Score.ts @@ -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, diff --git a/src/models/ScoreHistorical.ts b/src/models/ScoreHistorical.ts index 1b1ca8a..70b5a6a 100644 --- a/src/models/ScoreHistorical.ts +++ b/src/models/ScoreHistorical.ts @@ -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, }); diff --git a/src/models/index.ts b/src/models/index.ts index d3cc016..294b4ec 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -1,6 +1,7 @@ export { default as Customer, CustomerInterface } from './Customer'; export { default as CustomerPortal, CustomerPortalInterface } from './CustomerPortal'; 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'; diff --git a/src/pbx/actions/Misc.ts b/src/pbx/actions/Misc.ts index 2465a9d..83ead42 100644 --- a/src/pbx/actions/Misc.ts +++ b/src/pbx/actions/Misc.ts @@ -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({ - media: 'sound:access-denied', - }, undefined); - playback.once('PlaybackFinished', () => channel.hangup()); + public static accessDenied(channel: ARI.Channel) { + return new Promise((resolve, reject) => { + channel.play({ + media: 'sound:access-denied', + }, 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 { diff --git a/tsconfig.json b/tsconfig.json index 251319b..675c552 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -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'). */ diff --git a/yarn.lock b/yarn.lock index 2a09a46..5a53728 100644 --- a/yarn.lock +++ b/yarn.lock @@ -596,6 +596,14 @@ ax@0.1.8: resolved "https://registry.yarnpkg.com/ax/-/ax-0.1.8.tgz#27ca9a73fa4c78a478d62d827cad860a24fdd497" integrity sha1-J8qac/pMeKR41i2CfK2GCiT91Jc= +axios@^0.18.1: + version "0.18.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.1.tgz#ff3f0de2e7b5d180e757ad98000f1081b87bcea3" + integrity sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g== + dependencies: + follow-redirects "1.5.10" + is-buffer "^2.0.2" + axios@^0.19.2: version "0.19.2" resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" @@ -2229,6 +2237,11 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= +is-buffer@^2.0.2: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + is-callable@^1.1.4, is-callable@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" @@ -3773,6 +3786,13 @@ standard-as-callback@^2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= +stock-info@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/stock-info/-/stock-info-1.2.0.tgz#a29f54c5416deec5a08a9f3c19736a91c913c20a" + integrity sha512-GxhFrCWlibRdS/9KC1aRMBD0p4IyGj/rKD5InK/ZUOFAapJ76QhVY/ShsNT0WRkOh12Bh6NfZkWU/Ipj90NstQ== + dependencies: + axios "^0.18.1" + stream-shift@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" @@ -4051,6 +4071,11 @@ tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.12.0.tgz#d1fc9cacd06a1456c62f2902b361573e83d66473" integrity sha512-5rxCQkP0kytf4H1T4xz1imjxaUUPMvc5aWp0rJ/VMIN7ClRiH1FwFvBt8wOeMasp/epeUnmSW6CixSIePtiLqA== +tslib@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" + integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== + tsutils@^3.17.1: version "3.17.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"