open the link directly

点击链接直接跳转

当前为 2022-03-27 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name open the link directly
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1.2
  5. // @description 点击链接直接跳转
  6. // @author nediiii
  7. // @match *://*.csdn.net/*
  8. // @match *://*.gitee.com/*
  9. // @match *://*.uisdc.com/*
  10. // @match *://*.logonews.cn/*
  11. // @match *://*.afdian.net/*
  12. // @match *://*.tianyancha.com/*
  13. // @match *://*.oschina.net/*
  14. // @match *://*.pixiv.net/*
  15. // @match *://*.jianshu.com/*
  16. // @match *://*.juejin.cn/*
  17. // @match *://*.weibo.cn/*
  18. // @match *://*.weibo.com/*
  19. // @match *://*.yuque.com/*
  20. // @match *://*.segmentfault.com/*
  21. // @match *://*.zhihu.com/*
  22. // @match *://*.bookmarkearth.com/*
  23. // @license GPLv3 License
  24. // @icon https://www.google.com/s2/favicons?sz=64&domain=greasyfork.org
  25. // @grant GM_xmlhttpRequest
  26. // ==/UserScript==
  27.  
  28. (function () {
  29. 'use strict';
  30.  
  31. const isValidURL = (url) => {
  32. try {
  33. new URL(url);
  34. return true;
  35. } catch (error) {
  36. return isValidURLWithBase(url);
  37. }
  38. }
  39.  
  40. const isValidURLWithBase = (url) => {
  41. try {
  42. new URL(url, getCurrentURLBase());
  43. return true;
  44. } catch (error) {
  45. return false;
  46. }
  47. }
  48.  
  49. const getCurrentURLBase = () => {
  50. return window.location.origin;
  51. }
  52.  
  53. const urlReg = /\bhttps?:\/\/\S+/gi;
  54.  
  55. const weiboResolver = async (href) => {
  56. return new Promise((resolve, reject) => {
  57. GM_xmlhttpRequest({
  58. method: "GET",
  59. url: href,
  60. onload: function (response) {
  61. console.log({ response });
  62. if (response.status != 200) {
  63. reject(href);
  64. return;
  65. }
  66.  
  67. // weibo跳转会区分目标网址是否备案
  68. // 未备案的, 中转
  69. // 已备案的, 直跳
  70. let realURI = href;
  71. if (response.finalUrl === href) {
  72. // 未备案, 中转网址
  73. // 网址在html里
  74. let doc = new DOMParser().parseFromString(response.responseText, "text/html");
  75. let node = doc.querySelector('body > div > div:nth-child(2)');
  76. let str = node.innerText;
  77. console.log({ doc });
  78. console.log({ node });
  79. console.log({ str });
  80.  
  81. let extractURL = str.match(urlReg);
  82. console.log({ extractURL });
  83. if (extractURL) {
  84. realURI = extractURL[0];
  85. }
  86. }
  87. else {
  88. // 已备案
  89. // 网址在finalUrl里
  90. let extractURL = response.finalUrl.match(urlReg);
  91. if (extractURL) {
  92. realURI = extractURL[0];
  93. }
  94. }
  95.  
  96. resolve(realURI)
  97. },
  98. onerror: function (error) {
  99. console.log({ error });
  100. reject(href)
  101. }
  102. });
  103. });
  104. }
  105.  
  106. const segfaultResolver = async (href) => {
  107. return new Promise((resolve, reject) => {
  108. GM_xmlhttpRequest({
  109. method: "GET",
  110. url: href,
  111. onload: function (response) {
  112. console.log({ response });
  113. if (response.status == 200) {
  114. let doc = new DOMParser().parseFromString(response.responseText, "text/html");
  115. let node = doc.querySelector('body > p')
  116. let str = node.innerText;
  117. console.log({ doc });
  118. console.log({ node });
  119. console.log({ str });
  120.  
  121. let realURI = href;
  122. let extractURL = str.match(urlReg);
  123. console.log({ extractURL });
  124. if (extractURL) {
  125. realURI = extractURL[0];
  126. }
  127. resolve(realURI)
  128. } else {
  129. reject(href)
  130. }
  131. },
  132. onerror: function (error) {
  133. console.log({ error });
  134. reject(href)
  135. }
  136. });
  137. });
  138. }
  139.  
  140. const bmeResolver = async (href) => {
  141. return new Promise((resolve, reject) => {
  142. GM_xmlhttpRequest({
  143. method: "GET",
  144. url: href,
  145. onload: function (response) {
  146. console.log({ response });
  147. if (response.status == 200) {
  148. let doc = new DOMParser().parseFromString(response.responseText, "text/html");
  149. let node = doc.querySelector('body > div.row.box > div.col-lg-6.jump-box > div > div.content > p.link');
  150. let str = node.innerText;
  151. console.log({ doc });
  152. console.log({ node });
  153. console.log({ str });
  154.  
  155. let realURI = href;
  156. let extractURL = str.match(urlReg);
  157. console.log({ extractURL });
  158. if (extractURL) {
  159. realURI = extractURL[0];
  160. }
  161. resolve(realURI)
  162. } else {
  163. reject(href)
  164. }
  165. },
  166. onerror: function (error) {
  167. console.log({ error });
  168. reject(href)
  169. }
  170. });
  171. });
  172. }
  173.  
  174. const patter_match = {
  175.  
  176. // 注意这里的pattern需要去看对应网站dom里的a标签的实际herf值, console也会打印日志, 可以自己添加正则来增加网站支持
  177. // https://link.zhihu.com/?target=https%3A//greasyfork.org/en/scripts/5029-yet-another-%25E8%2587%25AA%25E5%258F%25A4cb%25E5%2587%25BA%25E8%25AF%2584%25E8%25AE%25BA-sharing-plugin
  178. zhihu: { pattern: /https?:\/\/link\.zhihu\.com\/?\?target=(.+)$/ },
  179.  
  180. // https://www.jianshu.com/p/a6a63a0c6e53
  181. // https://links.jianshu.com/go?to=https%3A%2F%2Fnpm.taobao.org%2Fmirrors%2Felectron
  182. jianshu2: { pattern: /https?:\/\/links\.jianshu\.com\/go\?to=(.+)$/ },
  183.  
  184. // href="https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.aliyun.com%2Fgroup%2Falisoftwaretech%2F"
  185. juejin: { pattern: /https?:\/\/link\.juejin\.cn\/?\?target=(.+)$/ },
  186.  
  187. // https://gitee.com/meetqy/acss-dnd
  188. // href="https://gitee.com/link?target=https%3A%2F%2Fcuyang.me%2Facss-dnd%2F"
  189. gitee: { pattern: /https?:\/\/gitee\.com\/link\?target=(.+)$/ },
  190.  
  191. // https://www.uisdc.com/build-b-end-grid-system
  192. // https://link.uisdc.com/?redirect=https%3A%2F%2Fuxdesign.cc%2Fresponsive-grids-and-how-to-actually-use-them-970de4c16e01
  193. uisdc: { pattern: /https?:\/\/link\.uisdc\.com\/?\?redirect=(.+)$/ },
  194.  
  195. // https://www.logonews.cn/apple-sues-sex-topic-blog-for-logo-infringement.html
  196. // https://link.logonews.cn/?url=http://aasd.k12.wi.us/district
  197. logonews: { pattern: /https?:\/\/link\.logonews\.cn\/?\?url=(.+)$/ },
  198.  
  199. // https://afdian.net/@AdventCirno
  200. // https://afdian.net/link?target=https%3A%2F%2Fwww.patreon.com%2Fuser%3Fu%3D6139561
  201. afdian: { pattern: /https?:\/\/afdian\.net\/link\?target=(.+)$/ },
  202.  
  203. // https://www.tianyancha.com/company/28723141
  204. // https://www.tianyancha.com/security?target=https%3A%2F%2Fss.knet.cn%2Fverifyseal.dll%3Fsn%3De18042711010873571xsuv000000%26pa%3D111332
  205. tianyancha: { pattern: /https?:\/\/www\.tianyancha\.com\/security\?target=(.+)$/ },
  206.  
  207. // https://www.pixiv.net/users/25237
  208. // href="/jump.php?url=https%3A%2F%2Ftwitter.com%2Fomiya_io"
  209. // href="/jump.php?https%3A%2F%2Finstagram.com%2Fsnatti89%2F"
  210. pixiv: { pattern: /\/jump\.php\?(?:url=)?(.+)$/ },
  211.  
  212. // https://my.oschina.net/androiddevs/blog/5496556
  213. // https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fmp.weixin.qq.com%2Fmp%2Fappmsgalbum%3F__biz%3DMzk0NDIwMTExNw%3D%3D%26action%3Dgetalbum%26album_id%3D1879128471667326981%23wechat_redirect
  214. // href="https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fmp.weixin.qq.com%2Fmp%2Fappmsgalbum%3F__biz%3DMzk0NDIwMTExNw%3D%3D%26action%3Dgetalbum%26album_id%3D1879128471667326981%23wechat_redirect"
  215. oschina: { pattern: /https?:\/\/www\.oschina\.net\/action\/GoToLink\?url=(.+)$/ },
  216.  
  217. // https://weibo.cn/sinaurl?u=https%3A%2F%2Fwww.freebsd.org%2F
  218. // https://weibo.cn/sinaurl?toasturl=https%3A%2F%2Ftime.geekbang.org%2F
  219. // https://weibo.cn/sinaurl?luicode=10000011&lfid=230259&u=http%3A%2F%2Ft.cn%2FA6qHeVlf
  220. // https://weibo.cn/sinaurl?f=w&u=http%3A%2F%2Ft.cn%2FA66XY2gI&ep=LlAsNz3HD%2C1683963007%2CLlpkandl6%2C7276218544
  221. // href="https://weibo.cn/sinaurl?f=w&u=http%3A%2F%2Ft.cn%2FA66XY2gI&ep=LlAsNz3HD%2C1683963007%2CLlpkandl6%2C7276218544"
  222. weibo: { pattern: /https?:\/\/weibo\.cn\/sinaurl\?f=w&u=(.+)$/, resolver: weiboResolver },
  223.  
  224. // http://t.cn/A66926Pm 未备案的, 跳转到中转网址, response.finalUrl仍然还是http://t.cn/A66926Pm 目标网址出现在response.responseText里
  225. // http://t.cn/A669K964 已备案的, 直接跳转到目标网址, 出现在response.finalUrl里
  226. weibo2: { pattern: /(https?:\/\/t\.cn\/.+)$/, resolver: weiboResolver },
  227.  
  228.  
  229. // segmentfault对链接进行加密处理, 不知道如何decode, 所以只能写一个函数去单独处理
  230. // https://link.segmentfault.com/?enc=LZyRulLABKpXOHl2vbA%2F4w%3D%3D.MWhFMvjhyBk1ReIRoGxyxa0VxGtg%2Foyk0DMtfzZTJoKbsgoJFtGCPHe8%2BZ1HbRdcvNsGaVfll9oGQXLsZCHK7w%3D%3D
  231. segfault: { pattern: /https?:\/\/link\.segmentfault\.com\/?\?enc=(.+)$/, resolver: segfaultResolver },
  232.  
  233. // https://www.bookmarkearth.com/detail/097c687c98974691b2174bc1e85103d4
  234. // https://show.bookmarkearth.com/view/801
  235. bookmarkearth: { pattern: /(https?:\/\/show\.bookmarkearth\.com\/view\/.+)$/, resolver: bmeResolver },
  236.  
  237.  
  238. // 以下网站a标签的herf未修改, 推测是js做的弹窗, 所以不需要匹配, 也匹配不出来
  239. // csdn https://link.csdn.net/?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fzh-CN%2Fdocs%2FWeb%2FJavaScript%2FReference%2FGlobal_Objects%2FRegExp
  240. // 语雀 https://www.yuque.com/r/goto?url=https%3A%2F%2Fwww.canva.cn%2F
  241. }
  242.  
  243. const resolveRealURI = async (href) => {
  244. const fallbackURI = href;
  245.  
  246. for (let i in patter_match) {
  247. const matcher = href.match(patter_match[i].pattern);
  248. if (!matcher) {
  249. continue;
  250. }
  251. console.log({ matcher });
  252.  
  253. if (patter_match[i].hasOwnProperty('resolver')) {
  254. // complex customize resolver
  255. console.log(patter_match[i].resolver)
  256. let realURI = await patter_match[i].resolver(href);
  257. return realURI;
  258. }
  259.  
  260. const encodeURI = matcher[1];
  261. // simple reg resolver
  262. return decodeURIComponent(encodeURI);
  263. }
  264. return fallbackURI;
  265. }
  266.  
  267. const getHref = (e) => {
  268. let target = e.target;
  269. while (target) {
  270. if (target.tagName.toLowerCase() === 'a' && target.hasAttribute('href')) {
  271. console.log("find element", { target })
  272. return target.getAttribute('href');
  273. }
  274. target = target.parentElement;
  275. }
  276. return null;
  277. }
  278.  
  279. document.addEventListener('click', (e) => {
  280. console.log({ e })
  281. let href = getHref(e);
  282. if (href) {
  283.  
  284. // 不是url, 则不做处理
  285. // 兼容如 href设置为 'javascript:void(0);' 等的情况
  286. if (!isValidURL(href)) {
  287. return;
  288. }
  289.  
  290. e.stopPropagation();
  291. e.preventDefault();
  292.  
  293. resolveRealURI(href).then((realURI) => {
  294. console.log({ realURI })
  295. window.open(realURI);
  296. }).catch(() => { window.open(href); })
  297. }
  298. }, { capture: true })
  299.  
  300. })();