diff --git a/.env.example b/.env.example index 184fed5..ca3ad04 100644 --- a/.env.example +++ b/.env.example @@ -40,6 +40,8 @@ OUTPUT= TEMPDIR= # Put temporary image web server domain TMP_DOMAIN= +# Threshold where optional space saving methods will be performed +THRESHOLD= # Port for serving metrics. Metrics served are compatible with Prometheus. METRICS= diff --git a/app.js b/app.js index 9429ec1..af477cb 100644 --- a/app.js +++ b/app.js @@ -22,10 +22,11 @@ import Shard from "./shard.js"; import ImageWorker from "./utils/services/image.js"; import PrometheusWorker from "./utils/services/prometheus.js"; // some utils -import { readFileSync } from "fs"; +import { promises, readFileSync } from "fs"; import winston from "winston"; import { exec as baseExec } from "child_process"; import { promisify } from "util"; + const exec = promisify(baseExec); // database stuff import database from "./utils/database.js"; @@ -172,4 +173,36 @@ if (isMaster) { }); }); } + + //process the threshold into bytes early + if (process.env.TEMPDIR&&process.env.THRESHOLD) { + const matched = process.env.THRESHOLD.match(/(\d+)([KMGT])/); + const sizes = { + "K":1024, + "M":1048576, + "G":1073741824, + "T":1099511627776 + }; + if (matched&&matched[1]&&matched[2]) { + process.env.THRESHOLD=matched[1]*sizes[matched[2]]; + } else { + logger.error("Invalid THRESHOLD config."); + process.env.THRESHOLD = undefined; + } + let dirstat = (await promises.readdir(process.env.TEMPDIR)) + .map(async (file)=>{ + return new Promise((resolve,reject) =>{ + promises.stat(`${process.env.TEMPDIR}/${file}`) + .then((stats)=>{ + resolve(stats.size) + }) + }) + }); + Promise.all(dirstat) + .then((size)=>{ + process.env.DIRSIZECACHE = size.reduce((a,b)=>{ + return a+b + },0) + }) + } } diff --git a/events/messageCreate.js b/events/messageCreate.js index 4b2cb49..ba9adf5 100644 --- a/events/messageCreate.js +++ b/events/messageCreate.js @@ -4,6 +4,7 @@ import { log, error as _error } from "../utils/logger.js"; import { prefixCache, aliases, disabledCache, disabledCmdCache, commands } from "../utils/collections.js"; import parseCommand from "../utils/parseCommand.js"; import { clean } from "../utils/misc.js"; +import { spawn } from "child_process"; // run when someone sends a message export default async (client, cluster, worker, ipc, message) => { @@ -142,6 +143,34 @@ export default async (client, cluster, worker, ipc, message) => { }, }] }, reference)); + if (process.env.THRESHOLD) { + process.env.DIRSIZECACHE+=result.file.length; + if (process.env.DIRSIZECACHE>process.env.THRESHOLD) { + const files = (await promises.readdir(process.env.TEMPDIR)) + .map((file)=>{ + return new Promise((resolve,reject) => { + promises.stat(`${process.env.TEMPDIR}/${file}`) + .then((fstats)=>{ + resolve({ + name:file, + size:fstats.size, + ctime:fstats.ctime + }); + }) + .catch(reject); + }); + }); + Promise.all(files) + .then((files)=>{ + process.env.DIRSIZECACHE = files.reduce((a,b)=>{ + return a+b.size + },0) + const oldestFile = files.sort((a, b) => a.ctime - b.ctime)[0].name; + promises.rm(`${process.env.TEMPDIR}/${oldestFile}`); + log(`Removed oldest image file: ${oldestFile}`); + }); + } + } } else { await client.createMessage(message.channel.id, "The resulting image was more than 8MB in size, so I can't upload it."); }