您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Useful library for dealing with the storage.
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/405831/818910/Monkey%20Storage.js
// ==UserScript== // @name Monkey Storage // @namespace https://rafaelgssa.gitlab.io/monkey-scripts // @author rafaelgssa // @version 1.0.1 // @description Useful library for dealing with the storage. // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js // @require https://unpkg.com/uuid@latest/dist/umd/uuidv4.min.js // @require https://gf.qytechs.cn/scripts/405813-monkey-utils/code/Monkey%20Utils.js?version=818882 // @grant GM.setValue // @grant GM.getValue // @grant GM.deleteValue // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // ==/UserScript== /* global MonkeyUtils, uuidv4 */ /** * @typedef {Object} MonkeyStorageLock * @property {string} uuid * @property {string} key * @property {number} [timeoutId] * @typedef {Object} MonkeyStorageLocked * @property {string} uuid * @property {number} timestamp * @typedef {() => Promise<void>} MonkeyStorageRelease * @typedef {{ * [K: string]: unknown, * settings: Record<string, unknown> * }} MonkeyStorageValues */ // eslint-disable-next-line const MonkeyStorage = (() => { let _id = ''; /** @type {MonkeyStorageValues} */ const _defaultValues = { settings: {}, }; /** @type {MonkeyStorageValues} */ const _cache = { settings: {}, }; /** * Initializes the storage. * @param {string} id The ID to use for the local storage. * @param {Partial<MonkeyStorageValues>} [defaultValues] Any default values to set. * @returns {Promise<void>} */ const init = (id, defaultValues) => { _id = id; if (MonkeyUtils.isSet(defaultValues)) { for (const [key, value] of Object.entries(defaultValues)) { setDefaultSetting(key, value); } } return _updateCache('settings'); }; /** * Sets a default value. * @param {string} key The key of the default value to set. * @param {unknown} value The default value to set. */ const setDefaultValue = (key, value) => { _defaultValues[key] = value; }; /** * Sets a value in the storage. * @param {string} key The key of the value to set. * @param {unknown} value The value to set. * @returns {Promise<void>} */ const setValue = async (key, value) => { const stringifiedValue = JSON.stringify(value); await GM.setValue(key, stringifiedValue); _cache[key] = value; }; /** * Gets a value from the cache. * @param {string} key The key of the value to get. * @param {boolean} [updateCache] Whether to update the cache with the storage or not. * @returns {Promise<unknown>} The value. */ const getValue = async (key, updateCache = false) => { if (!MonkeyUtils.isSet(_cache[key]) || updateCache) { await _updateCache(key); } return _cache[key]; }; /** * Updates a value in the cache with the storage. * @param {string} key The key of the value to update. * @returns {Promise<void>} */ const _updateCache = async (key) => { let value = await GM.getValue(key); if (typeof value === 'string') { try { value = JSON.parse(value); } catch (err) { // Value is already parsed, just ignore. } } _cache[key] = MonkeyUtils.isSet(value) ? value : _defaultValues[key]; }; /** * Deletes a value from the storage. * @param {string} key The key of the value to delete. * @returns {Promise<void>} */ const deleteValue = async (key) => { await GM.deleteValue(key); delete _cache[key]; }; /** * Sets a value in the local storage. * @param {string} key The key of the value to set. * @param {unknown} value The value to set. */ const setLocalValue = (key, value) => { const stringifiedValue = JSON.stringify(value); window.localStorage.setItem(`${_id}_${key}`, stringifiedValue); _cache[key] = value; }; /** * Gets a value from the cache. * @param {string} key The key of the value to get. * @param {boolean} [updateCache] Whether to update the cache with the local storage or not. * @returns {unknown} The value. */ const getLocalValue = (key, updateCache = false) => { if (!MonkeyUtils.isSet(_cache[key]) || updateCache) { _updateLocalCache(key); } return _cache[key]; }; /** * Updates a value in the cache with the local storage. * @param {string} key The key of the value to update. */ const _updateLocalCache = (key) => { let value = window.localStorage.getItem(`${_id}_${key}`); if (typeof value === 'string') { try { value = JSON.parse(value); } catch (err) { // Value is already parsed, just ignore. } } _cache[key] = MonkeyUtils.isSet(value) ? value : _defaultValues[key]; }; /** * Deletes a value from the local storage. * @param {string} key The key of the value to delete. */ const deleteLocalValue = (key) => { window.localStorage.removeItem(`${_id}_${key}`); delete _cache[key]; }; /** * Sets a default setting. * @param {string} key The key of the default setting to set. * @param {unknown} setting The default setting to set. */ const setDefaultSetting = (key, setting) => { _defaultValues.settings[key] = setting; }; /** * Sets a setting in the cache. * @param {string} key The key of the setting to set. * @param {unknown} setting The setting to set. */ const setSetting = (key, setting) => { _cache.settings[key] = setting; }; /** * Gets a setting from the cache. * @param {string} key The key of the setting to get. * @param {boolean} [updateCache] Whether to update the settings cache with the storage or not. * @returns {Promise<unknown>} The setting. */ const getSetting = async (key, updateCache = false) => { if (isSettingsEmpty() || !MonkeyUtils.isSet(_cache.settings[key]) || updateCache) { await _updateCache('settings'); } return _cache.settings[key]; }; /** * Deletes a setting from the cache. * @param {string} key The key of the setting to delete. */ const deleteSetting = (key) => { delete _cache.settings[key]; }; /** * Saves the settings from the cache. * @returns {Promise<void>} */ const saveSettings = () => { return setValue('settings', _cache.settings); }; /** * Checks if the settings cache is empty. * @returns {boolean} Whether the settings cache is empty or not. */ const isSettingsEmpty = () => { return Object.keys(_cache.settings).length === 0; }; /** * Creates a lock in the storage to prevent other tabs from executing concurrently. * @param {string} key The key of the lock. * @returns {Promise<MonkeyStorageRelease | undefined>} A function to release the lock, if successful. */ const createLock = async (key) => { const uuid = uuidv4(); /** @type {MonkeyStorageLock} */ const lock = { uuid, key }; /** @type {MonkeyStorageLocked} */ let locked; _logLockProgress('Trying to create', lock); let value = await GM.getValue(key); locked = JSON.parse(MonkeyUtils.isSet(value) ? value.toString() : '{}'); if (locked.uuid && locked.uuid !== uuid && Date.now() - locked.timestamp > 5000) { _logLockProgress('Failed to create', lock); return; } _logLockProgress('Preparing to create', lock); await GM.setValue(key, `{ "uuid": "${uuid}", "timestamp": ${Date.now()} }`); await MonkeyUtils.sleep(1); value = await GM.getValue(key); locked = JSON.parse(MonkeyUtils.isSet(value) ? value.toString() : '{}'); if (locked.uuid !== uuid) { _logLockProgress('Failed to create', lock); return; } _logLockProgress('Created', lock); _reinforceLock(lock, 1); return () => _releaseLock(lock); }; /** * Keeps reinforcing a lock. * @param {MonkeyStorageLock} lock The lock to reinforce. * @param {number} frequency How frequently to reinforce the lock. */ const _reinforceLock = async (lock, frequency) => { const { uuid, key } = lock; _logLockProgress('Reinforcing', lock); await GM.setValue(key, `{ "uuid": "${uuid}", "timestamp": ${Date.now()} }`); lock.timeoutId = window.setTimeout(_reinforceLock, frequency * 1000, lock, frequency); }; /** * Releases a lock. * @param {MonkeyStorageLock} lock The lock to release. * @returns {Promise<void>} */ const _releaseLock = async (lock) => { const { uuid, key, timeoutId } = lock; window.clearTimeout(timeoutId); let value = await GM.getValue(key); /** @type {MonkeyStorageLocked} */ const locked = JSON.parse(MonkeyUtils.isSet(value) ? value.toString() : '{}'); if (locked.uuid === uuid) { await deleteValue(key); } _logLockProgress('Released', lock); }; /** * @param {string} progress * @param {MonkeyStorageLock} lock */ // eslint-disable-next-line const _logLockProgress = (progress, { uuid, key }) => { //console.log(`[${Date.now()}] ${progress} lock ${uuid}, ${key}`) }; return { init, setDefaultValue, setValue, getValue, deleteValue, setLocalValue, getLocalValue, deleteLocalValue, setDefaultSetting, setSetting, getSetting, deleteSetting, saveSettings, isSettingsEmpty, createLock, }; })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址