Library for misc util functions for userscripts
This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.greasyfork.org/scripts/581026/1842191/Misc%20utils%20library.js
// ==UserScript==
// @name Misc utils library
// @namespace MiscUtilsLib
// @description Library for misc util functions for userscripts
// @author kekekeKaj
// @version 1.1.1
// @grant none
// ==/UserScript==
// IMPORTANT: update version number when publishing!!!
function getMiscUtilsLibInfo() {
return 'Misc Utils Lib v1.1';
}
// ----LOCAL STORAGE CACHE-------
function getLocalStorageKeys() {
return {
stackMsgTemplates: generateLocalStorageKey('stackMsgTemplates'),
reviewMsgTemplates: generateLocalStorageKey('reviewMsgTemplates'),
recMsgTemplates: generateLocalStorageKey('recMsgTemplates'),
lastClearTimestamp: generateLocalStorageKey('lastClearTimestamp'),
reportedItemsInfo: generateLocalStorageKey('reportedItemsInfo'),
modSettings: generateLocalStorageKey('modSettings'),
reportInfoPrefix: generateLocalStorageKey('reportInfo.'),
stackInfoPrefix: generateLocalStorageKey('stackInfo.'),
reviewInfoPrefix: generateLocalStorageKey('reviewInfo.')
}
}
function loadCachedReportedItemInfo(reportType, itemId) {
if (itemId == null) {
return null;
}
switch(reportType) {
case 'stack':
return loadFromJSONInLocalStorage(getLocalStorageKeys().stackInfoPrefix + itemId);
case 'review':
return loadFromJSONInLocalStorage(getLocalStorageKeys().reviewInfoPrefix + itemId);
default:
throw 'Cannot load cached reported item: invalid report type';
}
}
function buildReportedItemStorageKey(reportType, itemId) {
if (itemId == null) {
return null;
}
switch(reportType) {
case 'stack':
return getLocalStorageKeys().stackInfoPrefix + itemId;
case 'review':
return getLocalStorageKeys().reviewInfoPrefix + itemId;
default:
throw 'Cannot build reported item storage key: invalid report type';
}
}
function getSessionStorageKeys() {
return {
stackEditResult: generateLocalStorageKey('stackEditResult'),
targetItemId: generateLocalStorageKey('targetItemId')
}
}
function getReviewAdminStorageKeyBase() {
return 'reviewAdmin';
}
function generateLocalStorageKey(keyword) {
return `${getReviewAdminStorageKeyBase()}.${keyword}`;
}
function saveAsJSONInLocalStorage(key, item, logItem = false) {
if (key == null || item == null) {
console.warn('Cannot save item: key or item is null!');
return;
}
setLocalStorageItem(key, JSON.stringify(item), logItem);
}
function loadFromJSONInLocalStorage(key, logItem = false) {
if (key == null) {
return null;
}
const objJSON = localStorage.getItem(key);
const parsedObj = objJSON == null ? null : JSON.parse(objJSON);
if (logItem) {
console.info(`Loaded from key ${key}: `, parsedObj);
}
return parsedObj;
}
function loadModSettings(defaultModName) {
const modSettings = getDefaultModSettings();
if (defaultModName != null) {
modSettings.modName = moderator;
}
overrideWithModSettings(
modSettings, loadFromJSONInLocalStorage(getLocalStorageKeys().modSettings, true));
return modSettings;
}
function setLocalStorageItem(key, item, logItem = false) {
try {
localStorage.setItem(key, item);
if (logItem) {
console.info(`Setting local storage value for ${key}: ${item}`);
}
} catch(err) {
if (err instanceof DOMException) {
console.warn('Error when trying to store item: ', err);
console.warn('LocalStorage may be full. Will attempt to clear storage');
if (shouldClearReportCache()) {
console.log("Auto clearing old report data.");
clearMALModCache();
}
} else {
console.error('Unknown error when storing item: ', err)
}
}
}
function clearMALModCache() {
Object.keys(localStorage).forEach(function (e) {
if (e.startsWith(getReviewAdminStorageKeyBase()) || e.includes("submissions&type")) {
localStorage.removeItem(e);
}
});
const lastClearTimestampKey = getLocalStorageKeys().lastClearTimestamp;
localStorage.setItem(lastClearTimestampKey, String(Date.now()));
}
/**
* Function to check whether to clear the queue cache.
* @returns true if we don't have a last clear timestamp or if the timestamp is from more than a day ago.
*/
function shouldClearReportCache() {
const lastClearTimestampKey = getLocalStorageKeys().lastClearTimestamp;
const epochTimeStr = localStorage.getItem(lastClearTimestampKey);
if (epochTimeStr == null) {
return true;
}
const lastClearTime = new Date(parseInt(epochTimeStr));
const oneDayInMs = 60000 * 60 * 24;
return lastClearTime == null || Date.now() - lastClearTime > oneDayInMs;
}
// --- FETCH ---
function fetchPostReq(url, data, successCallback, errorCallback) {
fetch(
url,
{
method: "POST",
body: data
})
.then(response => { return response.text() })
.then(successCallback)
.catch(errorCallback);
}
function fetchGetReq(url, successCallback, errorCallback) {
fetch(url)
.then(unwrapResponseTxt)
.then(successCallback)
.catch(errorCallback);
}
function unwrapResponseTxt(response) {
if (response.ok) {
return response.text();
}
throw new Error('Request failed for URL: ' + response.url);
}
// --- OTHER FUNCTIONS ---
/**
* Loads msg templates and returns them in a map
* @param {string} localStorageKey The local storage key from which to load the messages.
* @returns {Map} of msg summary -> msgs
*/
function loadMsgTemplates(localStorageKey) {
const rawTemplateStr = localStorage.getItem(localStorageKey);
if (rawTemplateStr == null) {
console.error('No templates found in local storage!');
return null;
}
const splitTemplates = rawTemplateStr.split("*****").filter(segment => segment.trim().length > 0);
// if the parsing was correct, we should end up with even number of segments representing pairs
// consisting of a stock message and its summary.
if (splitTemplates.length % 2 !== 0) {
console.error("Error parsing message templates!");
return null;
}
const msgTemplateMap = new Map();
for (let i = 0; i < splitTemplates.length; i += 2) {
msgTemplateMap.set(splitTemplates[i].trim(), splitTemplates[i + 1].trim());
}
return msgTemplateMap;
}
function getDefaultModSettings() {
return {
modName: "",
modTitle: "User Content Moderator",
bulkActionDelayInMs: 200
}
}
/**
* Overrides an object's values with the ones from the mod's settings config.
*
* @param {any} objToOverride
* @param {any} modSettings
*/
function overrideWithModSettings(objToOverride, modSettings) {
if (modSettings == null) {
return;
}
if (hasNonWhitespaceValue(modSettings.modName)) {
objToOverride.modName = modSettings.modName;
}
if (hasNonWhitespaceValue(modSettings.modTitle)) {
objToOverride.modTitle = modSettings.modTitle;
}
if (modSettings.bulkActionDelayInMs != null) {
objToOverride.bulkActionDelayInMs = modSettings.bulkActionDelayInMs;
}
}
function getProfileUrl(username) {
return `/profile/${username}`;
}