diff --git a/package.json b/package.json index 0919a04..c9ae7a6 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "private": false, "devDependencies": { "@types/bull": "^3.14.4", + "@types/cron": "^1.7.2", "@types/express": "^4.17.6", "@types/helmet": "^0.0.47", "@types/jsonwebtoken": "^8.5.0", @@ -32,6 +33,7 @@ "body-parser": "^1.19.0", "brain.js": "^2.0.0-beta.2", "bull": "^3.18.1", + "cron": "^1.8.2", "eris": "^0.13.3", "eris-pagination": "bsian03/eris-pagination", "express": "^4.17.1", diff --git a/src/api/comm.libraryofcode.org/routes/report.ts b/src/api/comm.libraryofcode.org/routes/report.ts index b003802..818e59a 100644 --- a/src/api/comm.libraryofcode.org/routes/report.ts +++ b/src/api/comm.libraryofcode.org/routes/report.ts @@ -4,6 +4,7 @@ import jwt from 'jsonwebtoken'; import { v4 as uuid } from 'uuid'; import { TextChannel } from 'eris'; import { LocalStorage, Route, Server, RichEmbed } from '../../../class'; +import { ScoreHistoricalRaw } from '../../../models/ScoreHistorical'; export default class Report extends Route { public timeout: Set; @@ -43,7 +44,7 @@ export default class Report extends Route { if (!mem) return res.status(404).json({ code: this.constants.codes.NOT_FOUND, message: this.constants.codes.NOT_FOUND }); if (member.locked) return res.status(403).json({ code: this.constants.codes.PERMISSION_DENIED, message: this.constants.messages.PERMISSION_DENIED }); - if (merchant.type !== 1) return res.status(403).json({ code: this.constants.codes.PERMISSION_DENIED, message: this.constants.messages.PERMISSION_DENIED }); + if (merchant?.type !== 1) return res.status(403).json({ code: this.constants.codes.PERMISSION_DENIED, message: this.constants.messages.PERMISSION_DENIED }); const flags = []; @@ -105,6 +106,13 @@ export default class Report extends Route { } } + const historicalData = await this.server.client.db.ScoreHistorical.find({ userID: member.userID }).lean().exec(); + const array: ScoreHistoricalRaw[] = []; + for (const data of historicalData) { + delete data.report?.softInquiries; + array.push(data); + } + if (member.notify) { const chan = await this.server.client.getDMChannel(member.userID); try { @@ -152,6 +160,7 @@ export default class Report extends Route { miscScore, otherScore, inquiries, + historical: array ?? [], }, }); } catch (err) { @@ -246,6 +255,14 @@ export default class Report extends Route { else if (member.cloudServices > 10) cloudServicesScore = 10; else cloudServicesScore = Math.round(member.cloudServices); + const historicalData = await this.server.client.db.ScoreHistorical.find({ userID: member.userID }).lean().exec(); + const array: ScoreHistoricalRaw[] = []; + for (const data of historicalData) { + delete data.report?.softInquiries; + delete data.report?.inquiries; + array.push(data); + } + return res.status(200).json({ code: this.constants.codes.SUCCESS, @@ -268,6 +285,7 @@ export default class Report extends Route { cloudServicesScore, otherScore, miscScore, + historical: array ?? [], }, }); } catch (err) { diff --git a/src/class/Client.ts b/src/class/Client.ts index b6a559a..ef01bed 100644 --- a/src/class/Client.ts +++ b/src/class/Client.ts @@ -3,7 +3,7 @@ import pluris from 'pluris'; import mongoose from 'mongoose'; import { promises as fs } from 'fs'; import { Collection, Command, LocalStorage, Queue, Util, ServerManagement, Event } from '.'; -import { File, FileInterface, Member, MemberInterface, Merchant, MerchantInterface, Moderation, ModerationInterface, NNTrainingData, NNTrainingDataInterface, Note, NoteInterface, PagerNumber, PagerNumberInterface, Rank, RankInterface, Redirect, RedirectInterface, Score, ScoreInterface, Staff, StaffInterface, Stat, StatInterface } from '../models'; +import { File, FileInterface, Member, MemberInterface, Merchant, MerchantInterface, Moderation, ModerationInterface, NNTrainingData, NNTrainingDataInterface, Note, NoteInterface, PagerNumber, PagerNumberInterface, Rank, RankInterface, Redirect, RedirectInterface, Score, ScoreInterface, ScoreHistorical, ScoreHistoricalInterface, Staff, StaffInterface, Stat, StatInterface } from '../models'; import { Config } from '../../types'; // eslint-disable-line pluris(eris); @@ -23,7 +23,7 @@ export default class Client extends eris.Client { public queue: Queue; - public db: { File: mongoose.Model, Member: mongoose.Model, Merchant: mongoose.Model, Moderation: mongoose.Model, NNTrainingData: mongoose.Model, Note: mongoose.Model, PagerNumber: mongoose.Model, Rank: mongoose.Model, Redirect: mongoose.Model, Score: mongoose.Model, Staff: mongoose.Model, Stat: mongoose.Model, local: { muted: LocalStorage } }; + public db: { File: mongoose.Model, Member: mongoose.Model, Merchant: mongoose.Model, Moderation: mongoose.Model, NNTrainingData: mongoose.Model, Note: mongoose.Model, PagerNumber: 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); @@ -31,7 +31,7 @@ export default class Client extends eris.Client { this.events = new Collection(); this.intervals = new Collection(); this.queue = new Queue(this); - this.db = { File, Member, Merchant, Moderation, NNTrainingData, Note, PagerNumber, Rank, Redirect, Score, Staff, Stat, local: { muted: new LocalStorage('muted') } }; + this.db = { File, Member, Merchant, Moderation, NNTrainingData, Note, PagerNumber, Rank, Redirect, Score, ScoreHistorical, Staff, Stat, local: { muted: new LocalStorage('muted') } }; } diff --git a/src/class/Queue.ts b/src/class/Queue.ts index edf0557..f84ac8c 100644 --- a/src/class/Queue.ts +++ b/src/class/Queue.ts @@ -1,5 +1,6 @@ /* eslint-disable no-eval */ import Bull from 'bull'; +import cron from 'cron'; import { TextableChannel } from 'eris'; import { Client, RichEmbed } from '.'; import { ScoreInterface } from '../models'; @@ -17,6 +18,30 @@ export default class Queue { score: new Bull('score', { prefix: 'queue::score', limiter: { max: 80, duration: 1000 } }), }; this.setProcessors(); + // this.setCronJobs(); + } + + protected setCronJobs() { + const historialCommunityReportJob = new cron.CronJob('0 20 * * *', async () => { + try { + const reports = await this.client.db.Score.find().lean().exec(); + const startDate = new Date(); + + for (const report of reports) { + const data = new this.client.db.ScoreHistorical({ + userID: report.userID, + report, + date: startDate, + }); + // eslint-disable-next-line no-await-in-loop + await data.save(); + } + } catch (err) { + this.client.util.handleError(err); + } + }); + + historialCommunityReportJob.start(); } public async jobCounts() { diff --git a/src/models/ScoreHistorical.ts b/src/models/ScoreHistorical.ts new file mode 100644 index 0000000..1b1ca8a --- /dev/null +++ b/src/models/ScoreHistorical.ts @@ -0,0 +1,22 @@ +import { Document, Schema, model } from 'mongoose'; +import { ScoreInterfaceRaw } from '.'; + +export interface ScoreHistoricalRaw { + userID: string, + report: ScoreInterfaceRaw, + date: Date, +} + +export interface ScoreHistoricalInterface extends Document { + userID: string, + report: ScoreInterfaceRaw, + date: Date +} + +const ScoreHistorical: Schema = new Schema({ + userID: String, + report: Object, + date: Date, +}); + +export default model('ScoreHistorical', ScoreHistorical); diff --git a/src/models/index.ts b/src/models/index.ts index dbf39a8..e9dc0be 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -7,6 +7,7 @@ export { default as Note, NoteInterface } from './Note'; export { default as PagerNumber, PagerNumberInterface, PagerNumberRaw } from './PagerNumber'; export { default as Rank, RankInterface } from './Rank'; export { default as Redirect, RedirectInterface, RedirectRaw } from './Redirect'; -export { default as Score, ScoreInterface } from './Score'; +export { default as Score, ScoreInterface, ScoreInterfaceRaw } from './Score'; +export { default as ScoreHistorical, ScoreHistoricalInterface } from './ScoreHistorical'; export { default as Staff, StaffInterface } from './Staff'; export { default as Stat, StatInterface } from './Stat'; diff --git a/yarn.lock b/yarn.lock index 5b761d2..7bd2026 100644 --- a/yarn.lock +++ b/yarn.lock @@ -64,6 +64,14 @@ dependencies: "@types/node" "*" +"@types/cron@^1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@types/cron/-/cron-1.7.2.tgz#e9fb420da616920dae82d13adfca53282ffaab6e" + integrity sha512-AEpNLRcsVSc5AdseJKNHpz0d4e8+ow+abTaC0fKDbAU86rF1evoFF0oC2fV9FdqtfVXkG2LKshpLTJCFOpyvTg== + dependencies: + "@types/node" "*" + moment ">=2.14.0" + "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" @@ -694,6 +702,13 @@ cron-parser@^2.13.0: is-nan "^1.3.0" moment-timezone "^0.5.31" +cron@^1.8.2: + version "1.8.2" + resolved "https://registry.yarnpkg.com/cron/-/cron-1.8.2.tgz#4ac5e3c55ba8c163d84f3407bde94632da8370ce" + integrity sha512-Gk2c4y6xKEO8FSAUTklqtfSr7oTq0CiPQeLBG5Fl0qoXpZyMcj1SG59YL+hqq04bu6/IuEA7lMkYDAplQNKkyg== + dependencies: + moment-timezone "^0.5.x" + cross-spawn@^7.0.2: version "7.0.2" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.2.tgz#d0d7dcfa74e89115c7619f4f721a94e1fdb716d6" @@ -2056,14 +2071,14 @@ mkdirp@^1.0.3: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -moment-timezone@^0.5.31: +moment-timezone@^0.5.31, moment-timezone@^0.5.x: version "0.5.31" resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.31.tgz#9c40d8c5026f0c7ab46eda3d63e49c155148de05" integrity sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA== dependencies: moment ">= 2.9.0" -"moment@>= 2.9.0": +"moment@>= 2.9.0", moment@>=2.14.0: version "2.29.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==