Repository: yxgsir/yichahucha Branch: master Commit: 4caf44244b4d Files: 10 Total size: 83.8 KB Directory structure: gitextract_k3v5eieh/ ├── README.md ├── check_in.js ├── jd_price.js ├── jd_price_lite.js ├── nf_rating.js ├── tb_price.js ├── tb_price_lite.js ├── tool.js ├── wb_ad.js └── wb_launch.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: README.md ================================================ # Surge Remove weibo ads ``` [Script] http-response ^https?://m?api\.weibo\.c(n|om)/2/(statuses/(unread|extend|positives/get|(friends|video)(/|_)timeline)|stories/(video_stream|home_list)|(groups|fangle)/timeline|profile/statuses|comments/build_comments|photo/recommend_list|service/picfeed|searchall|cardlist|page|\!/photos/pic_recommend_status) requires-body=1,script-path=https://raw.githubusercontent.com/yichahucha/surge/master/wb_ad.js http-response ^https?://(sdk|wb)app\.uve\.weibo\.com(/interface/sdk/sdkad.php|/wbapplua/wbpullad.lua) requires-body=1,script-path=https://raw.githubusercontent.com/yichahucha/surge/master/wb_launch.js [MITM] hostname = api.weibo.cn, mapi.weibo.com, *.uve.weibo.com ``` Display netflix ratings(IMDb、douaban) ``` [Script] http-request ^https?://ios\.prod\.ftl\.netflix\.com/iosui/user/.+path=%5B%22videos%22%2C%\d+%22%2C%22summary%22%5D script-path=https://raw.githubusercontent.com/yichahucha/surge/master/nf_rating.js http-response ^https?://ios\.prod\.ftl\.netflix\.com/iosui/user/.+path=%5B%22videos%22%2C%\d+%22%2C%22summary%22%5D requires-body=1,script-path=https://raw.githubusercontent.com/yichahucha/surge/master/nf_rating.js [MITM] hostname = ios.prod.ftl.netflix.com ``` Display jd historical price ``` # 使用不生效或失效的检查一下配置有没有这两条复写,如果有删除试试 # ^https?:\/\/api\.m\.jd.com\/client\.action\?functionId=start - reject # ^https?:\/\/api\.m\.jd.com\/client\.action\?functionId=(start|queryMaterialAdverts) - reject [Script] http-response ^https?://api\.m\.jd\.com/client\.action\?functionId=(wareBusiness|serverConfig) requires-body=1,script-path=https://raw.githubusercontent.com/yichahucha/surge/master/jd_price.js [MITM] hostname = api.m.jd.com ``` Display taobao historical price(提供两种方式,根据需求二选一) ``` # 使用脚本删除对应 IP,不生效或失效的需要卸载 tb 重装,注意不开脚本进 tb 会失效 [Script] http-response ^https?://amdc\.m\.taobao\.com/amdc/mobileDispatch requires-body=1,script-path=https://raw.githubusercontent.com/yichahucha/surge/master/tb_price.js http-response ^https://trade-acs\.m\.taobao\.com/gw/mtop\.taobao\.detail\.getdetail requires-body=1,script-path=https://raw.githubusercontent.com/yichahucha/surge/master/tb_price.js [MITM] hostname = trade-acs.m.taobao.com,amdc.m.taobao.com ``` ``` # 使用规则屏蔽指定 IP 段,有可能误伤其他功能或者应用,可以自己抓包缩小 IP 范围,随时生效 [Rule] IP-CIDR, 203.119.144.0/23, REJECT, no-resolve IP-CIDR, 203.119.175.0/24, REJECT, no-resolve IP-CIDR, 106.11.162.0/24, REJECT, no-resolve IP-CIDR, 47.102.83.0/24, REJECT, no-resolve [Script] http-response ^https://trade-acs\.m\.taobao\.com/gw/mtop\.taobao\.detail\.getdetail requires-body=1,script-path=https://raw.githubusercontent.com/yichahucha/surge/master/tb_price.js [MITM] hostname = trade-acs.m.taobao.com,amdc.m.taobao.com ``` Daily work check-in reminder ``` [Script] cron "0 9,18 * * 1-5" script-path=https://raw.githubusercontent.com/yichahucha/surge/master/check_in.js ``` # Quan-X Remove weibo ads ``` [rewrite_local] ^https?://m?api\.weibo\.c(n|om)/2/(statuses/(unread|extend|positives/get|(friends|video)(/|_)timeline)|stories/(video_stream|home_list)|(groups|fangle)/timeline|profile/statuses|comments/build_comments|photo/recommend_list|service/picfeed|searchall|cardlist|page|\!/photos/pic_recommend_status) url script-response-body wb_ad.js ^https?://(sdk|wb)app\.uve\.weibo\.com(/interface/sdk/sdkad.php|/wbapplua/wbpullad.lua) url script-response-body wb_launch.js [mitm] hostname = api.weibo.cn, mapi.weibo.com, *.uve.weibo.com ``` Display netflix ratings(IMDb、douaban) ``` [rewrite_local] ^https?://ios\.prod\.ftl\.netflix\.com/iosui/user/.+path=%5B%22videos%22%2C%\d+%22%2C%22summary%22%5D url script-request-header nf_rating.js ^https?://ios\.prod\.ftl\.netflix\.com/iosui/user/.+path=%5B%22videos%22%2C%\d+%22%2C%22summary%22%5D url script-response-body nf_rating.js [mitm] hostname = ios.prod.ftl.netflix.com ``` Display jd historical price ``` [rewrite_local] ^https?://api\.m\.jd\.com/client\.action\?functionId=(wareBusiness|serverConfig) url script-response-body jd_price.js [mitm] hostname = api.m.jd.com ``` Display taobao historical price(提供两种方式,根据需求二选一) ``` # 使用脚本删除对应 IP,不生效或失效的需要卸载 tb 重装,注意不开脚本进 tb 会失效 [rewrite_local] ^https?://amdc\.m\.taobao\.com/amdc/mobileDispatch url script-response-body tb_price.js ^https://trade-acs\.m\.taobao\.com/gw/mtop\.taobao\.detail\.getdetail url script-response-body tb_price.js [mitm] hostname = trade-acs.m.taobao.com,amdc.m.taobao.com ``` ``` # 使用规则屏蔽指定 IP 段,有可能误伤其他功能或者应用,可以自己抓包缩小 IP 范围,随时生效 [filter_local] ip-cidr, 203.119.144.0/23, reject ip-cidr, 203.119.175.0/24, reject ip-cidr, 106.11.162.0/24, reject ip-cidr, 47.102.83.0/24, reject [rewrite_local] ^https://trade-acs\.m\.taobao\.com/gw/mtop\.taobao\.detail\.getdetail url script-response-body tb_price.js [mitm] hostname = trade-acs.m.taobao.com,amdc.m.taobao.com ``` Daily work check-in reminder ``` [task_local] 0 9,18 * * 1-5 check_in.js ``` [Issue Group](http://t.me/scriptgroup) ================================================ FILE: check_in.js ================================================ /* README:https://github.com/yichahucha/surge/tree/master 每日打卡提醒(corn "0 9,18 * * 1-5" 周一到周五,早九晚六)+ 每日壹句(有道词典)+ 跳转钉钉打卡页面(下拉通知点击链接) */ const $tool = new Tool() $tool.get('https://dict.youdao.com/infoline/style/cardList?mode=publish&client=mobile&style=daily&size=2', function (error, response, data) { let obj = JSON.parse(data); let date = new Date(); let isAM = date.getHours() < 12 ? true : false; let title = 'Clock' + (isAM ? ' in' : ' out') + (isAM ? ' ☀️' : ' 🌙'); let subtitle = ''; let content = 'dingtalk://dingtalkclient/page/link?url=https://attend.dingtalk.com/attend/index.html'; if (!error) { if (obj && obj.length > 1) { let yi = obj[1]; content = yi.title + '\n' + yi.summary + '\n\n' + content; } } $tool.notify(title, subtitle, content); $done(); }) function Tool() { _node = (() => { if (typeof require == "function") { const request = require('request') return ({ request }) } else { return (null) } })() _isSurge = typeof $httpClient != "undefined" _isQuanX = typeof $task != "undefined" this.isSurge = _isSurge this.isQuanX = _isQuanX this.isResponse = typeof $response != "undefined" this.notify = (title, subtitle, message) => { if (_isQuanX) $notify(title, subtitle, message) if (_isSurge) $notification.post(title, subtitle, message) if (_node) console.log(JSON.stringify({ title, subtitle, message })); } this.write = (value, key) => { if (_isQuanX) return $prefs.setValueForKey(value, key) if (_isSurge) return $persistentStore.write(value, key) } this.read = (key) => { if (_isQuanX) return $prefs.valueForKey(key) if (_isSurge) return $persistentStore.read(key) } this.get = (options, callback) => { if (_isQuanX) { if (typeof options == "string") options = { url: options } options["method"] = "GET" $task.fetch(options).then(response => { callback(null, _status(response), response.body) }, reason => callback(reason.error, null, null)) } if (_isSurge) $httpClient.get(options, (error, response, body) => { callback(error, _status(response), body) }) if (_node) _node.request(options, (error, response, body) => { callback(error, _status(response), body) }) } this.post = (options, callback) => { if (_isQuanX) { if (typeof options == "string") options = { url: options } options["method"] = "POST" $task.fetch(options).then(response => { callback(null, _status(response), response.body) }, reason => callback(reason.error, null, null)) } if (_isSurge) $httpClient.post(options, (error, response, body) => { callback(error, _status(response), body) }) if (_node) _node.request.post(options, (error, response, body) => { callback(error, _status(response), body) }) } _status = (response) => { if (response) { if (response.status) { response["statusCode"] = response.status } else if (response.statusCode) { response["status"] = response.statusCode } } return response } } ================================================ FILE: jd_price.js ================================================ /* README:https://github.com/yichahucha/surge/tree/master */ const path1 = "serverConfig"; const path2 = "wareBusiness"; const consolelog = false; const url = $request.url; const body = $response.body; const $tool = tool(); if (url.indexOf(path1) != -1) { let obj = JSON.parse(body); delete obj.serverConfig.httpdns; $done({ body: JSON.stringify(obj) }); } if (url.indexOf(path2) != -1) { let obj = JSON.parse(body); const floors = obj.floors; const commodity_info = floors[floors.length - 1]; const shareUrl = commodity_info.data.property.shareUrl; request_history_price(shareUrl, function (data) { if (data) { const lowerword = adword_obj(); lowerword.data.ad.textColor = "#fe0000"; let bestIndex = 0; for (let index = 0; index < floors.length; index++) { const element = floors[index]; if (element.mId == lowerword.mId) { bestIndex = index + 1; break; } else { if (element.sortId > lowerword.sortId) { bestIndex = index; break; } } } if (data.ok == 1 && data.single) { const lower = lowerMsgs(data.single) const detail = priceSummary(data) const tip = data.PriceRemark.Tip + "(仅供参考)" lowerword.data.ad.adword = `${lower} ${tip}\n${detail}`; floors.insert(bestIndex, lowerword); } if (data.ok == 0 && data.msg.length > 0) { lowerword.data.ad.adword = "⚠️ " + data.msg; floors.insert(bestIndex, lowerword); } $done({ body: JSON.stringify(obj) }); } else { $done({ body }); } }) } function lowerMsgs(data) { const lower = data.lowerPriceyh const lowerDate = dateFormat(data.lowerDateyh) const lowerMsg = "〽️历史最低到手价:¥" + String(lower) + ` (${lowerDate}) ` return lowerMsg } function priceSummary(data) { let summary = "" let listPriceDetail = data.PriceRemark.ListPriceDetail listPriceDetail.pop() let list = listPriceDetail.concat(historySummary(data.single)) list.forEach((item, index) => { if (index == 2) { item.Name = "双十一价格" } else if (index == 3) { item.Name = "六一八价格" } else if (index == 4) { item.Name = "三十天最低" } summary += `\n${item.Name}${getSpace(8)}${item.Price}${getSpace(8)}${item.Date}${getSpace(8)}${item.Difference}` }) return summary } function historySummary(single) { const rexMatch = /\[.*?\]/g; const rexExec = /\[(.*),(.*),"(.*)"\]/; let currentPrice, lowest60, lowest180, lowest360 let list = single.jiagequshiyh.match(rexMatch); list = list.reverse().slice(0, 360); list.forEach((item, index) => { if (item.length > 0) { const result = rexExec.exec(item); const dateUTC = new Date(eval(result[1])); const date = dateUTC.format("yyyy-MM-dd"); let price = parseFloat(result[2]); if (index == 0) { currentPrice = price lowest60 = { Name: "六十天最低", Price: `¥${String(price)}`, Date: date, Difference: difference(currentPrice, price), price } lowest180 = { Name: "一百八最低", Price: `¥${String(price)}`, Date: date, Difference: difference(currentPrice, price), price } lowest360 = { Name: "三百六最低", Price: `¥${String(price)}`, Date: date, Difference: difference(currentPrice, price), price } } if (index < 60 && price <= lowest60.price) { lowest60.price = price lowest60.Price = `¥${String(price)}` lowest60.Date = date lowest60.Difference = difference(currentPrice, price) } if (index < 180 && price <= lowest180.price) { lowest180.price = price lowest180.Price = `¥${String(price)}` lowest180.Date = date lowest180.Difference = difference(currentPrice, price) } if (index < 360 && price <= lowest360.price) { lowest360.price = price lowest360.Price = `¥${String(price)}` lowest360.Date = date lowest360.Difference = difference(currentPrice, price) } } }); return [lowest60, lowest180, lowest360]; } function difference(currentPrice, price) { let difference = strip(currentPrice - price) if (difference == 0) { return "-" } else { return `${difference > 0 ? "↑" : "↓"}${String(difference)}` } } function strip(num, precision = 12) { return +parseFloat(num.toPrecision(precision)); } function request_history_price(share_url, callback) { const options = { url: "https://apapia-history.manmanbuy.com/ChromeWidgetServices/WidgetServices.ashx", headers: { "Content-Type": "application/x-www-form-urlencoded;charset=utf-8", "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_1_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 - mmbWebBrowse - ios" }, body: "methodName=getHistoryTrend&p_url=" + encodeURIComponent(share_url) } $tool.post(options, function (error, response, data) { if (!error) { callback(JSON.parse(data)); if (consolelog) console.log("Data:\n" + data); } else { callback(null, null); if (consolelog) console.log("Error:\n" + error); } }) } function dateFormat(cellval) { const date = new Date(parseInt(cellval.replace("/Date(", "").replace(")/", ""), 10)); const month = date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1; const currentDate = date.getDate() < 10 ? "0" + date.getDate() : date.getDate(); return date.getFullYear() + "-" + month + "-" + currentDate; } function getSpace(length) { let blank = ""; for (let index = 0; index < length; index++) { blank += " "; } return blank; } function adword_obj() { return { "bId": "eCustom_flo_199", "cf": { "bgc": "#ffffff", "spl": "empty" }, "data": { "ad": { "adword": "", "textColor": "#8C8C8C", "color": "#f23030", "newALContent": true, "hasFold": true, "class": "com.jd.app.server.warecoresoa.domain.AdWordInfo.AdWordInfo", "adLinkContent": "", "adLink": "" } }, "mId": "bpAdword", "refId": "eAdword_0000000028", "sortId": 13 } } function tool() { const isSurge = typeof $httpClient != "undefined" const isQuanX = typeof $task != "undefined" const isResponse = typeof $response != "undefined" const node = (() => { if (typeof require == "function") { const request = require('request') return ({ request }) } else { return (null) } })() const notify = (title, subtitle, message) => { if (isQuanX) $notify(title, subtitle, message) if (isSurge) $notification.post(title, subtitle, message) if (node) console.log(JSON.stringify({ title, subtitle, message })); } const write = (value, key) => { if (isQuanX) return $prefs.setValueForKey(value, key) if (isSurge) return $persistentStore.write(value, key) } const read = (key) => { if (isQuanX) return $prefs.valueForKey(key) if (isSurge) return $persistentStore.read(key) } const adapterStatus = (response) => { if (response) { if (response.status) { response["statusCode"] = response.status } else if (response.statusCode) { response["status"] = response.statusCode } } return response } const get = (options, callback) => { if (isQuanX) { if (typeof options == "string") options = { url: options } options["method"] = "GET" $task.fetch(options).then(response => { callback(null, adapterStatus(response), response.body) }, reason => callback(reason.error, null, null)) } if (isSurge) $httpClient.get(options, (error, response, body) => { callback(error, adapterStatus(response), body) }) if (node) { node.request(options, (error, response, body) => { callback(error, adapterStatus(response), body) }) } } const post = (options, callback) => { if (isQuanX) { if (typeof options == "string") options = { url: options } options["method"] = "POST" $task.fetch(options).then(response => { callback(null, adapterStatus(response), response.body) }, reason => callback(reason.error, null, null)) } if (isSurge) { $httpClient.post(options, (error, response, body) => { callback(error, adapterStatus(response), body) }) } if (node) { node.request.post(options, (error, response, body) => { callback(error, adapterStatus(response), body) }) } } return { isQuanX, isSurge, isResponse, notify, write, read, get, post } } Array.prototype.insert = function (index, item) { this.splice(index, 0, item); }; Date.prototype.format = function (fmt) { var o = { "y+": this.getFullYear(), "M+": this.getMonth() + 1, "d+": this.getDate(), "h+": this.getHours(), "m+": this.getMinutes(), "s+": this.getSeconds(), "q+": Math.floor((this.getMonth() + 3) / 3), "S+": this.getMilliseconds() }; for (var k in o) { if (new RegExp("(" + k + ")").test(fmt)) { if (k == "y+") { fmt = fmt.replace(RegExp.$1, ("" + o[k]).substr(4 - RegExp.$1.length)); } else if (k == "S+") { var lens = RegExp.$1.length; lens = lens == 1 ? 3 : lens; fmt = fmt.replace(RegExp.$1, ("00" + o[k]).substr(("" + o[k]).length - 1, lens)); } else { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); } } } return fmt; } ================================================ FILE: jd_price_lite.js ================================================ /* README:https://github.com/yichahucha/surge/tree/master */ const path1 = "serverConfig"; const path2 = "wareBusiness"; const consolelog = false; const url = $request.url; const body = $response.body; const $tool = tool(); if (url.indexOf(path1) != -1) { let obj = JSON.parse(body); delete obj.serverConfig.httpdns; $done({ body: JSON.stringify(obj) }); } if (url.indexOf(path2) != -1) { $done({ body }); let obj = JSON.parse(body); const floors = obj.floors; const commodity_info = floors[floors.length - 1]; const shareUrl = commodity_info.data.property.shareUrl; request_history_price(shareUrl, function (data) { if (data) { if (data.ok == 1 && data.single) { const lower = lowerMsgs(data.single) const detail = priceSummary(data) const tip = data.PriceRemark.Tip + "(仅供参考)" $tool.notify("", "", `${lower} ${tip}\n${detail}\n\n👉查看详情:http://tool.manmanbuy.com/historyLowest.aspx?url=${encodeURI(shareUrl)}`) } if (data.ok == 0 && data.msg.length > 0) { $tool.notify("", "", `⚠️ ${data.msg}`) } } }) } function lowerMsgs(data) { const lower = data.lowerPriceyh const lowerDate = dateFormat(data.lowerDateyh) const lowerMsg = "〽️历史最低到手价:¥" + String(lower) + ` (${lowerDate}) ` return lowerMsg } function priceSummary(data) { let summary = "" let listPriceDetail = data.PriceRemark.ListPriceDetail listPriceDetail.pop() let list = listPriceDetail.concat(historySummary(data.single)) list.forEach((item, index) => { if (index == 2) { item.Name = "双十一价格" } else if (index == 3) { item.Name = "六一八价格" } else if (index == 4) { item.Name = "三十天最低" } summary += `\n${item.Name} ${item.Price} ${item.Date} ${item.Difference}` }) return summary } function historySummary(single) { const rexMatch = /\[.*?\]/g; const rexExec = /\[(.*),(.*),"(.*)"\]/; let currentPrice, lowest60, lowest180, lowest360 let list = single.jiagequshiyh.match(rexMatch); list = list.reverse().slice(0, 360); list.forEach((item, index) => { if (item.length > 0) { const result = rexExec.exec(item); const dateUTC = new Date(eval(result[1])); const date = dateUTC.format("yyyy-MM-dd"); let price = parseFloat(result[2]); if (index == 0) { currentPrice = price lowest60 = { Name: "六十天最低", Price: `¥${String(price)}`, Date: date, Difference: difference(currentPrice, price), price } lowest180 = { Name: "一百八最低", Price: `¥${String(price)}`, Date: date, Difference: difference(currentPrice, price), price } lowest360 = { Name: "三百六最低", Price: `¥${String(price)}`, Date: date, Difference: difference(currentPrice, price), price } } if (index < 60 && price <= lowest60.price) { lowest60.price = price lowest60.Price = `¥${String(price)}` lowest60.Date = date lowest60.Difference = difference(currentPrice, price) } if (index < 180 && price <= lowest180.price) { lowest180.price = price lowest180.Price = `¥${String(price)}` lowest180.Date = date lowest180.Difference = difference(currentPrice, price) } if (index < 360 && price <= lowest360.price) { lowest360.price = price lowest360.Price = `¥${String(price)}` lowest360.Date = date lowest360.Difference = difference(currentPrice, price) } } }); return [lowest60, lowest180, lowest360]; } function difference(currentPrice, price) { let difference = strip(currentPrice - price) if (difference == 0) { return "-" } else { return `${difference > 0 ? "↑" : "↓"}${String(difference)}` } } function strip(num, precision = 12) { return +parseFloat(num.toPrecision(precision)); } function request_history_price(share_url, callback) { const options = { url: "https://apapia-history.manmanbuy.com/ChromeWidgetServices/WidgetServices.ashx", headers: { "Content-Type": "application/x-www-form-urlencoded;charset=utf-8", "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_1_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 - mmbWebBrowse - ios" }, body: "methodName=getHistoryTrend&p_url=" + encodeURIComponent(share_url) } $tool.post(options, function (error, response, data) { if (!error) { callback(JSON.parse(data)); if (consolelog) console.log("Data:\n" + data); } else { callback(null, null); if (consolelog) console.log("Error:\n" + error); } }) } function dateFormat(cellval) { const date = new Date(parseInt(cellval.replace("/Date(", "").replace(")/", ""), 10)); const month = date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1; const currentDate = date.getDate() < 10 ? "0" + date.getDate() : date.getDate(); return date.getFullYear() + "-" + month + "-" + currentDate; } function tool() { const isSurge = typeof $httpClient != "undefined" const isQuanX = typeof $task != "undefined" const node = (() => { if (typeof require == "function") { const request = require('request') return ({ request }) } else { return (null) } })() const notify = (title, subtitle, message) => { if (isQuanX) $notify(title, subtitle, message) if (isSurge) $notification.post(title, subtitle, message) if (node) console.log(JSON.stringify({ title, subtitle, message })); } const setCache = (value, key) => { if (isQuanX) return $prefs.setValueForKey(value, key) if (isSurge) return $persistentStore.write(value, key) } const getCache = (key) => { if (isQuanX) return $prefs.valueForKey(key) if (isSurge) return $persistentStore.read(key) } const adapterStatus = (response) => { if (response.status) { response["statusCode"] = response.status } else if (response.statusCode) { response["status"] = response.statusCode } return response } const get = (options, callback) => { if (isQuanX) { if (typeof options == "string") options = { url: options } options["method"] = "GET" $task.fetch(options).then(response => { callback(null, adapterStatus(response), response.body) }, reason => callback(reason.error, null, null)) } if (isSurge) $httpClient.get(options, (error, response, body) => { callback(error, adapterStatus(response), body) }) if (node) { node.request(options, (error, response, body) => { callback(error, adapterStatus(response), body) }) } } const post = (options, callback) => { if (isQuanX) { if (typeof options == "string") options = { url: options } options["method"] = "POST" $task.fetch(options).then(response => { callback(null, adapterStatus(response), response.body) }, reason => callback(reason.error, null, null)) } if (isSurge) { $httpClient.post(options, (error, response, body) => { callback(error, adapterStatus(response), body) }) } if (node) { node.request.post(options, (error, response, body) => { callback(error, adapterStatus(response), body) }) } } return { isQuanX, isSurge, notify, setCache, getCache, get, post } } Array.prototype.insert = function (index, item) { this.splice(index, 0, item); }; Date.prototype.format = function (fmt) { var o = { "y+": this.getFullYear(), "M+": this.getMonth() + 1, "d+": this.getDate(), "h+": this.getHours(), "m+": this.getMinutes(), "s+": this.getSeconds(), "q+": Math.floor((this.getMonth() + 3) / 3), "S+": this.getMilliseconds() }; for (var k in o) { if (new RegExp("(" + k + ")").test(fmt)) { if (k == "y+") { fmt = fmt.replace(RegExp.$1, ("" + o[k]).substr(4 - RegExp.$1.length)); } else if (k == "S+") { var lens = RegExp.$1.length; lens = lens == 1 ? 3 : lens; fmt = fmt.replace(RegExp.$1, ("00" + o[k]).substr(("" + o[k]).length - 1, lens)); } else { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); } } } return fmt; } ================================================ FILE: nf_rating.js ================================================ /* README:https://github.com/yichahucha/surge/tree/master */ const $tool = new Tool() const consoleLog = false; const imdbApikeyCacheKey = "IMDbApikey"; const netflixTitleCacheKey = "NetflixTitle"; if (!$tool.isResponse) { let url = $request.url; const urlDecode = decodeURIComponent(url); const videos = urlDecode.match(/"videos","(\d+)"/); const videoID = videos[1]; const map = getTitleMap(); const title = map[videoID]; const isEnglish = url.match(/languages=en/) ? true : false; if (!title && !isEnglish) { const currentSummary = urlDecode.match(/\["videos","(\d+)","current","summary"\]/); url = url.replace("&path=" + encodeURIComponent(currentSummary[0]), ""); url = url.replace(/&languages=(.*?)&/, "&languages=en-US&"); } url += "&path=" + encodeURIComponent(`[${videos[0]},"details"]`); $done({ url }); } else { var IMDbApikeys = IMDbApikeys(); var IMDbApikey = $tool.read(imdbApikeyCacheKey); if (!IMDbApikey) updateIMDbApikey(); let obj = JSON.parse($response.body); if (consoleLog) console.log("Netflix Original Body:\n" + $response.body); const videoID = obj.paths[0][1]; const video = obj.value.videos[videoID]; const map = getTitleMap(); let title = map[videoID]; if (!title) { title = video.summary.title; setTitleMap(videoID, title, map); } let year = null; let type = video.summary.type; if (type == "movie") { year = video.details.releaseYear; } else if (type == "show") { type = "series"; } delete video.details; const requestRatings = async () => { const IMDb = await requestIMDbRating(title, year, type); const Douban = await requestDoubanRating(IMDb.id); const IMDbrating = IMDb.msg.rating; const tomatoes = IMDb.msg.tomatoes; const country = IMDb.msg.country; const doubanRating = Douban.rating; const message = `${country}\n${IMDbrating}\n${doubanRating}${tomatoes.length > 0 ? "\n" + tomatoes + "\n" : "\n"}`; return message; } let msg = ""; requestRatings() .then(message => msg = message) .catch(error => msg = error + "\n") .finally(() => { let summary = obj.value.videos[videoID].summary; summary["supplementalMessage"] = `${msg}${summary && summary.supplementalMessage ? "\n" + summary.supplementalMessage : ""}`; if (consoleLog) console.log("Netflix Modified Body:\n" + JSON.stringify(obj)); $done({ body: JSON.stringify(obj) }); }); } function getTitleMap() { const map = $tool.read(netflixTitleCacheKey); return map ? JSON.parse(map) : {}; } function setTitleMap(id, title, map) { map[id] = title; $tool.write(JSON.stringify(map), netflixTitleCacheKey); } function requestDoubanRating(imdbId) { return new Promise(function (resolve, reject) { const url = "https://api.douban.com/v2/movie/imdb/" + imdbId + "?apikey=0df993c66c0c636e29ecbb5344252a4a"; if (consoleLog) console.log("Netflix Douban Rating URL:\n" + url); $tool.get(url, function (error, response, data) { if (!error) { if (consoleLog) console.log("Netflix Douban Rating Data:\n" + data); if (response.status == 200) { const obj = JSON.parse(data); const rating = get_douban_rating_message(obj); resolve({ rating }); } else { resolve({ rating: "Douban: " + errorTip().noData }); } } else { if (consoleLog) console.log("Netflix Douban Rating Error:\n" + error); resolve({ rating: "Douban: " + errorTip().error }); } }); }); } function requestIMDbRating(title, year, type) { return new Promise(function (resolve, reject) { let url = "https://www.omdbapi.com/?t=" + encodeURI(title) + "&apikey=" + IMDbApikey; if (year) url += "&y=" + year; if (type) url += "&type=" + type; if (consoleLog) console.log("Netflix IMDb Rating URL:\n" + url); $tool.get(url, function (error, response, data) { if (!error) { if (consoleLog) console.log("Netflix IMDb Rating Data:\n" + data); if (response.status == 200) { const obj = JSON.parse(data); if (obj.Response != "False") { const id = obj.imdbID; const msg = get_IMDb_message(obj); resolve({ id, msg }); } else { reject(errorTip().noData); } } else if (response.status == 401) { if (IMDbApikeys.length > 1) { updateIMDbApikey(); requestIMDbRating(title, year, type); } else { reject(errorTip().noData); } } else { reject(errorTip().noData); } } else { if (consoleLog) console.log("Netflix IMDb Rating Error:\n" + error); reject(errorTip().error); } }); }); } function updateIMDbApikey() { if (IMDbApikey) IMDbApikeys.splice(IMDbApikeys.indexOf(IMDbApikey), 1); const index = Math.floor(Math.random() * IMDbApikeys.length); IMDbApikey = IMDbApikeys[index]; $tool.write(IMDbApikey, imdbApikeyCacheKey); } function get_IMDb_message(data) { let rating_message = "IMDb: ⭐️ N/A"; let tomatoes_message = ""; let country_message = ""; let ratings = data.Ratings; if (ratings.length > 0) { const imdb_source = ratings[0]["Source"]; if (imdb_source == "Internet Movie Database") { const imdb_votes = data.imdbVotes; const imdb_rating = ratings[0]["Value"]; rating_message = "IMDb: ⭐️ " + imdb_rating + " " + "" + imdb_votes; if (data.Type == "movie") { if (ratings.length > 1) { const source = ratings[1]["Source"]; if (source == "Rotten Tomatoes") { const tomatoes = ratings[1]["Value"]; tomatoes_message = "Tomatoes: 🍅 " + tomatoes; } } } } } country_message = get_country_message(data.Country); return { rating: rating_message, tomatoes: tomatoes_message, country: country_message } } function get_douban_rating_message(data) { const average = data.rating.average; const numRaters = data.rating.numRaters; const rating_message = `Douban: ⭐️ ${average.length > 0 ? average + "/10" : "N/A"} ${numRaters == 0 ? "" : parseFloat(numRaters).toLocaleString()}`; return rating_message; } function get_country_message(data) { const country = data; const countrys = country.split(", "); let emoji_country = ""; countrys.forEach(item => { emoji_country += countryEmoji(item) + " " + item + ", "; }); return emoji_country.slice(0, -2); } function errorTip() { return { noData: "⭐️ N/A", error: "❌ N/A" } } function IMDbApikeys() { const apikeys = [ "PlzBanMe", "4e89234e", "f75e0253", "d8bb2d6b", "ae64ce8d", "7218d678", "b2650e38", "8c4a29ab", "9bd135c2", "953dbabe", "1a66ef12", "3e7ea721", "457fc4ff", "d2131426", "9cc1a9b7", "e53c2c11", "f6dfce0e", "b9db622f", "e6bde2b9", "d324dbab", "d7904fa3", "aeaf88b9"]; return apikeys; } function countryEmoji(name) { const emojiMap = { "Chequered": "🏁", "Triangular": "🚩", "Crossed": "🎌", "Black": "🏴", "White": "🏳", "Rainbow": "🏳️‍🌈", "Pirate": "🏴‍☠️", "Ascension Island": "🇦🇨", "Andorra": "🇦🇩", "United Arab Emirates": "🇦🇪", "Afghanistan": "🇦🇫", "Antigua & Barbuda": "🇦🇬", "Anguilla": "🇦🇮", "Albania": "🇦🇱", "Armenia": "🇦🇲", "Angola": "🇦🇴", "Antarctica": "🇦🇶", "Argentina": "🇦🇷", "American Samoa": "🇦🇸", "Austria": "🇦🇹", "Australia": "🇦🇺", "Aruba": "🇦🇼", "Åland Islands": "🇦🇽", "Azerbaijan": "🇦🇿", "Bosnia & Herzegovina": "🇧🇦", "Barbados": "🇧🇧", "Bangladesh": "🇧🇩", "Belgium": "🇧🇪", "Burkina Faso": "🇧🇫", "Bulgaria": "🇧🇬", "Bahrain": "🇧🇭", "Burundi": "🇧🇮", "Benin": "🇧🇯", "St. Barthélemy": "🇧🇱", "Bermuda": "🇧🇲", "Brunei": "🇧🇳", "Bolivia": "🇧🇴", "Caribbean Netherlands": "🇧🇶", "Brazil": "🇧🇷", "Bahamas": "🇧🇸", "Bhutan": "🇧🇹", "Bouvet Island": "🇧🇻", "Botswana": "🇧🇼", "Belarus": "🇧🇾", "Belize": "🇧🇿", "Canada": "🇨🇦", "Cocos (Keeling) Islands": "🇨🇨", "Congo - Kinshasa": "🇨🇩", "Congo": "🇨🇩", "Central African Republic": "🇨🇫", "Congo - Brazzaville": "🇨🇬", "Switzerland": "🇨🇭", "Côte d’Ivoire": "🇨🇮", "Cook Islands": "🇨🇰", "Chile": "🇨🇱", "Cameroon": "🇨🇲", "China": "🇨🇳", "Colombia": "🇨🇴", "Clipperton Island": "🇨🇵", "Costa Rica": "🇨🇷", "Cuba": "🇨🇺", "Cape Verde": "🇨🇻", "Curaçao": "🇨🇼", "Christmas Island": "🇨🇽", "Cyprus": "🇨🇾", "Czechia": "🇨🇿", "Czech Republic": "🇨🇿", "Germany": "🇩🇪", "Diego Garcia": "🇩🇬", "Djibouti": "🇩🇯", "Denmark": "🇩🇰", "Dominica": "🇩🇲", "Dominican Republic": "🇩🇴", "Algeria": "🇩🇿", "Ceuta & Melilla": "🇪🇦", "Ecuador": "🇪🇨", "Estonia": "🇪🇪", "Egypt": "🇪🇬", "Western Sahara": "🇪🇭", "Eritrea": "🇪🇷", "Spain": "🇪🇸", "Ethiopia": "🇪🇹", "European Union": "🇪🇺", "Finland": "🇫🇮", "Fiji": "🇫🇯", "Falkland Islands": "🇫🇰", "Micronesia": "🇫🇲", "Faroe Islands": "🇫🇴", "France": "🇫🇷", "Gabon": "🇬🇦", "United Kingdom": "🇬🇧", "UK": "🇬🇧", "Grenada": "🇬🇩", "Georgia": "🇬🇪", "French Guiana": "🇬🇫", "Guernsey": "🇬🇬", "Ghana": "🇬🇭", "Gibraltar": "🇬🇮", "Greenland": "🇬🇱", "Gambia": "🇬🇲", "Guinea": "🇬🇳", "Guadeloupe": "🇬🇵", "Equatorial Guinea": "🇬🇶", "Greece": "🇬🇷", "South Georgia & South Sandwich Is lands": "🇬🇸", "Guatemala": "🇬🇹", "Guam": "🇬🇺", "Guinea-Bissau": "🇬🇼", "Guyana": "🇬🇾", "Hong Kong SAR China": "🇭🇰", "Hong Kong": "🇭🇰", "Heard & McDonald Islands": "🇭🇲", "Honduras": "🇭🇳", "Croatia": "🇭🇷", "Haiti": "🇭🇹", "Hungary": "🇭🇺", "Canary Islands": "🇮🇨", "Indonesia": "🇮🇩", "Ireland": "🇮🇪", "Israel": "🇮🇱", "Isle of Man": "🇮🇲", "India": "🇮🇳", "British Indian Ocean Territory": "🇮🇴", "Iraq": "🇮🇶", "Iran": "🇮🇷", "Iceland": "🇮🇸", "Italy": "🇮🇹", "Jersey": "🇯🇪", "Jamaica": "🇯🇲", "Jordan": "🇯🇴", "Japan": "🇯🇵", "Kenya": "🇰🇪", "Kyrgyzstan": "🇰🇬", "Cambodia": "🇰🇭", "Kiribati": "🇰🇮", "Comoros": "🇰🇲", "St. Kitts & Nevis": "🇰🇳", "North Korea": "🇰🇵", "South Korea": "🇰🇷", "Kuwait": "🇰🇼", "Cayman Islands": "🇰🇾", "Kazakhstan": "🇰🇿", "Laos": "🇱🇦", "Lebanon": "🇱🇧", "St. Lucia": "🇱🇨", "Liechtenstein": "🇱🇮", "Sri Lanka": "🇱🇰", "Liberia": "🇱🇷", "Lesotho": "🇱🇸", "Lithuania": "🇱🇹", "Luxembourg": "🇱🇺", "Latvia": "🇱🇻", "Libya": "🇱🇾", "Morocco": "🇲🇦", "Monaco": "🇲🇨", "Moldova": "🇲🇩", "Montenegro": "🇲🇪", "St. Martin": "🇲🇫", "Madagascar": "🇲🇬", "Marshall Islands": "🇲🇭", "North Macedonia": "🇲🇰", "Mali": "🇲🇱", "Myanmar (Burma)": "🇲🇲", "Mongolia": "🇲🇳", "Macau Sar China": "🇲🇴", "Northern Mariana Islands": "🇲🇵", "Martinique": "🇲🇶", "Mauritania": "🇲🇷", "Montserrat": "🇲🇸", "Malta": "🇲🇹", "Mauritius": "🇲🇺", "Maldives": "🇲🇻", "Malawi": "🇲🇼", "Mexico": "🇲🇽", "Malaysia": "🇲🇾", "Mozambique": "🇲🇿", "Namibia": "🇳🇦", "New Caledonia": "🇳🇨", "Niger": "🇳🇪", "Norfolk Island": "🇳🇫", "Nigeria": "🇳🇬", "Nicaragua": "🇳🇮", "Netherlands": "🇳🇱", "Norway": "🇳🇴", "Nepal": "🇳🇵", "Nauru": "🇳🇷", "Niue": "🇳🇺", "New Zealand": "🇳🇿", "Oman": "🇴🇲", "Panama": "🇵🇦", "Peru": "🇵🇪", "French Polynesia": "🇵🇫", "Papua New Guinea": "🇵🇬", "Philippines": "🇵🇭", "Pakistan": "🇵🇰", "Poland": "🇵🇱", "St. Pierre & Miquelon": "🇵🇲", "Pitcairn Islands": "🇵🇳", "Puerto Rico": "🇵🇷", "Palestinian Territories": "🇵🇸", "Portugal": "🇵🇹", "Palau": "🇵🇼", "Paraguay": "🇵🇾", "Qatar": "🇶🇦", "Réunion": "🇷🇪", "Romania": "🇷🇴", "Serbia": "🇷🇸", "Russia": "🇷🇺", "Rwanda": "🇷🇼", "Saudi Arabia": "🇸🇦", "Solomon Islands": "🇸🇧", "Seychelles": "🇸🇨", "Sudan": "🇸🇩", "Sweden": "🇸🇪", "Singapore": "🇸🇬", "St. Helena": "🇸🇭", "Slovenia": "🇸🇮", "Svalbard & Jan Mayen": "🇸🇯", "Slovakia": "🇸🇰", "Sierra Leone": "🇸🇱", "San Marino": "🇸🇲", "Senegal": "🇸🇳", "Somalia": "🇸🇴", "Suriname": "🇸🇷", "South Sudan": "🇸🇸", "São Tomé & Príncipe": "🇸🇹", "El Salvador": "🇸🇻", "Sint Maarten": "🇸🇽", "Syria": "🇸🇾", "Swaziland": "🇸🇿", "Tristan Da Cunha": "🇹🇦", "Turks & Caicos Islands": "🇹🇨", "Chad": "🇹🇩", "French Southern Territories": "🇹🇫", "Togo": "🇹🇬", "Thailand": "🇹🇭", "Tajikistan": "🇹🇯", "Tokelau": "🇹🇰", "Timor-Leste": "🇹🇱", "Turkmenistan": "🇹🇲", "Tunisia": "🇹🇳", "Tonga": "🇹🇴", "Turkey": "🇹🇷", "Trinidad & Tobago": "🇹🇹", "Tuvalu": "🇹🇻", "Taiwan": "🇨🇳", "Tanzania": "🇹🇿", "Ukraine": "🇺🇦", "Uganda": "🇺🇬", "U.S. Outlying Islands": "🇺🇲", "United Nations": "🇺🇳", "United States": "🇺🇸", "USA": "🇺🇸", "Uruguay": "🇺🇾", "Uzbekistan": "🇺🇿", "Vatican City": "🇻🇦", "St. Vincent & Grenadines": "🇻🇨", "Venezuela": "🇻🇪", "British Virgin Islands": "🇻🇬", "U.S. Virgin Islands": "🇻🇮", "Vietnam": "🇻🇳", "Vanuatu": "🇻🇺", "Wallis & Futuna": "🇼🇫", "Samoa": "🇼🇸", "Kosovo": "🇽🇰", "Yemen": "🇾🇪", "Mayotte": "🇾🇹", "South Africa": "🇿🇦", "Zambia": "🇿🇲", "Zimbabwe": "🇿🇼", "England": "🏴󠁧󠁢󠁥󠁮󠁧󠁿", "Scotland": "🏴󠁧󠁢󠁳󠁣󠁴󠁿", "Wales": "🏴󠁧󠁢󠁷󠁬󠁳󠁿", } return emojiMap[name] ? emojiMap[name] : emojiMap["Chequered"]; } function Tool() { _node = (() => { if (typeof require == "function") { const request = require('request') return ({ request }) } else { return (null) } })() _isSurge = typeof $httpClient != "undefined" _isQuanX = typeof $task != "undefined" this.isSurge = _isSurge this.isQuanX = _isQuanX this.isResponse = typeof $response != "undefined" this.notify = (title, subtitle, message) => { if (_isQuanX) $notify(title, subtitle, message) if (_isSurge) $notification.post(title, subtitle, message) if (_node) console.log(JSON.stringify({ title, subtitle, message })); } this.write = (value, key) => { if (_isQuanX) return $prefs.setValueForKey(value, key) if (_isSurge) return $persistentStore.write(value, key) } this.read = (key) => { if (_isQuanX) return $prefs.valueForKey(key) if (_isSurge) return $persistentStore.read(key) } this.get = (options, callback) => { if (_isQuanX) { if (typeof options == "string") options = { url: options } options["method"] = "GET" $task.fetch(options).then(response => { callback(null, _status(response), response.body) }, reason => callback(reason.error, null, null)) } if (_isSurge) $httpClient.get(options, (error, response, body) => { callback(error, _status(response), body) }) if (_node) _node.request(options, (error, response, body) => { callback(error, _status(response), body) }) } this.post = (options, callback) => { if (_isQuanX) { if (typeof options == "string") options = { url: options } options["method"] = "POST" $task.fetch(options).then(response => { callback(null, _status(response), response.body) }, reason => callback(reason.error, null, null)) } if (_isSurge) $httpClient.post(options, (error, response, body) => { callback(error, _status(response), body) }) if (_node) _node.request.post(options, (error, response, body) => { callback(error, _status(response), body) }) } _status = (response) => { if (response) { if (response.status) { response["statusCode"] = response.status } else if (response.statusCode) { response["status"] = response.statusCode } } return response } } ================================================ FILE: tb_price.js ================================================ /* README:https://github.com/yichahucha/surge/tree/master */ const $tool = new Tool() const $base64 = new Base64() const consoleLog = false const url = $request.url const body = $response.body const path1 = "/amdc/mobileDispatch" const path2 = "/gw/mtop.taobao.detail.getdetail" if (url.indexOf(path1) != -1) { let obj = JSON.parse($base64.decode(body)) let dns = obj.dns if (dns && dns.length > 0) { let i = dns.length; while (i--) { const element = dns[i]; let host = "trade-acs.m.taobao.com" if (element.host == host) { element.ips = [] if (consoleLog) console.log(JSON.stringify(element)) } } } $done({ body: $base64.encode(JSON.stringify(obj)) }) } if (url.indexOf(path2) != -1) { const body = $response.body let obj = JSON.parse(body) let apiStack = obj.data.apiStack[0] let value = JSON.parse(apiStack.value) if (value.global) { let tradeConsumerProtection = value.global.data.tradeConsumerProtection if (!tradeConsumerProtection) { value.global.data["tradeConsumerProtection"] = customTradeConsumerProtection() } tradeConsumerProtection = value.global.data.tradeConsumerProtection let service = tradeConsumerProtection.tradeConsumerService.service let nonService = tradeConsumerProtection.tradeConsumerService.nonService let item = obj.data.item let shareUrl = `https://item.taobao.com/item.htm?id=${item.itemId}` requestPrice(shareUrl, function (data) { if (data) { if (data.ok == 1 && data.single) { const lower = lowerMsgs(data.single) const tbitems = priceSummary(data) const tip = data.PriceRemark.Tip service.items = service.items.concat(nonService.items) service.items.unshift(customItem(lower[1], `${lower[0]} ${tip}` + "(仅供参考)")) nonService.title = "价格详情" nonService.items = tbitems } if (data.ok == 0 && data.msg.length > 0) { service.items.unshift(customItem("历史价格", data.msg)) } apiStack.value = JSON.stringify(value) $done({ body: JSON.stringify(obj) }) } else { $done({ body }) } }) } else { $done({ body }) } } function lowerMsgs(data) { const lower = data.lowerPriceyh const lowerDate = dateFormat(data.lowerDateyh) const lowerMsg = "最低到手价:¥" + String(lower) + `(${lowerDate})` const lowerMsg1 = "历史最低¥" + String(lower) return [lowerMsg, lowerMsg1] } function priceSummary(data) { let tbitems = [] let summary = "" let listPriceDetail = data.PriceRemark.ListPriceDetail listPriceDetail.pop() let list = listPriceDetail.concat(historySummary(data.single)) list.forEach((item, index) => { if (index == 2) { item.Name = "双十一价格" } else if (index == 3) { item.Name = "六一八价格" } else if (index == 4) { item.Name = "三十天最低" } summary = `${item.Name}${getSpace(10)}${item.Price}${getSpace(10)}${item.Date}` tbitems.push(customItem(summary)) }) return tbitems } function historySummary(single) { const rexMatch = /\[.*?\]/g; const rexExec = /\[(.*),(.*),"(.*)"\]/; let currentPrice, lowest60, lowest180, lowest360 let list = single.jiagequshiyh.match(rexMatch); list = list.reverse().slice(0, 360); list.forEach((item, index) => { if (item.length > 0) { const result = rexExec.exec(item); const dateUTC = new Date(eval(result[1])); const date = dateUTC.format("yyyy-MM-dd"); let price = parseFloat(result[2]); if (index == 0) { currentPrice = price lowest60 = { Name: "六十天最低", Price: `¥${String(price)}`, Date: date, Difference: difference(currentPrice, price), price } lowest180 = { Name: "一百八最低", Price: `¥${String(price)}`, Date: date, Difference: difference(currentPrice, price), price } lowest360 = { Name: "三百六最低", Price: `¥${String(price)}`, Date: date, Difference: difference(currentPrice, price), price } } if (index < 60 && price <= lowest60.price) { lowest60.price = price lowest60.Price = `¥${String(price)}` lowest60.Date = date lowest60.Difference = difference(currentPrice, price) } if (index < 180 && price <= lowest180.price) { lowest180.price = price lowest180.Price = `¥${String(price)}` lowest180.Date = date lowest180.Difference = difference(currentPrice, price) } if (index < 360 && price <= lowest360.price) { lowest360.price = price lowest360.Price = `¥${String(price)}` lowest360.Date = date lowest360.Difference = difference(currentPrice, price) } } }); return [lowest60, lowest180, lowest360]; } function difference(currentPrice, price) { let difference = strip(currentPrice - price) if (difference == 0) { return "-" } else { return `${difference > 0 ? "↑" : "↓"}${String(difference)}` } } function strip(num, precision = 12) { return +parseFloat(num.toPrecision(precision)); } function requestPrice(share_url, callback) { const options = { url: "https://apapia-history.manmanbuy.com/ChromeWidgetServices/WidgetServices.ashx", headers: { "Content-Type": "application/x-www-form-urlencoded;charset=utf-8", "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_1_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 - mmbWebBrowse - ios" }, body: "methodName=getHistoryTrend&p_url=" + encodeURIComponent(share_url) } $tool.post(options, function (error, response, data) { if (!error) { callback(JSON.parse(data)); if (consolelog) console.log("Data:\n" + data); } else { callback(null, null); if (consolelog) console.log("Error:\n" + error); } }) } function dateFormat(cellval) { const date = new Date(parseInt(cellval.replace("/Date(", "").replace(")/", ""), 10)); const month = date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1; const currentDate = date.getDate() < 10 ? "0" + date.getDate() : date.getDate(); return date.getFullYear() + "-" + month + "-" + currentDate; } function getSpace(length) { let blank = ""; for (let index = 0; index < length; index++) { blank += " "; } return blank; } function customItem(title, desc) { return { icon: "https://s2.ax1x.com/2020/02/16/3STeIJ.png", title: title, desc: desc } } function customTradeConsumerProtection() { return { "tradeConsumerService": { "service": { "items": [ ], "icon": "", "title": "基础服务" }, "nonService": { "items": [ ], "title": "其他" } }, "passValue": "all", "url": "https://h5.m.taobao.com/app/detailsubpage/consumer/index.js", "type": "0" } } Array.prototype.insert = function (index, item) { this.splice(index, 0, item); }; Date.prototype.format = function (fmt) { var o = { "y+": this.getFullYear(), "M+": this.getMonth() + 1, "d+": this.getDate(), "h+": this.getHours(), "m+": this.getMinutes(), "s+": this.getSeconds(), "q+": Math.floor((this.getMonth() + 3) / 3), "S+": this.getMilliseconds() }; for (var k in o) { if (new RegExp("(" + k + ")").test(fmt)) { if (k == "y+") { fmt = fmt.replace(RegExp.$1, ("" + o[k]).substr(4 - RegExp.$1.length)); } else if (k == "S+") { var lens = RegExp.$1.length; lens = lens == 1 ? 3 : lens; fmt = fmt.replace(RegExp.$1, ("00" + o[k]).substr(("" + o[k]).length - 1, lens)); } else { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); } } } return fmt; } function Tool() { _node = (() => { if (typeof require == "function") { const request = require('request') return ({ request }) } else { return (null) } })() _isSurge = typeof $httpClient != "undefined" _isQuanX = typeof $task != "undefined" this.isSurge = _isSurge this.isQuanX = _isQuanX this.isResponse = typeof $response != "undefined" this.notify = (title, subtitle, message) => { if (_isQuanX) $notify(title, subtitle, message) if (_isSurge) $notification.post(title, subtitle, message) if (_node) console.log(JSON.stringify({ title, subtitle, message })); } this.write = (value, key) => { if (_isQuanX) return $prefs.setValueForKey(value, key) if (_isSurge) return $persistentStore.write(value, key) } this.read = (key) => { if (_isQuanX) return $prefs.valueForKey(key) if (_isSurge) return $persistentStore.read(key) } this.get = (options, callback) => { if (_isQuanX) { if (typeof options == "string") options = { url: options } options["method"] = "GET" $task.fetch(options).then(response => { callback(null, _status(response), response.body) }, reason => callback(reason.error, null, null)) } if (_isSurge) $httpClient.get(options, (error, response, body) => { callback(error, _status(response), body) }) if (_node) _node.request(options, (error, response, body) => { callback(error, _status(response), body) }) } this.post = (options, callback) => { if (_isQuanX) { if (typeof options == "string") options = { url: options } options["method"] = "POST" $task.fetch(options).then(response => { callback(null, _status(response), response.body) }, reason => callback(reason.error, null, null)) } if (_isSurge) $httpClient.post(options, (error, response, body) => { callback(error, _status(response), body) }) if (_node) _node.request.post(options, (error, response, body) => { callback(error, _status(response), body) }) } _status = (response) => { if (response) { if (response.status) { response["statusCode"] = response.status } else if (response.statusCode) { response["status"] = response.statusCode } } return response } } function Base64() { // private property _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; // public method for encoding this.encode = function (input) { var output = ""; var chr1, chr2, chr3, enc1, enc2, enc3, enc4; var i = 0; input = _utf8_encode(input); while (i < input.length) { chr1 = input.charCodeAt(i++); chr2 = input.charCodeAt(i++); chr3 = input.charCodeAt(i++); enc1 = chr1 >> 2; enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + _keyStr.charAt(enc1) + _keyStr.charAt(enc2) + _keyStr.charAt(enc3) + _keyStr.charAt(enc4); } return output; } // public method for decoding this.decode = function (input) { var output = ""; var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; var i = 0; input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); while (i < input.length) { enc1 = _keyStr.indexOf(input.charAt(i++)); enc2 = _keyStr.indexOf(input.charAt(i++)); enc3 = _keyStr.indexOf(input.charAt(i++)); enc4 = _keyStr.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 != 64) { output = output + String.fromCharCode(chr2); } if (enc4 != 64) { output = output + String.fromCharCode(chr3); } } output = _utf8_decode(output); return output; } // private method for UTF-8 encoding _utf8_encode = function (string) { string = string.replace(/\r\n/g, "\n"); var utftext = ""; for (var n = 0; n < string.length; n++) { var c = string.charCodeAt(n); if (c < 128) { utftext += String.fromCharCode(c); } else if ((c > 127) && (c < 2048)) { utftext += String.fromCharCode((c >> 6) | 192); utftext += String.fromCharCode((c & 63) | 128); } else { utftext += String.fromCharCode((c >> 12) | 224); utftext += String.fromCharCode(((c >> 6) & 63) | 128); utftext += String.fromCharCode((c & 63) | 128); } } return utftext; } // private method for UTF-8 decoding _utf8_decode = function (utftext) { var string = ""; var i = 0; var c = c1 = c2 = 0; while (i < utftext.length) { c = utftext.charCodeAt(i); if (c < 128) { string += String.fromCharCode(c); i++; } else if ((c > 191) && (c < 224)) { c2 = utftext.charCodeAt(i + 1); string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); i += 2; } else { c2 = utftext.charCodeAt(i + 1); c3 = utftext.charCodeAt(i + 2); string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); i += 3; } } return string; } } ================================================ FILE: tb_price_lite.js ================================================ /* README:https://github.com/yichahucha/surge/tree/master */ const $tool = new Tool() const $base64 = new Base64() const consoleLog = false const url = $request.url const body = $response.body const path1 = "/amdc/mobileDispatch" const path2 = "/gw/mtop.taobao.detail.getdetail" if (url.indexOf(path1) != -1) { let obj = JSON.parse($base64.decode(body)) let dns = obj.dns if (dns && dns.length > 0) { let i = dns.length; while (i--) { const element = dns[i]; let host = "trade-acs.m.taobao.com" if (element.host == host) { element.ips = [] if (consoleLog) console.log(JSON.stringify(element)) } } } $done({ body: $base64.encode(JSON.stringify(obj)) }) } if (url.indexOf(path2) != -1) { $done({ body }) const obj = JSON.parse(body) let item = obj.data.item let shareUrl = `https://item.taobao.com/item.htm?id=${item.itemId}` requestPrice(shareUrl, function (data) { if (data) { if (data.ok == 1 && data.single) { const lower = lowerMsgs(data.single) const detail = priceSummary(data) const tip = data.PriceRemark.Tip + "(仅供参考)" $tool.notify("", "", `${lower} ${tip}\n${detail}\n\n👉查看详情:http://tool.manmanbuy.com/historyLowest.aspx?url=${encodeURI(shareUrl)}`) } if (data.ok == 0 && data.msg.length > 0) { $tool.notify("", "", `⚠️ ${data.msg}`) } } }) } function lowerMsgs(data) { const lower = data.lowerPriceyh const lowerDate = dateFormat(data.lowerDateyh) const lowerMsg = "〽️历史最低到手价:¥" + String(lower) + `(${lowerDate})` return lowerMsg } function priceSummary(data) { let summary = "" let listPriceDetail = data.PriceRemark.ListPriceDetail listPriceDetail.pop() let list = listPriceDetail.concat(historySummary(data.single)) list.forEach((item, index) => { if (index == 2) { item.Name = "双十一价格" } else if (index == 3) { item.Name = "六一八价格" } else if (index == 4) { item.Name = "三十天最低" } summary += `\n${item.Name} ${item.Price} ${item.Date} ${item.Difference}` }) return summary } function historySummary(single) { const rexMatch = /\[.*?\]/g; const rexExec = /\[(.*),(.*),"(.*)"\]/; let currentPrice, lowest60, lowest180, lowest360 let list = single.jiagequshiyh.match(rexMatch); list = list.reverse().slice(0, 360); list.forEach((item, index) => { if (item.length > 0) { const result = rexExec.exec(item); const dateUTC = new Date(eval(result[1])); const date = dateUTC.format("yyyy-MM-dd"); let price = parseFloat(result[2]); if (index == 0) { currentPrice = price lowest60 = { Name: "六十天最低", Price: `¥${String(price)}`, Date: date, Difference: difference(currentPrice, price), price } lowest180 = { Name: "一百八最低", Price: `¥${String(price)}`, Date: date, Difference: difference(currentPrice, price), price } lowest360 = { Name: "三百六最低", Price: `¥${String(price)}`, Date: date, Difference: difference(currentPrice, price), price } } if (index < 60 && price <= lowest60.price) { lowest60.price = price lowest60.Price = `¥${String(price)}` lowest60.Date = date lowest60.Difference = difference(currentPrice, price) } if (index < 180 && price <= lowest180.price) { lowest180.price = price lowest180.Price = `¥${String(price)}` lowest180.Date = date lowest180.Difference = difference(currentPrice, price) } if (index < 360 && price <= lowest360.price) { lowest360.price = price lowest360.Price = `¥${String(price)}` lowest360.Date = date lowest360.Difference = difference(currentPrice, price) } } }); return [lowest60, lowest180, lowest360]; } function difference(currentPrice, price) { let difference = strip(currentPrice - price) if (difference == 0) { return "-" } else { return `${difference > 0 ? "↑" : "↓"}${String(difference)}` } } function strip(num, precision = 12) { return +parseFloat(num.toPrecision(precision)); } function requestPrice(share_url, callback) { const options = { url: "https://apapia-history.manmanbuy.com/ChromeWidgetServices/WidgetServices.ashx", headers: { "Content-Type": "application/x-www-form-urlencoded;charset=utf-8", "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_1_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 - mmbWebBrowse - ios" }, body: "methodName=getHistoryTrend&p_url=" + encodeURIComponent(share_url) } $tool.post(options, function (error, response, data) { if (!error) { callback(JSON.parse(data)); if (consolelog) console.log("Data:\n" + data); } else { callback(null, null); if (consolelog) console.log("Error:\n" + error); } }) } function dateFormat(cellval) { const date = new Date(parseInt(cellval.replace("/Date(", "").replace(")/", ""), 10)); const month = date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1; const currentDate = date.getDate() < 10 ? "0" + date.getDate() : date.getDate(); return date.getFullYear() + "-" + month + "-" + currentDate; } Array.prototype.insert = function (index, item) { this.splice(index, 0, item); }; Date.prototype.format = function (fmt) { var o = { "y+": this.getFullYear(), "M+": this.getMonth() + 1, "d+": this.getDate(), "h+": this.getHours(), "m+": this.getMinutes(), "s+": this.getSeconds(), "q+": Math.floor((this.getMonth() + 3) / 3), "S+": this.getMilliseconds() }; for (var k in o) { if (new RegExp("(" + k + ")").test(fmt)) { if (k == "y+") { fmt = fmt.replace(RegExp.$1, ("" + o[k]).substr(4 - RegExp.$1.length)); } else if (k == "S+") { var lens = RegExp.$1.length; lens = lens == 1 ? 3 : lens; fmt = fmt.replace(RegExp.$1, ("00" + o[k]).substr(("" + o[k]).length - 1, lens)); } else { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); } } } return fmt; } function Tool() { _node = (() => { if (typeof require == "function") { const request = require('request') return ({ request }) } else { return (null) } })() _isSurge = typeof $httpClient != "undefined" _isQuanX = typeof $task != "undefined" this.isSurge = _isSurge this.isQuanX = _isQuanX this.isResponse = typeof $response != "undefined" this.notify = (title, subtitle, message) => { if (_isQuanX) $notify(title, subtitle, message) if (_isSurge) $notification.post(title, subtitle, message) if (_node) console.log(JSON.stringify({ title, subtitle, message })); } this.write = (value, key) => { if (_isQuanX) return $prefs.setValueForKey(value, key) if (_isSurge) return $persistentStore.write(value, key) } this.read = (key) => { if (_isQuanX) return $prefs.valueForKey(key) if (_isSurge) return $persistentStore.read(key) } this.get = (options, callback) => { if (_isQuanX) { if (typeof options == "string") options = { url: options } options["method"] = "GET" $task.fetch(options).then(response => { callback(null, _status(response), response.body) }, reason => callback(reason.error, null, null)) } if (_isSurge) $httpClient.get(options, (error, response, body) => { callback(error, _status(response), body) }) if (_node) _node.request(options, (error, response, body) => { callback(error, _status(response), body) }) } this.post = (options, callback) => { if (_isQuanX) { if (typeof options == "string") options = { url: options } options["method"] = "POST" $task.fetch(options).then(response => { callback(null, _status(response), response.body) }, reason => callback(reason.error, null, null)) } if (_isSurge) $httpClient.post(options, (error, response, body) => { callback(error, _status(response), body) }) if (_node) _node.request.post(options, (error, response, body) => { callback(error, _status(response), body) }) } _status = (response) => { if (response) { if (response.status) { response["statusCode"] = response.status } else if (response.statusCode) { response["status"] = response.statusCode } } return response } } function Base64() { // private property _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; // public method for encoding this.encode = function (input) { var output = ""; var chr1, chr2, chr3, enc1, enc2, enc3, enc4; var i = 0; input = _utf8_encode(input); while (i < input.length) { chr1 = input.charCodeAt(i++); chr2 = input.charCodeAt(i++); chr3 = input.charCodeAt(i++); enc1 = chr1 >> 2; enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + _keyStr.charAt(enc1) + _keyStr.charAt(enc2) + _keyStr.charAt(enc3) + _keyStr.charAt(enc4); } return output; } // public method for decoding this.decode = function (input) { var output = ""; var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; var i = 0; input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); while (i < input.length) { enc1 = _keyStr.indexOf(input.charAt(i++)); enc2 = _keyStr.indexOf(input.charAt(i++)); enc3 = _keyStr.indexOf(input.charAt(i++)); enc4 = _keyStr.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 != 64) { output = output + String.fromCharCode(chr2); } if (enc4 != 64) { output = output + String.fromCharCode(chr3); } } output = _utf8_decode(output); return output; } // private method for UTF-8 encoding _utf8_encode = function (string) { string = string.replace(/\r\n/g, "\n"); var utftext = ""; for (var n = 0; n < string.length; n++) { var c = string.charCodeAt(n); if (c < 128) { utftext += String.fromCharCode(c); } else if ((c > 127) && (c < 2048)) { utftext += String.fromCharCode((c >> 6) | 192); utftext += String.fromCharCode((c & 63) | 128); } else { utftext += String.fromCharCode((c >> 12) | 224); utftext += String.fromCharCode(((c >> 6) & 63) | 128); utftext += String.fromCharCode((c & 63) | 128); } } return utftext; } // private method for UTF-8 decoding _utf8_decode = function (utftext) { var string = ""; var i = 0; var c = c1 = c2 = 0; while (i < utftext.length) { c = utftext.charCodeAt(i); if (c < 128) { string += String.fromCharCode(c); i++; } else if ((c > 191) && (c < 224)) { c2 = utftext.charCodeAt(i + 1); string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); i += 2; } else { c2 = utftext.charCodeAt(i + 1); c3 = utftext.charCodeAt(i + 2); string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); i += 3; } } return string; } } ================================================ FILE: tool.js ================================================ // 统一 Surge、QuanX、Node 相关脚本 API, 方便开发、调试 // 包括 Node(request 模块)、Surge($httpClient,$notification,$persistentStore 模块)、QuanX($task,$notify,$prefs 模块) const $tool = new Tool() // notify $tool.notify("title", "subtitle", "message") // cache $tool.write("value", "key") $tool.read("key") // get $tool.get("http://www.baidu.com", function (error, response, body) { // response.status response.statusCode response.headers if (!error) { if (response.statusCode == 200) { console.log(body) } } else { console.log(error) } }) // post const request = { url: "https://www.baidu.com", body: "" //... } $tool.post(request, function (error, response, body) { // response.status response.statusCode response.headers if (!error) { if (response.statusCode == 200) { console.log(body) } } else { console.log(error) } }) function Tool() { _node = (() => { if (typeof require == "function") { const request = require('request') return ({ request }) } else { return (null) } })() _isSurge = typeof $httpClient != "undefined" _isQuanX = typeof $task != "undefined" this.isSurge = _isSurge this.isQuanX = _isQuanX this.isResponse = typeof $response != "undefined" this.notify = (title, subtitle, message) => { if (_isQuanX) $notify(title, subtitle, message) if (_isSurge) $notification.post(title, subtitle, message) if (_node) console.log(JSON.stringify({ title, subtitle, message })); } this.write = (value, key) => { if (_isQuanX) return $prefs.setValueForKey(value, key) if (_isSurge) return $persistentStore.write(value, key) } this.read = (key) => { if (_isQuanX) return $prefs.valueForKey(key) if (_isSurge) return $persistentStore.read(key) } this.get = (options, callback) => { if (_isQuanX) { if (typeof options == "string") options = { url: options } options["method"] = "GET" $task.fetch(options).then(response => { callback(null, _status(response), response.body) }, reason => callback(reason.error, null, null)) } if (_isSurge) $httpClient.get(options, (error, response, body) => { callback(error, _status(response), body) }) if (_node) _node.request(options, (error, response, body) => { callback(error, _status(response), body) }) } this.post = (options, callback) => { if (_isQuanX) { if (typeof options == "string") options = { url: options } options["method"] = "POST" $task.fetch(options).then(response => { callback(null, _status(response), response.body) }, reason => callback(reason.error, null, null)) } if (_isSurge) $httpClient.post(options, (error, response, body) => { callback(error, _status(response), body) }) if (_node) _node.request.post(options, (error, response, body) => { callback(error, _status(response), body) }) } _status = (response) => { if (response) { if (response.status) { response["statusCode"] = response.status } else if (response.statusCode) { response["status"] = response.statusCode } } return response } } function tool() { const isSurge = typeof $httpClient != "undefined" const isQuanX = typeof $task != "undefined" const isResponse = typeof $response != "undefined" const node = (() => { if (typeof require == "function") { const request = require('request') return ({ request }) } else { return (null) } })() const notify = (title, subtitle, message) => { if (isQuanX) $notify(title, subtitle, message) if (isSurge) $notification.post(title, subtitle, message) if (node) console.log(JSON.stringify({ title, subtitle, message })); } const write = (value, key) => { if (isQuanX) return $prefs.setValueForKey(value, key) if (isSurge) return $persistentStore.write(value, key) } const read = (key) => { if (isQuanX) return $prefs.valueForKey(key) if (isSurge) return $persistentStore.read(key) } const adapterStatus = (response) => { if (response) { if (response.status) { response["statusCode"] = response.status } else if (response.statusCode) { response["status"] = response.statusCode } } return response } const get = (options, callback) => { if (isQuanX) { if (typeof options == "string") options = { url: options } options["method"] = "GET" $task.fetch(options).then(response => { callback(null, adapterStatus(response), response.body) }, reason => callback(reason.error, null, null)) } if (isSurge) $httpClient.get(options, (error, response, body) => { callback(error, adapterStatus(response), body) }) if (node) { node.request(options, (error, response, body) => { callback(error, adapterStatus(response), body) }) } } const post = (options, callback) => { if (isQuanX) { if (typeof options == "string") options = { url: options } options["method"] = "POST" $task.fetch(options).then(response => { callback(null, adapterStatus(response), response.body) }, reason => callback(reason.error, null, null)) } if (isSurge) { $httpClient.post(options, (error, response, body) => { callback(error, adapterStatus(response), body) }) } if (node) { node.request.post(options, (error, response, body) => { callback(error, adapterStatus(response), body) }) } } return { isQuanX, isSurge, isResponse, notify, write, read, get, post } } ================================================ FILE: wb_ad.js ================================================ /* README:https://github.com/yichahucha/surge/tree/master */ const path1 = "/groups/timeline"; const path2 = "/statuses/unread"; const path3 = "/statuses/extend"; const path4 = "/comments/build_comments"; const path5 = "/photo/recommend_list"; const path6 = "/stories/video_stream"; const path7 = "/statuses/positives/get"; const path8 = "/stories/home_list"; const path9 = "/profile/statuses"; const path10 = "/statuses/friends/timeline"; const path11 = "/service/picfeed"; const path12 = "/fangle/timeline"; const path13 = "/searchall"; const path14 = "/cardlist"; const path15 = "/statuses/video_timeline"; const path16 = "/page"; const path17 = "/statuses/friends_timeline"; const path18 = "/!/photos/pic_recommend_status"; const url = $request.url; var body = $response.body; if ( url.indexOf(path1) != -1 || url.indexOf(path2) != -1 || url.indexOf(path10) != -1 || url.indexOf(path15) != -1 || url.indexOf(path17) != -1 ) { let obj = JSON.parse(body); if (obj.statuses) obj.statuses = filter_timeline_statuses(obj.statuses); if (obj.advertises) obj.advertises = []; if (obj.ad) obj.ad = []; if (obj.num) obj.num = obj.original_num; if (obj.trends) obj.trends = []; body = JSON.stringify(obj); } if (url.indexOf(path3) != -1) { let obj = JSON.parse(body); if (obj.trend) delete obj.trend; body = JSON.stringify(obj); } if (url.indexOf(path4) != -1) { let obj = JSON.parse(body); obj.recommend_max_id = 0; if (obj.status) { if (obj.top_hot_structs) { obj.max_id = obj.top_hot_structs.call_back_struct.max_id; delete obj.top_hot_structs; } if (obj.datas) obj.datas = filter_comments(obj.datas); } else { obj.datas = []; } body = JSON.stringify(obj); } if (url.indexOf(path5) != -1 || url.indexOf(path18) != -1) { let obj = JSON.parse(body); obj.data = {}; body = JSON.stringify(obj); } if (url.indexOf(path6) != -1) { let obj = JSON.parse(body); let segments = obj.segments; if (segments && segments.length > 0) { let i = segments.length; while (i--) { const element = segments[i]; let is_ad = element.is_ad; if (is_ad && is_ad == true) segments.splice(i, 1); } } body = JSON.stringify(obj); } if (url.indexOf(path7) != -1) { let obj = JSON.parse(body); obj.datas = []; body = JSON.stringify(obj); } if (url.indexOf(path8) != -1) { let obj = JSON.parse(body); obj.story_list = []; body = JSON.stringify(obj); } if (url.indexOf(path11) != -1) { let obj = JSON.parse(body); obj.data = []; body = JSON.stringify(obj); } if ( url.indexOf(path9) != -1 || url.indexOf(path12) != -1 || url.indexOf(path13) != -1 || url.indexOf(path14) != -1 || url.indexOf(path16) != -1 ) { let obj = JSON.parse(body); if (obj.cards) obj.cards = filter_timeline_cards(obj.cards); body = JSON.stringify(obj); } $done({ body }); function filter_timeline_statuses(statuses) { if (statuses && statuses.length > 0) { let i = statuses.length; while (i--) { let element = statuses[i]; if (is_timeline_likerecommend(element.title)) statuses.splice(i, 1); if (is_timeline_ad(element)) statuses.splice(i, 1); } } return statuses; } function filter_comments(datas) { if (datas && datas.length > 0) { let i = datas.length; while (i--) { const element = datas[i]; let type = element.type; if (type == 5 || type == 1 || type == 6) datas.splice(i, 1); } } return datas; } function filter_timeline_cards(cards) { if (cards && cards.length > 0) { let j = cards.length; while (j--) { let item = cards[j]; let card_group = item.card_group; if (card_group && card_group.length > 0) { let i = card_group.length; while (i--) { let card_group_item = card_group[i]; let card_type = card_group_item.card_type; if (card_type) { if (card_type == 9) { if (is_timeline_ad(card_group_item.mblog)) card_group.splice(i, 1); } else if (card_type == 118 || card_type == 89) { card_group.splice(i, 1); } else if (card_type == 42) { if (card_group_item.desc == '\u53ef\u80fd\u611f\u5174\u8da3\u7684\u4eba') { cards.splice(j, 1); break; } } } } } else { let card_type = item.card_type; if (card_type && card_type == 9) { if (is_timeline_ad(item.mblog)) cards.splice(j, 1); } } } } return cards; } function is_timeline_ad(mblog) { if (!mblog) return false; let promotiontype = mblog.promotion && mblog.promotion.type && mblog.promotion.type == "ad"; let mblogtype = mblog.mblogtype && mblog.mblogtype == 1; return (promotiontype || mblogtype) ? true : false; } function is_timeline_likerecommend(title) { return title && title.type && title.type == "likerecommend" ? true : false; } ================================================ FILE: wb_launch.js ================================================ /* README:https://github.com/yichahucha/surge/tree/master */ const path1 = "/interface/sdk/sdkad.php"; const path2 = "/wbapplua/wbpullad.lua"; const url = $request.url; var body = $response.body; if (url.indexOf(path1) != -1) { let re = /\{.*\}/; body = body.match(re); var obj = JSON.parse(body); if (obj.background_delay_display_time) obj.background_delay_display_time = 60*60*24*365; if (obj.show_push_splash_ad) obj.show_push_splash_ad = false; if (obj.ads) obj.ads = []; body = JSON.stringify(obj) + 'OK'; } if (url.indexOf(path2) != -1) { var obj = JSON.parse(body); if (obj.cached_ad && obj.cached_ad.ads) obj.cached_ad.ads = []; body = JSON.stringify(obj); } $done({body});