Open the F**king URL Right Now

自动跳转某些网站不希望用户直达的外链

  1. // ==UserScript==
  2. // @name Open the F**king URL Right Now
  3. // @description 自动跳转某些网站不希望用户直达的外链
  4. // @author OldPanda
  5. // @match http*://c.pc.qq.com/*
  6. // @match http*://iphone.myzaker.com/zaker/link.php?*
  7. // @match http*://link.zhihu.com/?*
  8. // @match http*://t.cn/*
  9. // @match http*://www.360doc.cn/outlink.html?url=*
  10. // @match http://redir.yy.duowan.com/warning.php?url=*
  11. // @match http://www.360doc.com/content/*
  12. // @match https://afdian.com/link?target=*
  13. // @match https://afdian.net/link?target=*
  14. // @match https://ask.latexstudio.net/go/index?url=*
  15. // @match https://bbs.acgrip.com/*
  16. // @match https://bbs.nga.cn/read.php?*
  17. // @match https://blog.51cto.com/transfer?*
  18. // @match https://blzxteam.com/gowild.htm?url=*
  19. // @match https://cloud.tencent.com/developer/tools/blog-entry?target=*
  20. // @match https://developers.weixin.qq.com/community/middlepage/href?href=*
  21. // @match https://developer.aliyun.com/redirect?target=*
  22. // @match https://docs.qq.com/scenario/link.html?u=*
  23. // @match https://docs.qq.com/scenario/link.html?url=*
  24. // @match https://game.bilibili.com/linkfilter/?url=*
  25. // @match https://gitee.com/link?target=*
  26. // @match https://hd.nowcoder.com/link.html?target=*
  27. // @match https://hellogithub.com/periodical/statistics/click?target=*
  28. // @match https://jump2.bdimg.com/safecheck/index?url=*
  29. // @match https://leetcode.cn/link/?target=*
  30. // @match https://link.csdn.net/?*target=*
  31. // @match https://link.gitcode.com/?target=*
  32. // @match https://link.juejin.cn/?target=*
  33. // @match https://link.ld246.com/forward?goto=*
  34. // @match https://link.logonews.cn/?*
  35. // @match https://link.uisdc.com/?redirect=*
  36. // @match https://mail.qq.com/cgi-bin/readtemplate*
  37. // @match https://mp.weixin.qq.com/s/*
  38. // @match https://mp.weixin.qq.com/s?*
  39. // @match https://nga.178.com/read.php?*
  40. // @match https://open.work.weixin.qq.com/wwopen/uriconfirm?uri=*
  41. // @match https://ref.gamer.com.tw/redir.php/url=*
  42. // @match https://ref.gamer.com.tw/redir.php?url=*
  43. // @match https://shimo.im/outlink/black?url=*
  44. // @match https://shimo.im/outlink/gray?url=*
  45. // @match https://sspai.com/link?target=*
  46. // @match https://steamcommunity.com/linkfilter/?url=*
  47. // @match https://steamcommunity.com/linkfilter/?u=*
  48. // @match https://support.qq.com/product/*/link-jump?jump=*
  49. // @match https://support.qq.com/products/*/link-jump?jump=*
  50. // @match http*://t.techlife.app/*
  51. // @match https://t.me/iv?url=*
  52. // @match https://tieba.baidu.com/mo/q/checkurl?url=*
  53. // @match https://txc.qq.com/product/*/link-jump?jump=*
  54. // @match https://txc.qq.com/products/*/link-jump?jump=*
  55. // @match https://weibo.cn/sinaurl?*
  56. // @match https://weixin110.qq.com/cgi-bin/mmspamsupport-bin/newredirectconfirmcgi*
  57. // @match https://wx.mail.qq.com/xmspamcheck/xmsafejump?*
  58. // @match https://www.bookmarkearth.com/view/*
  59. // @match https://www.chinaz.com/go.shtml?url=*
  60. // @match https://www.coolapk.com/link?url=*
  61. // @match https://www.curseforge.com/linkout?remoteUrl=*
  62. // @match https://www.douban.com/link2/?url=*
  63. // @match https://www.gcores.com/link?target=*
  64. // @match https://www.google.com/url?q=*
  65. // @match https://www.instagram.com/linkshim/?u=*
  66. // @match https://www.jianshu.com/go-wild?*
  67. // @match https://www.kookapp.cn/go-wild.html?url=*
  68. // @match https://www.linkedin.com/safety/go?url=*
  69. // @match https://www.luogu.com.cn/discuss/*
  70. // @match https://www.luogu.com.cn/paste/*
  71. // @match https://www.luogu.com.cn/article/*
  72. // @match https://www.mczwlt.net/go-external?url=*
  73. // @match https://www.nodeseek.com/jump?to=*
  74. // @match https://www.oschina.net/action/GoToLink?url=*
  75. // @match https://www.pixiv.net/jump.php?*
  76. // @match https://www.qcc.com/web/transfer-link?link=*
  77. // @match https://www.tianyancha.com/security?target=*
  78. // @match https://www.yuque.com/r/goto?url=*
  79. // @match https://*.infoq.cn/link?target=*
  80. // @match https://www.baike.com/redirect_link?url=*
  81. // @match https://www.youtube.com/redirect?*
  82. // @exclude https://mp.weixin.qq.com/cgi-bin/*
  83. // @version 1.14.3
  84. // @run-at document-idle
  85. // @namespace https://old-panda.com/
  86. // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js
  87. // @license GPLv3 License
  88. // ==/UserScript==
  89.  
  90. const $ = jQuery.noConflict(true);
  91.  
  92. /**
  93. * @enum {string}
  94. * @name fuckers
  95. * @description all link pattern needed deal with
  96. */
  97. const fuckers = {
  98. acgrip: { match: 'https://bbs.acgrip.com/', redirect: removeFwinDialog },
  99. afdian: { match: 'https://afdian.net/link?target=', redirect: "target" },
  100. afdian2: { match: 'https://afdian.com/link?target=', redirect: "target" },
  101. baike: { match: 'https://www.baike.com/redirect_link?url=', redirect: "url" },
  102. blzxteam: { match: 'https://blzxteam.com/gowild.htm?url=', redirect: function () { const url = $("div._2VEbEOHfDtVWiQAJxSIrVi_0").first().attr("title"); window.location.href = url } },
  103. bookmarkearth: { match: 'https://www.bookmarkearth.com/view/', redirect: function () { window.location.replace(document.querySelector("p.link").innerHTML) } },
  104. chinaz: { match: 'https://www.chinaz.com/go.shtml?url=', redirect: "url" },
  105. coolapk: { match: 'https://www.coolapk.com/link?url=', redirect: "url" },
  106. csdn: { match: 'https://link.csdn.net/?*target=', redirect: "target" },
  107. cto51: { match: 'https://blog.51cto.com/transfer?', redirect: function () { window.location.href = window.location.href.replace("https://blog.51cto.com/transfer?", "") } },
  108. curseforge: { match: 'https://www.curseforge.com/linkout?remoteUrl=', redirect: function () { redirect(decodeURIComponent(curURL), 'remoteUrl') } },
  109. dilian: { match: 'https://link.ld246.com/forward?goto=', redirect: "goto" },
  110. developeraliyun: { match: 'https://developer.aliyun.com/redirect?target=', redirect: "target" },
  111. doc360_2: { match: 'http://www.360doc.cn/outlink.html?url=', redirect: "url" },
  112. doc360: { match: 'http://www.360doc.com/content/', redirect: function () { $("#articlecontent table tbody tr td#artContent").find("a").off("click") } },
  113. douban: { match: 'https://www.douban.com/link2/?url=', redirect: "url" },
  114. gamebilibili: { match: 'https://game.bilibili.com/linkfilter/?url=', redirect: "url" },
  115. gamertw: { match: 'https://ref.gamer.com.tw/redir.php/?url=', redirect: "url" },
  116. gamertw_2: { match: 'https://ref.gamer.com.tw/redir.php?url=', redirect: "url" },
  117. gcores: { match: 'https://www.gcores.com/link?target=', redirect: "target" },
  118. gitcode: { match: 'https://link.gitcode.com/?target=', redirect: "target" },
  119. gitee: { match: 'https://gitee.com/link?target=', redirect: "target" },
  120. google: { match: 'https://www.google.com/url?q=', redirect: "q" },
  121. hellogithub: { match: 'https://hellogithub.com/periodical/statistics/click?target=', redirect: "target" },
  122. infoq: { match: 'https://(xie.infoq.cn/link|www.infoq.cn/link)?target=', redirect: "target", enableRegex: true },
  123. instagram: { match: 'https://www.instagram.com/linkshim/?u=', redirect: "url" },
  124. jianshu: { match: 'https://www.jianshu.com/go-wild?', redirect: "url" },
  125. juejin: { match: 'https://link.juejin.cn/?target=', redirect: "target" },
  126. kook: { match: 'https://www.kookapp.cn/go-wild.html?url=', redirect: "url" },
  127. latexstudio: { match: 'https://ask.latexstudio.net/go/index?url=', redirect: "url" },
  128. leetcode: { match: 'https://leetcode.cn/link/?target', redirect: "target" },
  129. linkedin: { match: 'https://www.linkedin.com/safety/go?url=', redirect: "url" },
  130. logonews: { match: 'https://link.logonews.cn/?', redirect: "url" },
  131. luogu: { match: 'https://www.luogu.com.cn/paste/', redirect: function () { if (document.getElementById("url")) { window.location.href = $("#url").text() } } },
  132. luogu_2: { match: 'https://www.luogu.com.cn/discuss/', redirect: function () { if (document.getElementById("url")) { window.location.href = $("#url").text() } } },
  133. luogu_3: { match: 'https://www.luogu.com.cn/article/', redirect: function () { if (document.getElementById("url")) { window.location.href = $("#url").text() } } },
  134. mczwlt: { match: 'https://www.mczwlt.net/go-external?url=', redirect: "url" },
  135. nga: { match: 'https://nga.178.com/read.php?', redirect: function () { $("#m_posts #m_posts_c a").prop("onclick", null).off("click") } },
  136. nga2: { match: 'https://bbs.nga.cn/read.php?', redirect: function () { $("#m_posts #m_posts_c a").prop("onclick", null).off("click") } },
  137. nodeseek: { match: 'https://www.nodeseek.com/jump?to=', redirect: "to" },
  138. nowcoder: { match: 'https://hd.nowcoder.com/link.html?target=', redirect: "target" },
  139. oschina: { match: 'https://www.oschina.net/action/GoToLink?url=', redirect: "url" },
  140. pixiv: { match: 'https://www.pixiv.net/jump.php?', redirect: function () { window.location.href = decodeURIComponent(curURL.match(/jump.php\?[url=]*(.*)/)[1].replace(/=$/, '')) } },
  141. qcc: { match: 'https://www.qcc.com/web/transfer-link?link=', redirect: "link" },
  142. qq: { match: 'https://c.pc.qq.com/(middleb|middlect|middlem|index).html', redirect: "pfurl", enableRegex: true },
  143. qq2: { match: 'https://c.pc.qq.com/(ios|pc|android).html', redirect: "url", enableRegex: true },
  144. qqdocs: { match: 'https://docs.qq.com/scenario/link.html?url=', redirect: "url" },
  145. qqmail: { match: 'https://mail.qq.com/cgi-bin/readtemplate', redirect: "gourl" },
  146. qqmailwx: { match: 'https://wx.mail.qq.com/xmspamcheck/xmsafejump', redirect: "url" },
  147. shimo: { match: 'https://shimo.im/outlink/black', redirect: "url" },
  148. shimo_2: { match: 'https://shimo.im/outlink/gray', redirect: "url" },
  149. sspai: { match: 'https://sspai.com/link?target=', redirect: "target" },
  150. steam: { match: 'https://steamcommunity.com/linkfilter/?url=', redirect: "url" },
  151. steam2: { match: 'https://steamcommunity.com/linkfilter/?u=', redirect: "u" },
  152. techlife: { match: 'https://t.techlife.app/#/', redirect: function () { window.location.replace(document.querySelector("#urltext").innerHTML) } },
  153. telegram: { match: 'https://t.me/iv?url=', redirect: "url" },
  154. tencentclouddev: { match: 'https://cloud.tencent.com/developer/tools/blog-entry?target=', redirect: "target" },
  155. tianyancha: { match: 'https://www.tianyancha.com/security?target=', redirect: "target" },
  156. tieba: { match: 'https://jump2.bdimg.com/safecheck/index?url=', redirect: function () { window.location.replace(document.getElementsByClassName('btn')[0].getAttribute('href')) } },
  157. tieba_2: { match: 'https://tieba.baidu.com/mo/q/checkurl?url=', redirect: "url" },
  158. txc: { match: 'https://(txc|support).qq.com/products?/(\\d+)/link-jump', enableRegex: true, redirect: 'jump' },
  159. uisdc: { match: 'https://link.uisdc.com/?redirect=', redirect: "redirect" },
  160. wechat1: { match: 'https://mp.weixin.qq.com/s/', redirect: enableURLs },
  161. wechat2: { match: 'https://weixin110.qq.com/cgi-bin/mmspamsupport-bin/newredirectconfirmcgi', redirect: function () { window.location.replace($(".weui-msg__desc").first().text()) } },
  162. wechat3: { match: 'https://mp.weixin.qq.com/s?', redirect: function () { let elem = $("#js_access_msg"); if (elem !== undefined && elem.attr("href") !== undefined) { window.location.replace(elem.attr("href")); } } },
  163. // https://t.cn/RgAKoPE
  164. // https://weibo.cn/sinaurl?luicode=10000011&lfid=230259&u=http%3A%2F%2Ft.cn%2FA6qHeVlf
  165. // https://weibo.cn/sinaurl?toasturl=https%3A%2F%2Ftime.geekbang.org%2F
  166. // https://weibo.cn/sinaurl?u=https%3A%2F%2Fwww.freebsd.org%2F
  167. weibo_1: {
  168. match: 'https://t.cn/', redirect: function () {
  169. const link = $(".wrap .link").first().text() || document.querySelector('.open-url').children[0].href;
  170. const url = new URL(link);
  171. url.searchParams.delete("continueFlag");
  172. window.location.replace(url.toString());
  173. }
  174. }, // 微博网页版
  175. weibo_2: { match: 'https://weibo.cn/sinaurl?u', redirect: "u" },
  176. weibo_3: { match: 'https://weibo.cn/sinaurl?toasturl', redirect: "toasturl" },
  177. weibo_4: {
  178. match: 'https://weibo.cn/sinaurl?', redirect: function () {
  179. const url = new URL(link);
  180. url.searchParams.delete("continueFlag");
  181. window.location.replace(url.toString());
  182. }
  183. },
  184. weixindev: { match: 'https://developers.weixin.qq.com/community/middlepage/href?href=', redirect: "href" },
  185. work_weixin: { match: 'https://open.work.weixin.qq.com/wwopen/uriconfirm?uri=', redirect: "uri" },
  186. yuque: { match: 'https://www.yuque.com/r/goto?url=', redirect: "url" },
  187. youtube: { match: 'https://www.youtube.com/redirect?', redirect: "q" },
  188. yy: { match: 'http://redir.yy.duowan.com/warning.php?url=', redirect: "url" },
  189. zaker: { match: 'http://iphone.myzaker.com/zaker/link.php?', redirect: function () { redirect(curURL, "url", true) } },
  190. // https://link.zhihu.com/?target=https%3A%2F%2Ftime.geekbang.org%2F
  191. // https://link.zhihu.com/?utm_oi=35221042888704&target=https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/import
  192. zhihu: { match: 'https://link.zhihu.com/?', redirect: "target" },
  193. }
  194.  
  195. const curURL = window.location.href;
  196. const urlPattern = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g;
  197.  
  198. /**
  199. * Return URL without "http://" or "https://" at the beginning
  200. * @param {String} str
  201. */
  202. function removeProtocol(str) {
  203. return str.replace(/^https?\??:\/\//gm, '');
  204. }
  205.  
  206. function rstrip(str, regex) {
  207. let i = str.length - 1;
  208. while (i >= 0) {
  209. if (!str[i].match(regex)) {
  210. break;
  211. }
  212. i--;
  213. }
  214. return str.substring(0, i + 1);
  215. }
  216.  
  217. /**
  218. * Split concatenated URL string into separate URLs.
  219. * @param {String} str
  220. */
  221. function splitMultiURLs(str) {
  222. //TODO: add comments
  223. let results = new Array();
  224. let entry = "";
  225. while (str.length > 0) {
  226. if (str.indexOf("http:") === -1 && str.indexOf("https:") === -1) {
  227. entry += str;
  228. str = "";
  229. results.push(rstrip(entry, /[@:%_\+~#?&=,$^\*]/g));
  230. break;
  231. }
  232.  
  233. if (str.startsWith("http:")) {
  234. entry += "http:";
  235. str = str.substring("http:".length);
  236. } else if (str.startsWith("https:")) {
  237. entry += "https:";
  238. str = str.substring("https:".length);
  239. } else {
  240. return results;
  241. }
  242.  
  243. let nextIndex = Math.min(
  244. str.indexOf("https:") === -1 ? Number.MAX_SAFE_INTEGER : str.indexOf("https:"),
  245. str.indexOf("http:") === -1 ? Number.MAX_SAFE_INTEGER : str.indexOf("http:")
  246. );
  247. if (nextIndex > 0) {
  248. entry += str.substring(0, nextIndex);
  249. str = str.substring(nextIndex);
  250. }
  251. results.push(rstrip(entry, /[@:%_\+~#?&=,$^\*]/g));
  252. entry = "";
  253. }
  254. return results;
  255. }
  256.  
  257. /**
  258. * Replace url with clickable `<a>` tag in html content.
  259. * @param {String} url
  260. */
  261. function replaceSingleURL(url) {
  262. $("#js_content").html((_, html) => {
  263. return html.replaceAll(url, `<a target="_blank" rel="noopener noreferrer" href="${url}">${url}</a>`);
  264. });
  265. }
  266.  
  267. /**
  268. * Make urls clickable again on Weixin Media Platform.
  269. */
  270. function enableURLs() {
  271. let existingLinks = new Set();
  272. $("a").each(function () {
  273. existingLinks.add(this.href);
  274. });
  275.  
  276. $("#js_content > section").each(function (_, obj) {
  277. // Don't do anything on code blocks
  278. let className = $(obj).attr('class');
  279. if (className != undefined && className.indexOf("code-snippet__js") != -1) {
  280. return;
  281. }
  282. let content = $(obj).text();
  283. let urls = content.matchAll(urlPattern);
  284. let replaced = new Set();
  285. for (let value of urls) {
  286. let urlStr = $.trim(value[0]);
  287. for (let url of splitMultiURLs(urlStr)) {
  288. if (!url || replaced.has(url) || url.includes("localhost") || url.includes("127.0.0.1") || existingLinks.has(url)) {
  289. continue;
  290. }
  291. if (url.endsWith(".") && url[url.length - 2].match(/\d/g)) {
  292. url = url.substring(0, url.length - 2);
  293. }
  294. replaceSingleURL(url);
  295. replaced.add(url);
  296. }
  297. }
  298. });
  299.  
  300. // Replace loading image with actual one
  301. $("img").each(function (_, obj) {
  302. if ($(obj)[0].currentSrc === "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==") {
  303. $(obj).attr("src", $(obj)[0].dataset.src);
  304. } else {
  305. $(obj).attr("src", $(obj).attr("data-src"));
  306. $(obj).attr("style", "width: 100% !important; height: auto !important; display: initial; visibility: visible !important;");
  307. }
  308. });
  309. $("span.js_img_placeholder").remove();
  310. }
  311.  
  312. /**
  313. * Remove the fwin_dialog when clicking external links on Anime Subtitle Club
  314. */
  315. function removeFwinDialog() {
  316. $("a").each((_, elem) => {
  317. if (elem.href && elem.href.startsWith("http") && !elem.href.includes(window.location.host)) {
  318. elem.addEventListener("click", (_) => {
  319. window.open(elem.href, "_blank");
  320. hideMenu('fwin_dialog', 'dialog');
  321. })
  322. }
  323. })
  324. }
  325.  
  326. function redirect(fakeURLStr, trueURLParam, enableBase64 = false) {
  327. let fakeURL = new URL(fakeURLStr);
  328. let trueURL = fakeURL.searchParams.get(trueURLParam);
  329. if (trueURL.startsWith(fuckers.wechat1.match)) {
  330. // there could be multiple `&`s in url of a wechat link, so all of them
  331. // have to be included in the trueURL.
  332. trueURL = fakeURL.search.split(`${trueURLParam}=`).pop();
  333. } else {
  334. if (enableBase64) trueURL = window.atob(trueURL);
  335. if (trueURL.indexOf("http://") !== 0 && trueURL.indexOf("https://") !== 0) {
  336. trueURL = "https://" + trueURL;
  337. }
  338. }
  339. trueURL = decodeURIComponent(trueURL)
  340. window.location.replace(trueURL);
  341. }
  342.  
  343. /**
  344. * @function
  345. * @name match
  346. * @param {...string} pattern
  347. * @param {...boolean} enableRegex
  348. * @param {...boolean} checkProtocol
  349. * @description check if current URL matchs given patterns
  350. */
  351. function match(pattern, enableRegex = false, checkProtocol = false) {
  352. var curURLProto;
  353. if (checkProtocol) { curURLProto = curURL; }
  354. else {
  355. curURLProto = removeProtocol(curURL);
  356. pattern = removeProtocol(pattern);
  357. }
  358. if (enableRegex) {
  359. return curURLProto.search(pattern) > -1
  360. }
  361. else {
  362. return curURLProto.indexOf(pattern) === 0//Not Sure
  363. }
  364. }
  365.  
  366. (function () {
  367. 'use strict';
  368.  
  369. $(document).ready(function () {
  370. for (var i in fuckers) {
  371. if (match(fuckers[i].match, fuckers[i].enableRegex, fuckers[i].checkProtocol)) {
  372. switch (typeof (fuckers[i].redirect)) {
  373. case 'string':
  374. redirect(curURL, fuckers[i].redirect); break;
  375. case 'function':
  376. fuckers[i].redirect(); break;
  377. default:
  378. console.log(i + " redirect rule error!"); break;
  379. }
  380. }
  381. }
  382. });
  383.  
  384. })();