DiscordRoleAutoRemover/db.js

157 lines
3.9 KiB
JavaScript
Raw Normal View History

2025-09-29 03:04:38 +02:00
const fs = require('fs');
const path = require('path');
const cache = {};
function tableCache(table) {
let tableCache = cache[table];
if (!tableCache) {
tableCache = {};
cache[table] = tableCache;
}
return tableCache;
}
function dbSerialize(data) {
return JSON.stringify(data, replacer, 2);
}
module.exports.dbSerialize = dbSerialize;
function dbDeserialize(data) {
return JSON.parse(data, reviver);
}
module.exports.dbDeserialize = dbDeserialize;
function dbDelete(table, id) {
const file = dbFile(table, id);
if (fs.existsSync(file)) {
fs.unlinkSync(file);
}
delete tableCache(table)[id];
}
module.exports.dbDelete = dbDelete;
function dbWrite(table, id, data) {
const file = dbFile(table, id);
fs.writeFileSync(file, dbSerialize(data));
tableCache(table)[id] = data;
}
module.exports.dbWrite = dbWrite;
/**
* Gets an entry from the database.
* @param {string} table The table name
* @param {string} id The record ID.
* @returns {object | null} The entry
*/
function dbGet(table, id) {
const file = dbFile(table, id);
let data = tableCache(table)[id];
if (data === undefined) {
if (fs.existsSync(file)) {
data = dbDeserialize(fs.readFileSync(file));
} else {
data = null;
}
tableCache(table)[id] = data;
}
return data;
}
module.exports.dbGet = dbGet;
/**
* Gets all entires of a given table from the database.
* @param {string} table The table name
* @returns {object[]} The entries
*/
function dbGetAll(table) {
const files = fs.readdirSync(dbDir(table));
const all = [];
for (const file of files) {
const id = file.split('.')[0];
const data = dbGet(table, id);
if (!data) {
continue;
}
all.push(data);
}
return all;
}
module.exports.dbGetAll = dbGetAll;
function dbDir(table) {
return path.join('data', dbSafe(table));
}
function dbFile(table, id) {
return path.join('data', dbSafe(table), `${dbSafe(id)}.json`);
}
function dbSafe(str) {
return str.replace(/[^a-z0-9]/gi, '_').toLowerCase();
}
function reviver(key, value) {
if (typeof value === "object" && value != null) {
for (const customTypeName in customTypes) {
if (value.hasOwnProperty(customTypeName)) {
const customTypeValue = value[customTypeName];
const customType = customTypes[customTypeName];
return customType.reviver(customTypeValue);
}
}
}
return value;
}
function replacer(key, value) {
const rawValue = this[key];
for (const customTypeName in customTypes) {
const customType = customTypes[customTypeName];
if (customType.condition(rawValue)) {
return { [customTypeName]: customType.replacer(rawValue) };
}
}
return value;
}
/**
* Custom types support for JSON files. Make sure that no custom type names conflict with actual possible JSON keys.
*
* When serializing:
* - Custom types work by running the `condition` function of every value to be serialized.
* - If it returns `true`, the `replacer` function is called with the value.
* - The return value of the `replacer` will be saved to JSON wrapped in an object using the key of the custom type. (e.g. `{"$myType": "hello"}`).
*
* When deserializing:
* - Each value is checked if it is an object with a key of any custom type.
* - If one is found, the `reviver` is called for the value of this key.
* - The return value is used as the actual value.
*/
const customTypes = {
$date: {
condition: function (value) {
return value instanceof Date;
},
replacer: function (value) {
return value.toISOString();
},
reviver: function (value) {
return new Date(value);
},
},
$set: {
/** @param {Set} value */
condition: function (value) {
return value instanceof Set;
},
/** @param {Set} value */
replacer: function (value) {
return [...value];
},
/** @param {Array} value */
reviver: function (value) {
return new Set(value);
},
},
}