matrix-word-counter/word-counting.js

137 lines
3.3 KiB
JavaScript

import fs from "node:fs";
import { Module } from "node:module";
class WordCount {
constructor() {
const a = fs.readdirSync("./db");
//make sure dir exists
if (!a.some((b) => b === "count")) {
//if not, make the folder for it
fs.mkdirSync("./db/count");
}
const roomJSONlist = fs.readdirSync("./db/count");
//map to store everything in
this.perRoom = new Map();
this.writePromise = new Map();
this.writeQueued = new Map();
this.statsString = new Map();
//every stored room
for (const obj of roomJSONlist) {
//read and parse file
let roomFile;
try {
roomFile = fs.readFileSync(`./db/count/${obj}`);
} catch (e) {
console.log(`error reading file ${obj}, it may be a dir\n${e}`);
return;
}
const rm = JSON.parse(roomFile);
//get room id from the file name
const roomID = obj.substring(0, obj.length - 5);
//create a submap for the room
const roomMap = new Map();
this.perRoom.set(roomID, roomMap);
//each word is a key to a set of stats per user
const wordsSaved = Object.keys(rm);
for (const word of wordsSaved) {
//get the set of stats for that word
const stats = rm[word];
const users = Object.keys(stats);
//create a map for each word to store each user
const wordMap = new Map();
wordMap.set(word, wordMap);
//process each user
for (const user of users) {
//remaining bit to parce should just be an int to plop in
const userUsage = stats[user];
//if not a number its invalid data
if (Number.isNaN(userUsage)) return;
wordMap.set(user, userUsage);
}
}
}
}
async addToUser(room, word, user, amount) {
if (!this.perRoom.has(room)) this.perRoom.set(room, new Map());
let stats = this.perRoom.get(room).get(word);
if (!stats) {
stats = new Map();
this.perRoom.get(room).set(word, stats);
}
let current = stats.get(user);
//if undefined set it to 0 just to make sure i dont footgun myself
if (!current) current = 0;
//set new value
stats.set(user, current + amount);
this.queueWrite(room);
}
async queueWrite(room) {
//the data is being global, if theres a scheduled write queued already
//the data will be written already so we can just toss any other queued
//for that room
if (this.writeQueued.get(room)) return;
//get the last write to await
const currentWritePromise = this.writePromise.get(room);
if (currentWritePromise) {
//say we already have one right after this
this.writeQueued.set(room, true);
//prevent racey writes
await currentWritePromise;
}
//save the promise to prevent racey writes
this.writePromise.set(room, this.write(room));
//no longer queueing
this.writeQueued.delete(room);
}
async write(room) {
const statsMap = this.perRoom.get(room);
const words = Array.from(statsMap.keys());
const statsObjMap = new Map();
for (const word of words) {
statsObjMap.set(word, Object.fromEntries(statsMap.get(word).entries()));
}
const statsObj = Object.fromEntries(statsObjMap.entries());
const statsString = JSON.stringify(statsObj, null, 2);
const oldStatsString = this.statsString.get(room);
//if its the same as what was already written no need to write
if (oldStatsString === statsString) return;
fs.writeFileSync(`./db/count/${room}.json`, statsString);
}
}
export { WordCount };