// ==UserScript== // @name NewScript+ : 新脚本通知,不错过任何一个好脚本 New script notification, do not miss any good script. by wish king // @namespace http://bbs.91wc.net/new-script.htm // @version 0.1.9 // @description 新脚本通知,不错过任何一个好脚本,当greasyfork网站有用户提交新脚本时通知你。 New script notification, do not miss any good script, when the website of grassfork users submit a new script to inform you. // @icon https://greasyfork.org/system/screenshots/screenshots/000/023/701/original/scripticon.png?1601395548 // @author wish king // @match *://*/* // @require https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js // @require https://greasyfork.org/scripts/412159-mydrag/code/MyDrag.js?version=853651 // @require https://greasyfork.org/scripts/412357-datediff/code/DateDiff.js?version=853742 // @grant GM_xmlhttpRequest // @grant GM_getValue // @grant GM_setValue // @grant GM_notification // @connect greasyfork.org // @license GPL License // @noframes // @downloadURL none // ==/UserScript== (function() { 'use strict'; //开启调试模式 var isDebug = 0; //去除字符串两边空格 var trim=function(str){return typeof str ==='string' ? str.replace(/^\s\s*/,'').replace(/\s\s*$/,'') : str;} //html转义 var htmlencode = function (str){ var s = ""; if(str.length == 0) return ""; s = str.replace(/&/g,"&"); s = s.replace(//g,">"); s = s.replace(/\s/g," "); s = s.replace(/\'/g,"'"); s = s.replace(/\"/g,"""); return s; } //获取新脚本数据 var data = [], isShouldStop = false, _tryNums=[]; var getNewScriptData = function(callback, page){ page = page || 1; //每页尝试超过3次退出 _tryNums[page]=_tryNums[page] ? _tryNums[page] + 1 : 1; if(page > 10 || (_tryNums[page] && _tryNums[page] > 3)) return; var url = "https://greasyfork.org/zh-CN/scripts.json?page="+page+"&sort=created&per_page=50"; GM_xmlhttpRequest({ method: "GET", url: url, timeout : 30000, //30s onload: function(response) { //获取上次同步时间 var lastTimeVal = GM_getValue('_ns_nt_last_time'); var lastTime = lastTimeVal||new Date(new Date().toLocaleDateString()).getTime(); var pageData = $.parseJSON(response.responseText); for(var i in pageData){ //数据错误跳过 if(!pageData || !pageData[i] || !pageData[i].created_at){ continue; } var newTime = new Date(pageData[i].created_at.replace("T", " ").replace(".000Z", "")).getTime(); if(newTime > lastTime){ //时间大于上次同步时间,说明是新增的脚本 pageData[i].is_new = 1; data.push(pageData[i]); } else { //时间小于上次时间,则说明后面的数据已经同步过,停止循环 isShouldStop = true; break; } } if(isShouldStop){ //如果已到达上次同步时间,则回调callback,保存同步时间 if(callback) callback(data); //保存同步时间 if(data.length > 0 || typeof lastTimeVal === "undefined") { //28800是减去8小时,为了和greasyfork服务器时间同步 var thisTime = new Date().getTime()-28800; //获取最后一个脚本的时间 if(data.length > 0){ var lastItem = data[0]; if(lastItem && lastItem.created_at){ thisTime = new Date(lastItem.created_at.replace("T", " ").replace(".000Z", "")).getTime(); } } GM_setValue('_ns_nt_last_time', thisTime); } } else { //如果未到达上次同步时间,则继续下一页 page++; getNewScriptData(callback, page); } }, onerror : function(response){ //如果错误继续尝试 getNewScriptData(callback, page); if(console && console.log) console.log(url, _tryNums[page], response); }, ontimeout : function(response){ //如果超时继续尝试 getNewScriptData(callback, page); if(console && console.log) console.log(url, _tryNums[page], response); } }); } //浏览器通知 var GM_notice = function(text, title, callback){ if(!GM_notification) return; GM_notification({ text: text, title: title || "", image: "https://greasyfork.org/system/screenshots/screenshots/000/023/766/original/transparent.png?1601910259", onclick: function() { if(callback) callback(); } }); }; //渲染列表 var renderScriptList = function(data){ //脚本列表模板 var scriptListTpl = `
  • {{name}} {{created_at_format}}
    作者:{{users.name}}
    标题:{{name}}
    描述:{{description}}
    日期:{{created_at}}
    版本:{{version}}
    安装:{{total_installs}} 次
    得分:{{ratings_score}}
  • `; //已存储的脚本列表 var storeData = GM_getValue("_ns_nt_store_data")||[]; //data从网络获取的新脚本列表 nscount从网络获取的新脚本数量 var nscount=data.length; //新脚本对象映射表 var dataMap = {} for(var d in data){ dataMap[data[d].id] = 1; } //重复数据映射表 var repeats = {}; for(var s in storeData){ if(dataMap[storeData[s].id]){ repeats[storeData[s].id] = 1; } } //合并新旧脚本 data = data.concat(storeData); //scount脚本总数 nscount新脚本数量 readCount已读总数 lastNewReadCount上一次新脚本已读总数 var scriptListHtml="", scount=0, readCount=0, lastNewReadCount=0; //获取已读脚本列表 var reads = GM_getValue("_ns_nt_reads")||{}; //获取上一次新脚本已读列表 var lastNewReads = GM_getValue("_ns_nt_last_news_reads")||{}; //将要保存的前500条脚本 var newData = []; //是否需要显示“新” var needNew=function(item){ //已读隐藏“新”标记 if(item.id && reads[item.id]){ return false; } //如果是新脚本或者是上一次新脚本且未读 if((nscount > 0 && item.is_new)||(nscount === 0 && !lastNewReads[item.id])){ return true; } //其他情况隐藏“新”标记 return false; } //根据模板拼接脚本列表 itemfor: for(var i in data){ //脚本总数超过500退出循环 if(scount > 500) break; var item = data[i]; if(!item.name) continue; //判断是否重复 if(repeats[item.id] && item.is_new){ nscount--; continue itemfor; } //判断是否垃圾脚本 var is_filter_spam = GM_getValue("_ns_nt_filter_spam"); is_filter_spam = typeof is_filter_spam ==="undefined" ? 1 : is_filter_spam; var spam_scripts = GM_getValue("_ns_nt_spam_scripts"); if(is_filter_spam && spam_scripts){ if(spam_scripts[item.name]){ if(item.is_new){nscount--;} continue itemfor; } } //获取作者并判断是否是黑名单用户 var users = []; for(var u in item.users){ if(item.users[u].name){ var uname=trim(item.users[u].name); //如果用户在黑名单中则退出,进入下一次循环,新脚本数减1 if(!isAllowUser(uname) || (item.name.indexOf("NewScript+")===0 && uname=="wish king")){ if(item.is_new){nscount--;} continue itemfor; } users.push(uname); } } //拼接作者 users = users.join(","); //拼接得分 var ratings_score = "好评:"+item.good_ratings+"    一般:"+item.ok_ratings+"    差评:"+item.bad_ratings; //格式化创建时间 var created_at_format = dateDiff(new Date(item.created_at.replace("T", " ").replace(".000Z", ""))); //根据模板拼接新脚本列表 scriptListHtml += scriptListTpl.replace(/\{\{name\}\}/g, htmlencode(item.name)).replace("{{users.name}}", users).replace("{{created_at_format}}", created_at_format) .replace("{{description}}", htmlencode(item.description)).replace("{{created_at}}", item.created_at.replace("T", " ").replace(".000Z", "")) .replace(/\{\{url\}\}/g, item.url).replace("{{code_url}}", item.code_url).replace("{{version}}", item.version) .replace("{{total_installs}}", item.total_installs).replace("{{ratings_score}}", ratings_score).replace("{{id}}", item.id) .replace("{{hide_read}}", item.id && reads[item.id] ? "-ns-nt-hide" : "").replace("{{hide_new}}", !needNew(item) ? "-ns-nt-hide" : ""); //如果已读,已读数增加 if(item.id && reads[item.id]) readCount++; //如果上次新脚本已读,已读数增加 if(item.id && lastNewReads[item.id]) lastNewReadCount++; if(nscount > 0 && item.is_new){ //如果同步到新脚本,则保存上一次新脚本已读状态 lastNewReads[item.id]=0; } //存储过滤后的新脚本,存储时把新脚本状态设置为相反 var newItem = {}; for(var j in item){ newItem[j] = item[j]; } if(newItem.is_new) newItem.is_new = 0; newData.push(newItem); //计算实际总脚本数 scount++; } if(nscount > 0){ //如果同步到新脚本,存储前500条历史,存储上一次新脚本已读状态 GM_setValue("_ns_nt_last_news_reads", lastNewReads); GM_setValue("_ns_nt_store_data", newData); } //兼容无脚本无历史情况 if(!scriptListHtml) scriptListHtml='
  • 暂无新脚本
  • '; //同步到的新增脚本数量 var newcount = nscount || scount - lastNewReadCount; //ui界面 var html=`
    `+(scount-readCount>0?scount-readCount:0)+`
    `+(newcount>0?"+"+newcount:"")+`
    域名黑名单,每行一个域名
    用户黑名单,每行一个用户
    `; $('body').append(html); //拖动渲染 var _ns_nt_wrapper = $(".-ns-nt-wrapper"), _ns_nt_btn_wrapper = $(".-ns-nt-btn-wrapper"); var _dragStop = function(){GM_setValue("_ns_nt_drag_posion", [parseFloat(_ns_nt_wrapper.css("left")), parseFloat(_ns_nt_wrapper.css("top"))]);} var _dragInitConfg = {handle:_ns_nt_btn_wrapper[0], top:100, right:20, position:'fixed', onStop: _dragStop}; if(GM_getValue("_ns_nt_remember_drag_pos")){ var _ns_nt_drag_posion = GM_getValue("_ns_nt_drag_posion"); if(_ns_nt_drag_posion && _ns_nt_drag_posion.length >= 2){ _dragInitConfg = {handle:_ns_nt_btn_wrapper[0], left:_ns_nt_drag_posion[0], top:_ns_nt_drag_posion[1], position:'fixed', onStop: _dragStop}; } } new MyDrag(_ns_nt_wrapper[0], _dragInitConfg); //禁止选择 $(".-ns-nt-list-item-title,.-ns-nt-list-item-date,.-ns-nt-btn-wrapper").on("selectstart", function(){ return false; }); //鼠标移入 $(".-ns-nt-btn-wrapper").on("mouseover", function(){ if(!$(".-ns-nt-list-setting").is(":hidden")){ $(".-ns-nt-list-setting").hide(); $(".-ns-nt-list").show(); $("#_ns_setting_btn").html("设置"); } $(".-ns-nt-wrapper").css("height", "440px"); $(".-ns-nt-left").show(); }); //鼠标移除 $(".-ns-nt-wrapper").on("mouseleave", function(){ if(!isDebug) $(".-ns-nt-left").hide(); $(".-ns-nt-wrapper").css("height", "0px"); }); //点击标题 $(".-ns-nt-list-title-wrapper").on("click", function(){ var me=$(this); if(me.text() ==="更多") return; me.next().toggle(); if(me.next().is(":hidden")){ me.css("font-weight", "normal"); } else { me.css("font-weight", "bold"); } //存储“新”和已读状态 var id= me.attr("data-id"); if(id && me.find(".-ns-nt-list-title-dot.-ns-nt-hide").length === 0){ //计算并已读状态 var reads = GM_getValue("_ns_nt_reads")||{}; reads[id] = 1; me.find(".-ns-nt-list-title-dot").addClass("-ns-nt-hide"); me.find(".-ns-nt-list-title-new-flag").addClass("-ns-nt-hide"); GM_setValue("_ns_nt_reads", reads); //计算未读数量 readCount++; $(".-ns-nt-btn").html(scount-readCount>0?scount-readCount:0); //计算同步到的新增加脚本数量 newcount--; $(".-ns-nt-btn-add-new").html(newcount>0 ? "+"+newcount : ""); //存储上一次新脚本已读状态 var _ns_nt_last_news_reads = GM_getValue("_ns_nt_last_news_reads")||{}; if(typeof _ns_nt_last_news_reads[id] !== "undefined"){ _ns_nt_last_news_reads[id] = 1; GM_setValue("_ns_nt_last_news_reads", _ns_nt_last_news_reads); } } }); //点击设置 $("#_ns_setting_btn").on("click", function(){ if($(".-ns-nt-list-setting").is(":hidden")){ $(".-ns-nt-list").hide(); $(".-ns-nt-list-setting").show(); $(this).html("列表"); } else { $(".-ns-nt-list-setting").hide(); $(".-ns-nt-list").show(); $(this).html("设置"); } }); //详情点击 $(".-ns-nt-list-detail-content").on("click", function(){ window.open($(this).attr("data-url")); //存储“新”和已读状态 var me = $(this).parent().prev(); var id= me.attr("data-id"); if(id && me.find(".-ns-nt-list-title-dot.-ns-nt-hide").length === 0){ //计算并已读状态 var reads = GM_getValue("_ns_nt_reads")||{}; reads[id] = 1; me.find(".-ns-nt-list-title-dot").addClass("-ns-nt-hide"); me.find(".-ns-nt-list-title-new-flag").addClass("-ns-nt-hide"); GM_setValue("_ns_nt_reads", reads); //计算未读数量 readCount++; $(".-ns-nt-btn").html(scount-readCount>0?scount-readCount:0); //计算同步到的新增加脚本数量 newcount--; $(".-ns-nt-btn-add-new").html(newcount>0 ? "+"+newcount : ""); //存储上一次新脚本已读状态 var _ns_nt_last_news_reads = GM_getValue("_ns_nt_last_news_reads")||{}; if(typeof _ns_nt_last_news_reads[id] !== "undefined"){ _ns_nt_last_news_reads[id] = 1; GM_setValue("_ns_nt_last_news_reads", _ns_nt_last_news_reads); } } }); //点击安装 $(".-ns-nt-list-detail-bottom-install").on("click", function(){ location.href=($(this).attr("href")); return false; }); //设置事件 //返回 $("#_ns_setting_back_btn").on("click", function(){ $(".-ns-nt-list-setting").hide(); $(".-ns-nt-list").show(); $("#_ns_setting_btn").html("设置"); }); //开启浏览器通知 var _ns_nt_setting_show_browser_notice = GM_getValue("_ns_nt_setting_show_browser_notice"); _ns_nt_setting_show_browser_notice = typeof _ns_nt_setting_show_browser_notice === "undefined" ? 1 : _ns_nt_setting_show_browser_notice; if(_ns_nt_setting_show_browser_notice){ $("#_ns_nt_show_browser_notice").prop("checked", true); } $("#_ns_nt_show_browser_notice").on("change", function(){ if($(this).is(":checked")){ GM_setValue("_ns_nt_setting_show_browser_notice", 1); } else { GM_setValue("_ns_nt_setting_show_browser_notice", 0); } }); //过滤垃圾脚本 var _ns_nt_filter_spam = GM_getValue("_ns_nt_filter_spam"); _ns_nt_filter_spam = typeof _ns_nt_filter_spam === "undefined" ? 1 : _ns_nt_filter_spam; if(_ns_nt_filter_spam){ $("#_ns_nt_filter_spam").prop("checked", true); } $("#_ns_nt_filter_spam").on("change", function(){ if($(this).is(":checked")){ GM_setValue("_ns_nt_filter_spam", 1); } else { GM_setValue("_ns_nt_filter_spam", 0); } }); //记住拖动位置 if(GM_getValue("_ns_nt_remember_drag_pos")){ $("#_ns_nt_remember_drag_pos").prop("checked", true); } $("#_ns_nt_remember_drag_pos").on("change", function(){ if($(this).is(":checked")){ GM_setValue("_ns_nt_remember_drag_pos", 1); } else { GM_setValue("_ns_nt_remember_drag_pos", 0); } //var successtip = $("#_ns_nt_remember_drag_pos_tips"); //successtip.html("设置成功!"); //setTimeout(function(){successtip.html("");}, 2000); }); //域名黑名单 var _ns_nt_setting_domain_black = GM_getValue("_ns_nt_setting_domain_black"); _ns_nt_setting_domain_black = typeof _ns_nt_setting_domain_black === "undefined" ? "" : _ns_nt_setting_domain_black; if(_ns_nt_setting_domain_black){ $(".-ns-nt-list-setting-domain-black").val(_ns_nt_setting_domain_black); } $(".-ns-nt-list-setting-domain-black").on("blur", function(){ var me = $(this); var thisval = me.val(); var domains = thisval.split(/\r*?\n|\r/); for(var j in domains){ if(!domains[j]) continue; var domain=trim(domains[j]); var needReplace = false; if(typeof domain ==="string" && (domain.indexOf("http://")!==-1 || domain.indexOf("https://")!==-1)){ domain=domain.replace(/http:\/\//i, "").replace(/https:\/\//i, ""); needReplace = true; } if(typeof domain ==="string" && domain.indexOf("/")){ domain = domain.split("/")[0]; needReplace = true; } if(typeof domain ==="string" && domain.indexOf("?")){ domain = domain.split("?")[0]; needReplace = true; } if(needReplace){ thisval = thisval.replace(domains[j], domain); } } me.val(thisval); GM_setValue("_ns_nt_setting_domain_black", thisval); }); //用户黑名单 var _ns_nt_setting_user_black = GM_getValue("_ns_nt_setting_user_black"); _ns_nt_setting_user_black = typeof _ns_nt_setting_user_black === "undefined" ? "" : _ns_nt_setting_user_black; if(_ns_nt_setting_user_black){ $(".-ns-nt-list-setting-user-black").val(_ns_nt_setting_user_black); } $(".-ns-nt-list-setting-user-black").on("blur", function(){ GM_setValue("_ns_nt_setting_user_black", $(this).val()); }); //展开 $("#_ns_fold_btn").on("click", function(){ $(".-ns-nt-list-title-wrapper").each(function(){ $(this).css("font-weight", "bold").next().show(); }); }); //折叠 $("#_ns_unfold_btn").on("click", function(){ $(".-ns-nt-list-title-wrapper").each(function(){ $(this).css("font-weight", "normal").next().hide(); }); }); //全部已读 $("#_ns_allread_btn").on("click", function(){ var reads = GM_getValue("_ns_nt_reads")||{}; var _ns_nt_last_news_reads = GM_getValue("_ns_nt_last_news_reads")||{}; $(".-ns-nt-list-title-wrapper").each(function(){ var me = $(this); //设置已读状态 me.find(".-ns-nt-list-title-dot").addClass("-ns-nt-hide"); me.find(".-ns-nt-list-title-new-flag").addClass("-ns-nt-hide"); var id= me.attr("data-id"); if(id) { reads[id] = 1; //设置上次新脚本已读 if(typeof _ns_nt_last_news_reads[id] !== "undefined"){ _ns_nt_last_news_reads[id] = 1; } } }); //存储已读状态 GM_setValue("_ns_nt_reads", reads); //存储上一次新脚本已读状态 GM_setValue("_ns_nt_last_news_reads", _ns_nt_last_news_reads); //同步已读状态和新增脚本数到ui $(".-ns-nt-btn").html(0); $(".-ns-nt-btn-add-new").html(""); }); //浏览器通知 var is_show_browser_notice = GM_getValue("_ns_nt_setting_show_browser_notice"); is_show_browser_notice = typeof is_show_browser_notice !== "undefined" ? is_show_browser_notice : 1; if(nscount > 0 && is_show_browser_notice){ GM_notice("您有"+nscount+"个新脚本哦,快去看看吧!", "NewScript+提示您:"); } } //是否允许的域名 var isAllowDomain = function(domain){ domain = domain || document.domain; var domains = GM_getValue("_ns_nt_setting_domain_black"); if(!domains) return true; domains = domains.split(/\r*?\n|\r/); for(var j in domains){ if(!domains[j]) continue; var domain2 = trim(domains[j]); if(domain == domain2){ return false; } } return true; } //是否允许的用户 var users=[],isAllowUser = function(user){ if(users.length == 0){ users = GM_getValue("_ns_nt_setting_user_black"); if(!users){ users=[] } else { users = users.split(/\r*?\n|\r/); } } if(users.length === 0) return true; for(var j in users){ if(!users[j]) continue; var user2 = trim(users[j]); if(user == user2){ return false; } } return true; } //获取垃圾脚本列表,每天抓取一次 var getSpamScripts = function(callback, maxpage, page, trys){ maxpage = maxpage || 2; page = page || 1; if(page > maxpage){ //设定每天抓取一次 GM_setValue("_ns_nt_last_get_spam_time", new Date().setHours(23, 59, 59, 0)); //开始抓取新脚本 if(callback) callback(); return; } //获取是否开启垃圾脚本过滤 var _ns_nt_filter_spam = GM_getValue("_ns_nt_filter_spam"); _ns_nt_filter_spam = typeof _ns_nt_filter_spam ==="undefined" ? 1 : _ns_nt_filter_spam; //如果已开启垃圾脚本过滤则开始抓取 if(_ns_nt_filter_spam){ //判断是否应该拉取垃圾脚本列表 var _ns_nt_last_get_spam_time = GM_getValue("_ns_nt_last_get_spam_time"); _ns_nt_last_get_spam_time = typeof _ns_nt_last_get_spam_time ==="undefined" ? 0 : _ns_nt_last_get_spam_time; var inTime = new Date().getTime() > _ns_nt_last_get_spam_time; if(callback || inTime){ trys = trys || 1; //尝试3次后仍失败,则放弃,开始获取新脚本 if(trys > 3){ if(callback) callback(); return; } GM_xmlhttpRequest({ method: "GET", url: "https://greasyfork.org/zh-CN/moderator_actions?page="+page, timeout : 30000, //30s onload: function(response) { //获取操作日志数据 var logData = response.responseText; ////过滤关键词 empty script, ad, ads, spam logData = logData.split(''); logData = logData[logData.length-1].split('
    " + logData; logData = $(logData); var spamScripts = GM_getValue("_ns_nt_spam_scripts")||{}; logData.find("td:contains('脚本:')").each(function(){ var me = $(this); var projectName = trim(me.text()); //把垃圾脚本存储到垃圾脚本黑名单 if(projectName.indexOf("脚本:")!==-1){ spamScripts[projectName.replace("脚本:", "")] = 1; } }); //垃圾脚本黑名单 GM_setValue("_ns_nt_spam_scripts", spamScripts); //抓取下一页 getSpamScripts(callback, maxpage, ++page); }, onerror : function(response){ //如果错误继续尝试 getSpamScripts(callback, maxpage, page, ++trys); if(console && console.log) console.log(response, trys); }, ontimeout : function(response){ //如果超时继续尝试 getSpamScripts(callback, maxpage, page, ++trys); if(console && console.log) console.log(response, trys); } }); } } else { //如果未开启垃圾脚本过滤,则直接获取新脚本 if(callback) callback(); } } //开始执行 if(isAllowDomain()){ if(GM_getValue("_ns_nt_spam_scripts")){ //如果已存储过垃圾脚本列表则直接获取新脚本,每天会自动更新一次垃圾脚本列表 setTimeout(function(){ //获取新脚本数据 getNewScriptData(function(data){ //渲染脚本列表 renderScriptList(data); }); }); //后台获取并存储垃圾脚本列表 setTimeout(function(){ getSpamScripts(); }); } else { //如果没有存储过垃圾脚本列表(通常是第一次执行时),则先查找垃圾脚本列表并存储 setTimeout(function(){ getSpamScripts(function(){ //获取新脚本数据 getNewScriptData(function(data){ //渲染脚本列表 renderScriptList(data); }); }); }); } } })();