// ==UserScript== // @name 控制网页使用限制 单独域名版 // @namespace http://tampermonkey.net/ // @version 0.9 // @description 可以记录在不同的网站的使用时间,设置每个网站的可用时间,如果到达了指定的时间,页面会被遮挡而无法正常 // @author lavaf // @match https://*/* // @match http://*/* // @grant GM.setValue // @grant GM.getValue // @grant GM.deleteValue // @grant GM.listValues // @grant window.close // @noframes // @require https://cdn.bootcss.com/jquery/3.4.1/jquery.js // @require https://cdn.bootcss.com/jqueryui/1.12.1/jquery-ui.js // @downloadURL https://update.greasyfork.icu/scripts/402050/%E6%8E%A7%E5%88%B6%E7%BD%91%E9%A1%B5%E4%BD%BF%E7%94%A8%E9%99%90%E5%88%B6%20%E5%8D%95%E7%8B%AC%E5%9F%9F%E5%90%8D%E7%89%88.user.js // @updateURL https://update.greasyfork.icu/scripts/402050/%E6%8E%A7%E5%88%B6%E7%BD%91%E9%A1%B5%E4%BD%BF%E7%94%A8%E9%99%90%E5%88%B6%20%E5%8D%95%E7%8B%AC%E5%9F%9F%E5%90%8D%E7%89%88.meta.js // ==/UserScript== (async function () { 'use strict'; //region load css addStyle("@import 'https://cdn.bootcss.com/jqueryui/1.12.1/jquery-ui.css'"); addStyle(".web-page-usage-input-item input{width: 80%}") /** * 添加style 标签到页面 * @param style * @return {HTMLElement} */ function addStyle(style) { const head = document.getElementsByTagName("HEAD")[0]; const ele = head.appendChild(window.document.createElement('style')); ele.innerHTML = style; return ele; } //endregion load css //region function data let usage; /** * 删除数据 * @return {Promise} */ async function deleteData() { try { // noinspection JSUnresolvedVariable if (GM !== undefined && GM.deleteValue !== undefined && typeof GM.deleteValue == 'function') { // noinspection JSUnresolvedVariable,JSUnresolvedFunction await GM.deleteValue(realKey); } } catch (e) { // console.log(log_key, 'error', e) } } /** * @param index {String} * @returns {Promise} */ async function getData(index = realKey) { try { // noinspection JSUnresolvedVariable if (GM !== undefined && GM.getValue !== undefined) { // noinspection JSUnresolvedVariable,JSCheckFunctionSignatures let data = await GM.getValue(index).catch((reason => { console.log(log_key, 'error in promise catch', reason); })); if (data === undefined) { console.log(log_key,'getData data undefined') return null; } let dataObject=JSON.parse(data); if (dataObject.statistics.merge === undefined) { return data; } else { return await getData(dataObject.statistics.merge); } } } catch (e) { console.log(log_key, 'error in try-catch getData', e) } return null; } /** * * @param key {String} * @return {Promise} */ async function getRealKey(key) { console.log(log_key,"getRealKey param:key",key) try { // noinspection JSUnresolvedVariable if (GM !== undefined && GM.getValue !== undefined) { // noinspection JSUnresolvedVariable,JSCheckFunctionSignatures let valueString = await GM.getValue(key).catch((reason => { console.log(log_key, 'error in promise catch', reason); })); if (valueString === undefined) { return key; } console.log(log_key,"getRealKey valueString",valueString) let valueObject=JSON.parse(valueString); if (valueObject.statistics.merge === undefined) { console.log(log_key,'getRealKey 没有继续合并') return key; } else { if (valueObject.statistics.merge.indexOf(key_base) >= 0) { if (valueObject.statistics.merge===key){ throw new Error("出现回环") } return await getRealKey(valueObject.statistics.merge); } else { throw new Error("存储数据时出错,导致获取的key 非法") } } } } catch (e) { console.log(log_key, 'error in try-catch getRealKey', e) } return null; } /** * 获取其他域的数据 * @param key 保存数据的键 * @returns {Promise} */ async function getOtherData(key) { // console.log(log_key, 'getOtherData called:key:', key); try { // console.log(log_key,'getOtherData current key',newVar) // noinspection JSUnresolvedVariable if (GM !== undefined && GM.getValue !== undefined) { // noinspection JSUnresolvedVariable let valueString = await GM.getValue(key).catch((reason => { console.log(log_key, 'error in promise catch', reason); })); let ob=JSON.parse(valueString); if (valueString != null) { if (ob.statistics.merge === undefined) return ob; else return await getOtherData(ob.statistics.merge); } } } catch (e) { console.log(log_key, 'error in try-catch getOtherData', e) } return null; } /** * 保存数据 * @param remedy 保存时是否修复差距,比如还有一个页面写入这个内容,获取他的数据,然后添加自己的数据,基本上这样会叫上2 秒 * @returns {Promise} */ async function saveData(remedy = true) { console.log(log_key, "savedData called") if (remedy) await remedyTimeDiff(); let value = JSON.stringify(usage); try { // noinspection JSUnresolvedVariable if (GM !== undefined && GM.setValue !== undefined && typeof GM.setValue == 'function') { // noinspection JSUnresolvedVariable,JSUnresolvedFunction await GM.setValue(realKey, value); } } catch (e) { // console.log(log_key, 'error', e) } } /** * * @returns {Promise} */ async function getList() { try { // noinspection JSUnresolvedVariable if (GM !== undefined && GM.listValues !== undefined && typeof GM.listValues == 'function') { // noinspection JSUnresolvedVariable,JSUnresolvedFunction return await GM.listValues(); } } catch (e) { // console.log(log_key, 'error', e) } return null; } /** * 新建一个对象 * @return {{restrict: {single: undefined, domain, limit: undefined, interval: undefined}, statistics: {single: {date: undefined, time: number}|null, data: [], domain}}} */ function newObject() { return { 'restrict': {'domain': current_domain, 'limit': undefined, single: undefined, "interval": undefined}, 'statistics': { 'domain': current_domain, 'data': [], "single": {'time': 0, "date": undefined}, 'merge': undefined } }; } /** * 获取数据,解析成json对象 * @return {{'restrict':{'domain':String,'limit':Number,'single':Number},statistics:{'domain':String,'data':[{year:Number,month:Number,day:Number,time:Number}]}}} */ async function getUsage(save = false) { let data = await getData(); // console.log(log_key, 'data:', data); if (data == null) { console.log(log_key, "initUsage", "getData return null,可能是合并的数据被删除,此时需要重置合并选项,填写-1,点击确认即可"); let usage = newObject(); if (save) await saveData(); return usage; } else { let parse = JSON.parse(data); if (parse.restrict === undefined || parse.statistics === undefined) { console.log(log_key, 'script data is null'); let usage = newObject(); if (save) await saveData(); return usage; } return parse; } } async function initUsage() { usage = await getUsage(); //console.log(log_key, 'after initUsage usage:', usage); } //endregion function data //let message = "web_page_usage_init"; if (window.top !== window.self) { //console.log(message,"Current environment is frame") return; } let webPageUsage = "web_page_usage"; console.log(webPageUsage, 'start'); //region init key-url let current_h_href = location.href; //console.log(message, "current_h_href", current_h_href); let reg = new RegExp(/^(?:(?:ftp|http[s]?)?:\/\/)?(?:[\dA-Za-z-]+\.)+[\dA-Za-z-]+(?::[\d]{1,4})*/); let reg_localhost = new RegExp(/^(?:(?:ftp|http[s]?)?:\/\/)?localhost(?::[\d]{1,4})*/); let result = reg.exec(current_h_href); if (result == null) { //console.log(message, 'reg 不匹配'); result = reg_localhost.exec(current_h_href); if (result == null) { //console.log(message, "result is null"); return } } // console.log(log_key, "result:", result); let current_domain = result[0]; // let url_part = current_domain.split("."); // current_domain=url_part[url_part.length-2]+"."+url_part[url_part.length-1] // console.log(log_key,current_domain); let log_key = webPageUsage + "->" + current_domain; let key_base = "f-usage-data-"; let key = key_base + current_domain; let realKey=await getRealKey(key); if (realKey.indexOf(key_base)!=0) { realKey=key_base+realKey; } console.log(log_key,"key",key,'realKey',realKey); //endregion key-url await initUsage(); if (usage === undefined) { //console.log(log_key, "usage is null"); usage = {}; } let statistics_data = usage.statistics; let restrict_data = usage.restrict; let current_statistics = getDomainStatisticsTimeObject(); //console.log(log_key, 'current_statistics', current_statistics); let statisticsTimer; let checkTimer; let counter = 0; let printTimer; //region ui let manager_panel = $("
", {id: "web_page_usage_manager_panel_id", 'translate': 'no'}); manager_panel.appendTo('body'); let web_page_usage_z_index = 10000001; let dialog_z_index = '10000001'; let curtain_z_index = 10000000; manager_panel.css({ 'position': 'fixed', 'left': '0', 'top': '50%', 'z-index': web_page_usage_z_index, 'background-color': 'white', 'border': '1px solid black' }); //region statistics let statistics_panel = $('
'); let totalTimeWasteLabel = $("

