Open the F**king URL Right Now

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

目前为 2023-04-10 提交的版本,查看 最新版本

  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*://www.360doc.cn/outlink.html?url=*
  9. // @match http://redir.yy.duowan.com/warning.php?url=*
  10. // @match http://t.cn/*
  11. // @match http://www.360doc.com/content/*
  12. // @match https://afdian.net/link?target=*
  13. // @match https://bbs.nga.cn/read.php?*
  14. // @match https://blog.51cto.com/transfer?*
  15. // @match https://developers.weixin.qq.com/community/middlepage/href?href=*
  16. // @match https://docs.qq.com/scenario/link.html?u=*
  17. // @match https://docs.qq.com/scenario/link.html?url=*
  18. // @match https://game.bilibili.com/linkfilter/?url=*
  19. // @match https://gitee.com/link?target=*
  20. // @match https://jump2.bdimg.com/safecheck/index?url=*
  21. // @match https://leetcode.cn/link/?target=*
  22. // @match https://link.csdn.net/?target=*
  23. // @match https://link.juejin.cn/?target=*
  24. // @match https://link.ld246.com/forward?goto=*
  25. // @match https://link.logonews.cn/?*
  26. // @match https://link.uisdc.com/?redirect=*
  27. // @match https://mail.qq.com/cgi-bin/readtemplate*
  28. // @match https://mp.weixin.qq.com/s/*
  29. // @match https://nga.178.com/read.php?*
  30. // @match https://ref.gamer.com.tw/redir.php/url=*
  31. // @match https://sspai.com/link?target=*
  32. // @match https://steamcommunity.com/linkfilter/?url=*
  33. // @match https://t.me/iv?url=*
  34. // @match https://tieba.baidu.com/mo/q/checkurl?url=*
  35. // @match https://weibo.cn/sinaurl?*
  36. // @match https://weixin110.qq.com/cgi-bin/mmspamsupport-bin/newredirectconfirmcgi*
  37. // @match https://www.bookmarkearth.com/view/*
  38. // @match https://www.chinaz.com/go.shtml?url=*
  39. // @match https://www.coolapk.com/link?url=*
  40. // @match https://www.curseforge.com/linkout?remoteUrl=*
  41. // @match https://www.douban.com/link2/?url=*
  42. // @match https://www.instagram.com/linkshim/?u=*
  43. // @match https://www.jianshu.com/go-wild?*
  44. // @match https://www.kookapp.cn/go-wild.html?url=*
  45. // @match https://www.linkedin.com/safety/go?url=*
  46. // @match https://www.mcbbs.net/plugin.php?id=link_redirect&target=*
  47. // @match https://www.oschina.net/action/GoToLink?url=*
  48. // @match https://www.pixiv.net/jump.php?url=*
  49. // @match https://www.qcc.com/web/transfer-link?link=*
  50. // @match https://www.tianyancha.com/security?target=*
  51. // @match https://www.yuque.com/r/goto?url=*
  52. // @match https://xie.infoq.cn/link?target=*
  53. // @exclude https://mp.weixin.qq.com/cgi-bin/*
  54. // @version 1.5.1
  55. // @run-at document-idle
  56. // @namespace https://old-panda.com/
  57. // @require https://cdn.staticfile.org/jquery/3.6.3/jquery.min.js
  58. // @license GPLv3 License
  59. // ==/UserScript==
  60.  
  61. const $ = jQuery.noConflict(true);
  62.  
  63. /**
  64. * @enum {string}
  65. * @name fuckers
  66. * @description all link pattern needed deal with
  67. */
  68. const fuckers = {
  69. afdian: { match: 'https://afdian.net/link?target=', redirect: "target" },
  70. bookmarkearth: { match: 'https://www.bookmarkearth.com/view/', redirect: function () { window.location.replace(document.querySelector("p.link").innerHTML) } },
  71. chinaz: { match: 'https://www.chinaz.com/go.shtml?url=', redirect: "url" },
  72. coolapk: { match: 'https://www.coolapk.com/link?url=', redirect: "url" },
  73. csdn: { match: 'https://link.csdn.net/?target=', redirect: "target" },
  74. cto51: { match: 'https://blog.51cto.com/transfer?', redirect: function () { window.location.href = window.location.href.replace("https://blog.51cto.com/transfer?", "") } },
  75. curseforge: { match: 'https://www.curseforge.com/linkout?remoteUrl=', redirect: function () { window.location.replace(document.querySelector(".root-content a.button").href) } },
  76. dilian: { match: 'https://link.ld246.com/forward?goto=', redirect: "goto" },
  77. doc360_2: { match: 'http://www.360doc.cn/outlink.html?url=', redirect: "url" },
  78. doc360: { match: 'http://www.360doc.com/content/', redirect: function () { $("#articlecontent table tbody tr td#artContent").find("a").off("click") } },
  79. douban: { match: 'https://www.douban.com/link2/?url=', redirect: "url" },
  80. gamebilibili: { match: 'https://game.bilibili.com/linkfilter/?url=', redirect: "url" },
  81. gamertw: { match: 'https://ref.gamer.com.tw/redir.php/?url=', redirect: "url" },
  82. gitee: { match: 'https://gitee.com/link?target=', redirect: "target" },
  83. infoq: { match: 'https://xie.infoq.cn/link?target=', redirect: "target" },
  84. instagram: { match: 'https://www.instagram.com/linkshim/?u=', redirect: "url" },
  85. jianshu: { match: 'https://www.jianshu.com/go-wild?', redirect: "url" },
  86. juejin: { match: 'https://link.juejin.cn/?target=', redirect: "target" },
  87. kook: { match: 'https://www.kookapp.cn/go-wild.html?url=', redirect: "url" },
  88. leetcode: { match: 'https://leetcode.cn/link/?target', redirect: "target" },
  89. linkedin: { match: 'https://www.linkedin.com/safety/go?url=', redirect: "url" },
  90. logonews: { match: 'https://link.logonews.cn/?', redirect: "url" },
  91. mcbbs: { match: 'https://www.mcbbs.net/plugin.php?id=link_redirect&target=', redirect: "target" },
  92. nga: { match: 'https://nga.178.com/read.php?', redirect: function () { $("#m_posts #m_posts_c a").prop("onclick", null).off("click") } },
  93. nga2: { match: 'https://bbs.nga.cn/read.php?', redirect: function () { $("#m_posts #m_posts_c a").prop("onclick", null).off("click") } },
  94. oschina: { match: 'https://www.oschina.net/action/GoToLink?url=', redirect: "url" },
  95. pixiv: { match: 'https://www.pixiv.net/jump.php?url=', redirect: "url" },
  96. qcc: { match: 'https://www.qcc.com/web/transfer-link?link=', redirect: "link" },
  97. qq: { match: 'https://c.pc.qq.com/(middlem|index).html', redirect: "pfurl", enableRegex: true },
  98. qqios: { match: 'https://c.pc.qq.com/ios.html', redirect: "url" },
  99. qqdocs: { match: 'https://docs.qq.com/scenario/link.html?url=', redirect: "url" },
  100. qqmail: { match: 'https://mail.qq.com/cgi-bin/readtemplate', redirect: "gourl" },
  101. sspai: { match: 'https://sspai.com/link?target=', redirect: "target" },
  102. steam: { match: 'https://steamcommunity.com/linkfilter/?url=', redirect: "url" },
  103. telegram: { match: 'https://t.me/iv?url=', redirect: "url" },
  104. tianyancha: { match: 'https://www.tianyancha.com/security?target=', redirect: "target" },
  105. tieba: { match: 'https://jump2.bdimg.com/safecheck/index?url=', redirect: function () { window.location.replace(document.getElementsByClassName('btn')[0].getAttribute('href')) } },
  106. tieba_2: { match: 'https://tieba.baidu.com/mo/q/checkurl?url=', redirect: "url" },
  107. uisdc: { match: 'https://link.uisdc.com/?redirect=', redirect: "redirect" },
  108. wechat1: { match: 'https://mp.weixin.qq.com/s/', redirect: enableURLs },
  109. wechat2: { match: 'https://weixin110.qq.com/cgi-bin/mmspamsupport-bin/newredirectconfirmcgi', redirect: function () { window.location.replace($(".weui-msg__desc").first().text()) } },
  110. // http://t.cn/RgAKoPE
  111. // https://weibo.cn/sinaurl?luicode=10000011&lfid=230259&u=http%3A%2F%2Ft.cn%2FA6qHeVlf
  112. // https://weibo.cn/sinaurl?toasturl=https%3A%2F%2Ftime.geekbang.org%2F
  113. // https://weibo.cn/sinaurl?u=https%3A%2F%2Fwww.freebsd.org%2F
  114. weibo_1: { match: 'http://t.cn/', redirect: function () { const link = $(".wrap .link").first().text() || document.querySelector('.open-url').children[0].href; window.location.replace(link); } }, // 微博网页版
  115. weibo_2: { match: 'https://weibo.cn/sinaurl?u', redirect: "u" },
  116. weibo_3: { match: 'https://weibo.cn/sinaurl?toasturl', redirect: "toasturl" },
  117. weibo_4: { match: 'https://weibo.cn/sinaurl?', redirect: function () { const link = $(".wrap .link").first().text() || document.querySelector('.open-url').children[0].href; window.location.replace(link); } },
  118. weixindev: { match: 'https://developers.weixin.qq.com/community/middlepage/href?href=', redirect: "href" },
  119. yuque: { match: 'https://www.yuque.com/r/goto?url=', redirect: "url" },
  120. yy: { match: 'http://redir.yy.duowan.com/warning.php?url=', redirect: "url" },
  121. zaker: { match: 'http://iphone.myzaker.com/zaker/link.php?', redirect: function () { redirect(curURL, "url", true) } },
  122. // https://link.zhihu.com/?target=https%3A%2F%2Ftime.geekbang.org%2F
  123. // https://link.zhihu.com/?utm_oi=35221042888704&target=https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/import
  124. zhihu: { match: 'https://link.zhihu.com/?', redirect: "target" },
  125. }
  126.  
  127. const curURL = window.location.href;
  128. const urlPattern = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g;
  129.  
  130. /**
  131. * Return URL without "http://" or "https://" at the beginning
  132. * @param {String} str
  133. */
  134. function removeProtocol(str) {
  135. return str.replace(/^https?\??:\/\//gm, '');
  136. }
  137.  
  138. function rstrip(str, regex) {
  139. let i = str.length - 1;
  140. while (i >= 0) {
  141. if (!str[i].match(regex)) {
  142. break;
  143. }
  144. i--;
  145. }
  146. return str.substring(0, i + 1);
  147. }
  148.  
  149. /**
  150. * Split concatenated URL string into separate URLs.
  151. * @param {String} str
  152. */
  153. function splitMultiURLs(str) {
  154. //TODO: add comments
  155. let results = new Array();
  156. let entry = "";
  157. while (str.length > 0) {
  158. if (str.indexOf("http:") === -1 && str.indexOf("https:") === -1) {
  159. entry += str;
  160. str = "";
  161. results.push(rstrip(entry, /[@:%_\+~#?&=,$^\*]/g));
  162. break;
  163. }
  164.  
  165. if (str.startsWith("http:")) {
  166. entry += "http:";
  167. str = str.substring("http:".length);
  168. } else if (str.startsWith("https:")) {
  169. entry += "https:";
  170. str = str.substring("https:".length);
  171. } else {
  172. return results;
  173. }
  174.  
  175. let nextIndex = Math.min(
  176. str.indexOf("https:") === -1 ? Number.MAX_SAFE_INTEGER : str.indexOf("https:"),
  177. str.indexOf("http:") === -1 ? Number.MAX_SAFE_INTEGER : str.indexOf("http:")
  178. );
  179. if (nextIndex > 0) {
  180. entry += str.substring(0, nextIndex);
  181. str = str.substring(nextIndex);
  182. }
  183. results.push(rstrip(entry, /[@:%_\+~#?&=,$^\*]/g));
  184. entry = "";
  185. }
  186. return results;
  187. }
  188.  
  189. /**
  190. * Replace url with clickable `<a>` tag in html content.
  191. * @param {String} url
  192. */
  193. function replaceSingleURL(url) {
  194. $("#js_content").html((_, html) => {
  195. return html.replaceAll(url, `<a target="_blank" rel="noopener noreferrer" href="${url}">${url}</a>`);
  196. });
  197. }
  198.  
  199. /**
  200. * Make urls clickable again on Weixin Media Platform.
  201. */
  202. function enableURLs() {
  203. let existingLinks = new Set();
  204. $("a").each(function () {
  205. existingLinks.add(this.href);
  206. });
  207.  
  208. $("#js_content > section").each(function (_, obj) {
  209. // Don't do anything on code blocks
  210. let className = $(obj).attr('class');
  211. if (className != undefined && className.indexOf("code-snippet__js") != -1) {
  212. return;
  213. }
  214. let content = $(obj).text();
  215. let urls = content.matchAll(urlPattern);
  216. let replaced = new Set();
  217. for (let value of urls) {
  218. let urlStr = $.trim(value[0]);
  219. for (let url of splitMultiURLs(urlStr)) {
  220. if (!url || replaced.has(url) || url.includes("localhost") || url.includes("127.0.0.1") || existingLinks.has(url)) {
  221. continue;
  222. }
  223. if (url.endsWith(".") && url[url.length - 2].match(/\d/g)) {
  224. url = url.substring(0, url.length - 2);
  225. }
  226. replaceSingleURL(url);
  227. replaced.add(url);
  228. }
  229. }
  230. });
  231.  
  232. // Replace loading image with actual one
  233. $("img").each(function (_, obj) {
  234. if ($(obj)[0].currentSrc === "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==") {
  235. $(obj).attr("src", $(obj)[0].dataset.src);
  236. } else {
  237. $(obj).attr("src", $(obj).attr("data-src"));
  238. $(obj).attr("style", "width: 100% !important; height: auto !important; display: initial; visibility: visible !important;");
  239. }
  240. });
  241. $("span.js_img_placeholder").remove();
  242. }
  243.  
  244. function redirect(fakeURLStr, trueURLParam, enableBase64 = false) {
  245. let fakeURL = new URL(fakeURLStr);
  246. let trueURL = fakeURL.searchParams.get(trueURLParam);
  247. if (trueURL.startsWith(fuckers.wechat1.match)) {
  248. // there could be multiple `&`s in url of a wechat link, so all of them
  249. // have to be included in the trueURL.
  250. trueURL = fakeURL.search.split(`${trueURLParam}=`).pop();
  251. } else {
  252. if (enableBase64) trueURL = window.atob(trueURL);
  253. if (trueURL.indexOf("http://") !== 0 && trueURL.indexOf("https://") !== 0) {
  254. trueURL = "https://" + trueURL;
  255. }
  256. }
  257. window.location.replace(trueURL);
  258. }
  259.  
  260. /**
  261. * @function
  262. * @name match
  263. * @param {...string} pattern
  264. * @param {...boolean} enableRegex
  265. * @param {...boolean} checkProtocol
  266. * @description check if current URL matchs given patterns
  267. */
  268. function match(pattern, enableRegex = false, checkProtocol = false) {
  269. var curURLProto;
  270. if (checkProtocol) { curURLProto = curURL; }
  271. else {
  272. curURLProto = removeProtocol(curURL);
  273. pattern = removeProtocol(pattern);
  274. }
  275. if (enableRegex) {
  276. return curURLProto.search(pattern) > -1
  277. }
  278. else {
  279. return curURLProto.indexOf(pattern) === 0//Not Sure
  280. }
  281. }
  282.  
  283. (function () {
  284. 'use strict';
  285.  
  286. $(document).ready(function () {
  287. for (var i in fuckers) {
  288. if (match(fuckers[i].match, fuckers[i].enableRegex, fuckers[i].checkProtocol)) {
  289. switch (typeof (fuckers[i].redirect)) {
  290. case 'string':
  291. redirect(curURL, fuckers[i].redirect); break;
  292. case 'function':
  293. fuckers[i].redirect(); break;
  294. default:
  295. console.log(i + " redirect rule error!"); break;
  296. }
  297. }
  298. }
  299. });
  300.  
  301. })();