/* The Artificer was built in memory of Babka * With love, Ean * * December 21, 2020 */ import { // Discordeno deps cache, cacheHandlers, // imagescript dep is, // Log4Deno deps log, LT, } from '../deps.ts'; import { PastCommandCount } from './mod.d.ts'; import { dbClient, weekDays } from './db.ts'; import utils from './utils.ts'; import config from '../config.ts'; // getRandomStatus() returns status as string // Gets a new random status for the bot const getRandomStatus = async (): Promise => { let status = ''; switch (Math.floor((Math.random() * 4) + 1)) { case 1: status = `${config.prefix}help for commands`; break; case 2: status = `Running V${config.version}`; break; case 3: status = `${config.prefix}info to learn more`; break; default: { const cachedCount = await cacheHandlers.size('guilds'); status = `Rolling dice for ${cachedCount + cache.dispatchedGuildIds.size} servers`; break; } } return status; }; // updateListStatistics(bot ID, current guild count) returns nothing, posts to botlists // Sends the current server count to all bot list sites we are listed on const updateListStatistics = (botID: bigint, serverCount: number): void => { config.botLists.forEach(async (e) => { log(LT.LOG, `Updating statistics for ${JSON.stringify(e)}`); if (e.enabled) { const tempHeaders = new Headers(); tempHeaders.append(e.headers[0].header, e.headers[0].value); tempHeaders.append('Content-Type', 'application/json'); // ?{} is a template used in config, just need to replace it with the real value const response = await fetch(e.apiUrl.replace('?{bot_id}', botID.toString()), { 'method': 'POST', 'headers': tempHeaders, 'body': JSON.stringify(e.body).replace('"?{server_count}"', serverCount.toString()), // ?{server_count} needs the "" removed from around it aswell to make sure its sent as a number }); log(LT.INFO, `Posted server count to ${e.name}. Results: ${JSON.stringify(response)}`); } }); }; // Keep one week of data const hoursToKeep = 7 * 24; const previousHours: Array> = []; // updateHourlyRates() returns nothing, updates DB directly // Updates the hourlyRate for command usage const updateHourlyRates = async () => { try { const newestHour = await dbClient.query('SELECT command, count FROM command_cnt ORDER BY command;').catch((e) => utils.commonLoggers.dbError('intervals.ts:71', 'query', e)); previousHours.push(newestHour); if (previousHours.length > 1) { const oldestHour = previousHours[0]; const computedDiff: Array = []; for (let i = 0; i < newestHour.length; i++) { computedDiff.push({ command: newestHour[i].command, count: (newestHour[i].count - oldestHour[i].count), }); log(LT.LOG, `Updating hourlyRate | Computing diffs: ${JSON.stringify(computedDiff)}`); } // Update DB computedDiff.forEach(async (cmd) => { log(LT.LOG, `Updating hourlyRate | Storing to DB: ${JSON.stringify(cmd)}`); await dbClient.execute(`UPDATE command_cnt SET hourlyRate = ? WHERE command = ?`, [cmd.count / previousHours.length, cmd.command]).catch((e) => utils.commonLoggers.dbError('intervals.ts:88', 'update', e) ); }); } if (previousHours.length > hoursToKeep) { previousHours.unshift(); } } catch (e) { log(LT.ERROR, `Something went wrong in previousHours interval | Error: ${e.name} - ${e.message}`); } }; // getPercentOfRange(min, max, val) returns number // Gets a percent value of where val lies in the min-max range const getPercentOfRange = (minVal: number, maxVal: number, val: number): number => { const localMax = maxVal - minVal; const localVal = val - minVal; return localVal / localMax; }; // Pixel locations in heatmap-base.png, pixel locations are 0 based // dayPixels holds the left and right (AKA X Coord) pixel locations for each col (ex: [leftPX, rightPX]) const dayPixels: Array> = [ [72, 159], [163, 260], [264, 359], [363, 497], [501, 608], [612, 686], [690, 800], ]; // hourPixels holds the top and bottom (AKA Y Coord) pixel locations for each row (ex: [topPX, botPX]) const hourPixels: Array> = [ [29, 49], [51, 72], [74, 95], [97, 118], [120, 141], [143, 164], [166, 187], [189, 209], [211, 232], [234, 254], [256, 277], [279, 299], [301, 322], [324, 345], [347, 368], [370, 391], [393, 413], [415, 436], [438, 459], [461, 482], [484, 505], [507, 528], [530, 550], [552, 572], ]; // updateHeatmap() returns nothing, creates new heatmap.png // Updates the heatmap image with latest data from the db const updateHeatmapPng = async () => { const baseHeatmap = Deno.readFileSync('./src/endpoints/gets/heatmap-base.png'); const heatmap = await is.decode(baseHeatmap); if (heatmap instanceof is.Image) { // Get latest data from DB const heatmapData = await dbClient.query('SELECT * FROM roll_time_heatmap ORDER BY hour;').catch((e) => utils.commonLoggers.dbError('intervals.ts:148', 'query', e)); let minRollCnt = Infinity; let maxRollCnt = 0; // determine min and max values for (const hour of heatmapData) { for (const day of weekDays) { const rollCnt = hour[day]; log(LT.LOG, `updateHeatmapPng | finding min/max | min: ${minRollCnt} max: ${maxRollCnt} curr: ${rollCnt}`); if (rollCnt > maxRollCnt) { maxRollCnt = rollCnt; } if (rollCnt < minRollCnt) { minRollCnt = rollCnt; } } } // Apply values to image for (let hour = 0; hour < heatmapData.length; hour++) { for (let day = 0; day < weekDays.length; day++) { log(LT.LOG, `updateHeatmapPng | putting ${weekDays[day]} ${hour}:00 into image`); const percent = getPercentOfRange(minRollCnt, maxRollCnt, heatmapData[hour][weekDays[day]]); heatmap.drawBox( dayPixels[day][0] + 1, hourPixels[hour][0] + 1, dayPixels[day][1] - dayPixels[day][0] + 1, hourPixels[hour][1] - hourPixels[hour][0] + 1, is.Image.rgbToColor( 255 * (1 - percent), 255 * percent, 0, ), ); } } Deno.writeFileSync('./src/endpoints/gets/heatmap.png', await heatmap.encode()); } }; export default { getRandomStatus, updateListStatistics, updateHourlyRates, updateHeatmapPng, };