"); totalTimeWasteLabel.appendTo(statistics_panel); let statistics_ol = $('

    ', {id: "web-page-usage-statistics-ol"}); statistics_ol.css({'height': "100px", 'overflow-y': 'scroll'}); statistics_ol.appendTo(statistics_panel); let currentSecond = $(""); currentSecond.appendTo(statistics_panel); currentSecond.title = "当前计算时间的秒数 当前页面从打开直到现在的秒数" let clear_data_button = $("
`; $("#web-page-usage-export").click(function () { console.log(log_key, '复制后面的内容用于以后导入', usage) }); let restrictPanel = $($.parseHTML(restrictPanelString)); let dialog_string = `
到达单次使用限制了
`; let singleDialog = $($.parseHTML(dialog_string)); singleDialog.css('display', 'none'); singleDialog.appendTo('body'); restrictPanel.appendTo(manager_panel); let top = $('div#web-page-usage-top'); let input_string = `
`; let input_div = $($.parseHTML(input_string)); input_div.appendTo(top); let saveButton = $("#saveLimitButton"); let input_url = $("#add_input_data"); input_url.val(current_domain); let input_limit = $("#add_input_limit_time"); input_limit.val(restrict_data.limit); let inputSingleLimit = $("#add_input_single_limit"); inputSingleLimit.val(restrict_data.single); let interval = $("#add_input_time_interval") interval.val(restrict_data.interval) let saveSingleInterval = $("#save_single_interval") let inputUrlMerger=$("#add_input_merger") let saveMergerButton=$("#saveMergerButton") if (key === realKey) { inputUrlMerger.val(-1); } else { inputUrlMerger.val(realKey) } saveMergerButton.click(async function (){ stopTimer() let merge = inputUrlMerger.val(); if (merge == "-1") { statistics_data.merge = undefined; } else { let realKey1 = await getRealKey(key_base+merge); statistics_data.merge= realKey1; await saveData();//存储当前的merger alert("请刷新页面") // console.log(log_key,'update merger','merger',merge,realKey1," ",realKey) } // await afterUpdateConfig(); }) saveButton.click(async function () { stopTimer(); //获取input 中的内容 let url = input_url.val(); console.log(log_key, "添加限制", url, input_limit.val()); let limit = parseInt(input_limit.val()); if (limit === Number.NaN || limit === -1) { restrict_data.limit = undefined; } else { // noinspection JSValidateTypes restrict_data.limit = limit; console.log(log_key, "保存", limit, restrict_data); } await afterUpdateConfig(); }); let saveSingleButton = $("#save_single_limit"); saveSingleButton.click(async function () { stopTimer(); //获取input 中的内容 //let url = input_url.val(); // noinspection JSValidateTypes8 let single = parseInt(inputSingleLimit.val()); if (single === Number.NaN || single === -1) { restrict_data.single = undefined; } else { restrict_data.single = single; } await afterUpdateConfig(); }); saveSingleInterval.click(async function () { stopTimer(); let interval1 = parseInt(interval.val()); if (interval1 === Number.NaN || interval1 === -1) { restrict_data.interval = undefined; } else { restrict_data.interval = interval1; } await saveData(); startTimer(); printList(); }) // let center = $('div#center'); let viewStatisticsButton = $('button#web-page-usage-view'); viewStatisticsButton.click(async function () { dialog_ol.children().remove(); let list = await getList(); console.log(log_key, 'list', list, 'type', typeof list); let space = 0; for (let index = 0; index < list.length; index++) { let k = list[index]; let realKey=await getRealKey(k); if (realKey !== k) { space+=" merge to "+realKey; continue; } let temp = await getOtherData(k); space += JSON.stringify(temp).length; // console.log(log_key, 'temp', temp); let domain_item = temp.statistics.domain; let ol_div = $("
"); let ol_div_ol = $("
    "); let timeData = temp.statistics['data']; if (timeData == null) { continue; } let timeSum = 0; for (let day = 0; day < timeData.length; day++) { let ol_div_ol_p = $("

    "); let todayTimeData = timeData[day]; let time = todayTimeData['time']; timeSum += time; ol_div_ol_p.text(`${todayTimeData['year']}-${todayTimeData['month']}-${todayTimeData['day']}: ${format(time)}`); ol_div_ol_p.appendTo(ol_div_ol); } let ol_div_legend = $("

    ", {text: domain_item + " " + format(timeSum) + (temp.restrict.limit !== undefined ? " ⌛" + temp.restrict.limit : "")}); ol_div_legend.appendTo(ol_div); ol_div_ol.appendTo(ol_div); /** * * @type {Object} */ let position = null; for (let y = 0; y < dialog_ol.children().length; y++) { let $1 = $(dialog_ol.children().get(y)); if (parseInt($1.attr('data-timeData-sum')) < timeSum) { position = $1; break; } } if (position != null) { position.before(ol_div); } else ol_div.appendTo(dialog_ol); ol_div.attr('data-timeData-sum', timeSum); } if (dialog.dialog == null || typeof dialog.dialog !== "function") { dialog.dialog = function () { console.log(log_key, "启动对话框失败"); } } dialog.dialog({ title: '所有数据:数据占用空间' + space, modal: true, height: document.body.clientHeight / 2, width: document.body.clientWidth / 2, 'minWidth': '300', open: function (event, ui) { }, buttons: { 'close': function () { $(this).dialog("close"); } } }); let elementsByClassName = document.getElementsByClassName('ui-dialog'); for (let i = 0; i < elementsByClassName.length; i++) { if (elementsByClassName[i].innerText.indexOf("所有数据") >= 0) elementsByClassName[i].style.zIndex = dialog_z_index } }); viewStatisticsButton.dblclick(function () { clearInterval(statisticsTimer); }); let bottom = $('
    ', { "id": "web-page-usage-bottom" }); bottom.css({'height': "100px"}); let ol = $("
      ", { id: 'web-page-usage-restrictPanel-ol' }); ol.appendTo(bottom); bottom.appendTo(restrictPanel); printList(); //endregion restrictPanel //region button let button = $('