open the link directly

点击链接直接跳转

  1. // ==UserScript==
  2. // @name open the link directly
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1.11
  5. // @description 点击链接直接跳转
  6. // @author nediiii
  7. // @match *://*.csdn.net/*
  8. // @match *://*.gitee.com/*
  9. // @match *://*.uisdc.com/*
  10. // @match *://*.uiiiuiii.com/*
  11. // @match *://*.logonews.cn/*
  12. // @match *://*.afdian.net/*
  13. // @match *://*.tianyancha.com/*
  14. // @match *://*.oschina.net/*
  15. // @match *://*.pixiv.net/*
  16. // @match *://*.jianshu.com/*
  17. // @match *://*.juejin.cn/*
  18. // @match *://*.weibo.cn/*
  19. // @match *://*.weibo.com/*
  20. // @match *://*.yuque.com/*
  21. // @match *://*.segmentfault.com/*
  22. // @match *://*.zhihu.com/*
  23. // @match *://*.bookmarkearth.com/*
  24. // @match *://*.leetcode-cn.com/*
  25. // @match *://*.huaban.com/*
  26. // @match *://*.t.cn/*
  27. // @match *://*.weibo.cn/*
  28. // @match *://*.leetcode.cn/*
  29. // @run-at document-start
  30. // @license GPLv3 License
  31. // @icon https://www.google.com/s2/favicons?sz=64&domain=gf.qytechs.cn
  32. // @grant GM_xmlhttpRequest
  33. // @grant unsafeWindow
  34. // ==/UserScript==
  35.  
  36. (function () {
  37. 'use strict';
  38.  
  39. // 保存原始方法
  40. unsafeWindow.__otld_open = unsafeWindow.open;
  41. // 重写 open 方法
  42. var myopen = function (url, name, features) {
  43. console.log({ url }, { name }, { features });
  44. // debugger;
  45. return unsafeWindow.__otld_open(url, name, features);
  46. }
  47. // 屏蔽 JS 中对原生函数 native 属性的检测
  48. var _myopen = myopen.bind(null);
  49. _myopen.toString = unsafeWindow.__otld_open.toString;
  50. Object.defineProperty(unsafeWindow, 'open', {
  51. value: _myopen
  52. });
  53.  
  54. const getValidURL = (url) => {
  55. try {
  56. let u = new URL(url);
  57. return u;
  58. } catch (error) {
  59. return getValidURLWithBase(url);
  60. }
  61. }
  62.  
  63. const getValidURLWithBase = (url) => {
  64. try {
  65. let u = new URL(url, getCurrentURLBase());
  66. return u;
  67. } catch (error) {
  68. return null;
  69. }
  70. }
  71.  
  72. const isHttpProtocol = (url) => {
  73. return url.protocol == 'http:' || url.protocol == 'https:';
  74. }
  75.  
  76. const getCurrentURLBase = () => {
  77. return window.location.origin;
  78. }
  79.  
  80. const urlReg = /\bhttps?:\/\/\S+/gi;
  81.  
  82. const weiboResolver = async (href) => {
  83. return new Promise((resolve, reject) => {
  84. GM_xmlhttpRequest({
  85. method: "GET",
  86. url: href,
  87. onload: function (response) {
  88. console.log({ response });
  89. if (response.status != 200) {
  90. reject(href);
  91. return;
  92. }
  93.  
  94. // weibo跳转会区分目标网址是否备案
  95. // 未备案的, 中转
  96. // 已备案的, 直跳
  97. let realURI = href;
  98. if (response.finalUrl === href) {
  99. // 未备案, 中转网址
  100. // 网址在html里
  101. let doc = new DOMParser().parseFromString(response.responseText, "text/html");
  102. let node = doc.querySelector('body > div > div:nth-child(2)');
  103. let str = node.innerText;
  104. console.log({ doc });
  105. console.log({ node });
  106. console.log({ str });
  107.  
  108. let extractURL = str.match(urlReg);
  109. console.log({ extractURL });
  110. if (extractURL) {
  111. realURI = extractURL[0];
  112. }
  113. }
  114. else {
  115. // 已备案
  116. // 网址在finalUrl里
  117. let extractURL = response.finalUrl.match(urlReg);
  118. if (extractURL) {
  119. realURI = extractURL[0];
  120. }
  121. }
  122.  
  123. resolve(realURI)
  124. },
  125. onerror: function (error) {
  126. console.log({ error });
  127. reject(href)
  128. }
  129. });
  130. });
  131. }
  132.  
  133. const segfaultResolver = async (href) => {
  134. return new Promise((resolve, reject) => {
  135. GM_xmlhttpRequest({
  136. method: "GET",
  137. url: href,
  138. onload: function (response) {
  139. console.log({ response });
  140. if (response.status == 200) {
  141. let doc = new DOMParser().parseFromString(response.responseText, "text/html");
  142. let node = doc.querySelector('body > p')
  143. let str = node.innerText;
  144. console.log({ doc });
  145. console.log({ node });
  146. console.log({ str });
  147.  
  148. let realURI = href;
  149. let extractURL = str.match(urlReg);
  150. console.log({ extractURL });
  151. if (extractURL) {
  152. realURI = extractURL[0];
  153. }
  154. resolve(realURI)
  155. } else {
  156. reject(href)
  157. }
  158. },
  159. onerror: function (error) {
  160. console.log({ error });
  161. reject(href)
  162. }
  163. });
  164. });
  165. }
  166.  
  167. const bmeResolver = async (href) => {
  168. return new Promise((resolve, reject) => {
  169. GM_xmlhttpRequest({
  170. method: "GET",
  171. url: href,
  172. onload: function (response) {
  173. console.log({ response });
  174. if (response.status == 200) {
  175. let doc = new DOMParser().parseFromString(response.responseText, "text/html");
  176. let node = doc.querySelector('body > div.row.box > div.col-lg-6.jump-box > div > div.content > p.link');
  177. //querySelector('body > div > div > div').attributes['data-url'].value
  178. let str = doc.querySelector('body > div > div > div').attributes['data-url'].value;
  179. console.log({ doc });
  180. console.log({ node });
  181. console.log({ str });
  182.  
  183. let realURI = href;
  184. let extractURL = str.match(urlReg);
  185. console.log({ extractURL });
  186. if (extractURL) {
  187. realURI = extractURL[0];
  188. }
  189. resolve(realURI)
  190. } else {
  191. reject(href)
  192. }
  193. },
  194. onerror: function (error) {
  195. console.log({ error });
  196. reject(href)
  197. }
  198. });
  199. });
  200. }
  201.  
  202. const huabanResolver = async (href) => {
  203. return new Promise((resolve, reject) => {
  204. GM_xmlhttpRequest({
  205. method: "GET",
  206. url: href,
  207. onload: function (response) {
  208. console.log({ response });
  209. if (response.status == 200) {
  210. let doc = new DOMParser().parseFromString(response.responseText, "text/html");
  211. let node = doc.querySelector('body > script');
  212. let str = node.innerText;
  213. const obj = JSON.parse(str);
  214. console.log({ doc });
  215. console.log({ node });
  216. console.log({ str });
  217. console.log({ obj });
  218.  
  219. str = obj.props.pageProps.data.link;
  220.  
  221. let realURI = href;
  222. let extractURL = str.match(urlReg);
  223. console.log({ extractURL });
  224. if (extractURL) {
  225. realURI = extractURL[0];
  226. }
  227. resolve(realURI)
  228. } else {
  229. reject(href)
  230. }
  231. },
  232. onerror: function (error) {
  233. console.log({ error });
  234. reject(href)
  235. }
  236. });
  237. });
  238. }
  239.  
  240. const patter_match = {
  241.  
  242. // 注意这里的pattern需要去看对应网站dom里的a标签的实际herf值, console也会打印日志, 可以自己添加正则来增加网站支持
  243.  
  244. // https://zhuanlan.zhihu.com/p/23333042
  245. // https://link.zhihu.com/?target=https%3A//getkap.co/
  246. // https://link.zhihu.com/?target=https%3A//gf.qytechs.cn/en/scripts/5029-yet-another-%25E8%2587%25AA%25E5%258F%25A4cb%25E5%2587%25BA%25E8%25AF%2584%25E8%25AE%25BA-sharing-plugin
  247. zhihu: { pattern: /https?:\/\/link\.zhihu\.com\/?\?target=(.+)$/ },
  248.  
  249. // https://www.jianshu.com/p/a6a63a0c6e53
  250. // https://links.jianshu.com/go?to=https%3A%2F%2Fnpm.taobao.org%2Fmirrors%2Felectron
  251. jianshu2: { pattern: /https?:\/\/links\.jianshu\.com\/go\?to=(.+)$/ },
  252.  
  253. // https://www.jianshu.com/p/a3af2fb4d99e
  254. // https://link.jianshu.com/?t=http://www.ihie.org/john-kansky
  255. // https://www.jianshu.com/go-wild?ac=2&url=http%3A%2F%2Fwww.ihie.org%2Fjohn-kansky
  256. jianshu3: { pattern: /https?:\/\/link\.jianshu\.com\/\?t=(.+)$/ },
  257. jianshu4: { pattern: /https?:\/\/www\.jianshu\.com\/go-wild\?ac=2&url=(.+)$/ },
  258.  
  259. // href="https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.aliyun.com%2Fgroup%2Falisoftwaretech%2F"
  260. juejin: { pattern: /https?:\/\/link\.juejin\.cn\/?\?target=(.+)$/ },
  261.  
  262. // https://gitee.com/meetqy/acss-dnd
  263. // href="https://gitee.com/link?target=https%3A%2F%2Fcuyang.me%2Facss-dnd%2F"
  264. // https://blog.gitee.com/2022/01/20/lowcodetools/
  265. // https://link.juejin.cn/?target=https%3A%2F%2Flink.zhihu.com%2F%3Ftarget%3Dhttps%253A%2F%2Fgitee.com%2Fnocobase%2Fnocobase
  266. gitee: { pattern: /https?:\/\/gitee\.com\/link\?target=(.+)$/ },
  267.  
  268. // https://www.uisdc.com/build-b-end-grid-system
  269. // https://link.uisdc.com/?redirect=https%3A%2F%2Fuxdesign.cc%2Fresponsive-grids-and-how-to-actually-use-them-970de4c16e01
  270. uisdc: { pattern: /https?:\/\/link\.uisdc\.com\/?\?redirect=(.+)$/ },
  271.  
  272. // https://www.logonews.cn/apple-sues-sex-topic-blog-for-logo-infringement.html
  273. // https://link.logonews.cn/?url=http://aasd.k12.wi.us/district
  274. logonews: { pattern: /https?:\/\/link\.logonews\.cn\/?\?url=(.+)$/ },
  275.  
  276. // https://afdian.net/@AdventCirno
  277. // https://afdian.net/link?target=https%3A%2F%2Fwww.patreon.com%2Fuser%3Fu%3D6139561
  278. afdian: { pattern: /https?:\/\/afdian\.net\/link\?target=(.+)$/ },
  279.  
  280. // https://www.pixiv.net/users/25237
  281. // href="/jump.php?url=https%3A%2F%2Ftwitter.com%2Fomiya_io"
  282. // href="/jump.php?https%3A%2F%2Finstagram.com%2Fsnatti89%2F"
  283. pixiv: { pattern: /https?:\/\/www\.pixiv\.net\/jump\.php\?(?:url=)?(.+)$/ },
  284.  
  285. // https://my.oschina.net/androiddevs/blog/5496556
  286. // 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
  287. // 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"
  288. oschina: { pattern: /https?:\/\/www\.oschina\.net\/action\/GoToLink\?url=(.+)$/ },
  289.  
  290.  
  291. // https://uiiiuiii.com/software/491152.html
  292. // https://link.uiiiuiii.com/?redirect=https%3A%2F%2Fwww.behance.net%2Fgallery%2F117044741%2FHARSHITA-FREE-SIGNATURE-FONT%3Ftracking_source%3Dfor_you_feed_recommended
  293. uiiiuiii: { pattern: /https?:\/\/link\.uiiiuiii\.com\/?\?redirect=(.+)$/ },
  294.  
  295. // https://leetcode-cn.com/circle/discuss/mL0gxC/
  296. // https://leetcode-cn.com/link/?target=http%3A%2F%2Fwww.bytedance.com
  297. leetcodecn: { pattern: /https?:\/\/leetcode-cn\.com\/link\/\?target=(.+)$/ },
  298.  
  299. // https://leetcode.cn/link/?target=http%3A%2F%2Fwww.bytedance.com
  300. leetcodecn2: { pattern: /https?:\/\/leetcode\.cn\/link\/\?target=(.+)$/ },
  301.  
  302. // https://www.tianyancha.com/company/28723141
  303. // href="https://ss.knet.cn/verifyseal.dll?sn=e18042711010873571xsuv000000&pa=111332"
  304. // https://www.tianyancha.com/security?target=https%3A%2F%2Fss.knet.cn%2Fverifyseal.dll%3Fsn%3De18042711010873571xsuv000000%26pa%3D111332
  305. tianyancha: { pattern: /https?:\/\/www\.tianyancha\.com\/security\?target=(.+)$/ },
  306.  
  307. // https://huaban.com/pins/5095905933
  308. // https://huaban.com/go?pin_id=4614749616
  309. // https://huaban.com/go?pin_id=4614750040&url=item.taobao.com
  310. huaban: { pattern: /https?:\/\/huaban\.com\/go\?pin_id=(.+)$/, resolver: huabanResolver },
  311.  
  312. // https://weibo.cn/sinaurl?u=https%3A%2F%2Fwww.freebsd.org%2F
  313. // https://weibo.cn/sinaurl?toasturl=https%3A%2F%2Ftime.geekbang.org%2F
  314. // https://weibo.cn/sinaurl?luicode=10000011&lfid=230259&u=http%3A%2F%2Ft.cn%2FA6qHeVlf
  315. // https://weibo.cn/sinaurl?f=w&u=http%3A%2F%2Ft.cn%2FA66XY2gI&ep=LlAsNz3HD%2C1683963007%2CLlpkandl6%2C7276218544
  316. // href="https://weibo.cn/sinaurl?f=w&u=http%3A%2F%2Ft.cn%2FA66XY2gI&ep=LlAsNz3HD%2C1683963007%2CLlpkandl6%2C7276218544"
  317. weibo: { pattern: /https?:\/\/weibo\.cn\/sinaurl\?(?:toasturl|u|f=w&u|ep)=(.+)$/, resolver: weiboResolver },
  318.  
  319. // http://t.cn/A66926Pm 未备案的, 跳转到中转网址, response.finalUrl仍然还是http://t.cn/A66926Pm 目标网址出现在response.responseText里
  320. // http://t.cn/A669K964 已备案的, 直接跳转到目标网址, 出现在response.finalUrl里
  321. weibo2: { pattern: /(https?:\/\/t\.cn\/.+)$/, resolver: weiboResolver },
  322.  
  323.  
  324. // segmentfault对链接进行加密处理, 不知道如何decode, 所以只能写一个函数去单独处理
  325. // https://link.segmentfault.com/?enc=LZyRulLABKpXOHl2vbA%2F4w%3D%3D.MWhFMvjhyBk1ReIRoGxyxa0VxGtg%2Foyk0DMtfzZTJoKbsgoJFtGCPHe8%2BZ1HbRdcvNsGaVfll9oGQXLsZCHK7w%3D%3D
  326. // https://segmentfault.com/a/1190000017434150
  327. segfault: { pattern: /https?:\/\/link\.segmentfault\.com\/?\?enc=(.+)$/, resolver: segfaultResolver },
  328.  
  329. // https://www.bookmarkearth.com/detail/097c687c98974691b2174bc1e85103d4
  330. // https://show.bookmarkearth.com/view/801
  331. // https://www.bookmarkearth.com/view/801
  332. bookmarkearth: { pattern: /(https?:\/\/show\.bookmarkearth\.com\/view\/.+)$/, resolver: bmeResolver },
  333. bookmarkearth2: { pattern: /(https?:\/\/www\.bookmarkearth\.com\/view\/.+)$/, resolver: bmeResolver },
  334.  
  335. // 以下网站a标签的herf未修改, 推测是js做的弹窗, 所以不需要匹配, 也匹配不出来
  336. // csdn https://link.csdn.net/?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fzh-CN%2Fdocs%2FWeb%2FJavaScript%2FReference%2FGlobal_Objects%2FRegExp
  337. // 语雀 https://www.yuque.com/r/goto?url=https%3A%2F%2Fwww.canva.cn%2F
  338.  
  339. // https://blog.csdn.net/weixin_41010294/article/details/85289852
  340. csdn: { host: 'csdn.net' },
  341. // https://link.csdn.net/?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fzh-CN%2Fdocs%2FWeb%2FJavaScript%2FReference%2FGlobal_Objects%2FRegExp
  342. csdn2: { pattern: /https?:\/\/link\.csdn\.net\/\?target=(.+)$/ },
  343.  
  344. // https://www.yuque.com/yuque/gpvawt/fuu6h3
  345. yuque: { host: 'yuque.com' },
  346. }
  347.  
  348. const matchHostResolver = (url, host) => {
  349. // 1. 需要当前网站与host匹配
  350. if (!window.location.host.includes(host)) {
  351. return false;
  352. }
  353. // 2. url为外链
  354. if (url.host.includes(host)) {
  355. return false;
  356. }
  357.  
  358. return true;
  359. }
  360.  
  361. const getMatchPattern = (url) => {
  362. for (let i in patter_match) {
  363. // console.log("url:", url, "patter_match[i]:", patter_match[i])
  364. if (patter_match[i].hasOwnProperty('host') && matchHostResolver(url, patter_match[i].host)) {
  365. return patter_match[i];
  366. }
  367. if (patter_match[i].hasOwnProperty('pattern') && url.href.match(patter_match[i].pattern)) {
  368. return patter_match[i];
  369. }
  370. }
  371. return null;
  372. }
  373.  
  374. const resolveRealURI = async (href) => {
  375. // TODO 兼容套娃链接, 如 https://link.juejin.cn/?target=https%3A%2F%2Flink.zhihu.com%2F%3Ftarget%3Dhttps%253A%2F%2Fgitee.com%2Fnocobase%2Fnocobase
  376. const fallbackURI = href;
  377.  
  378. for (let i in patter_match) {
  379. const matcher = href.match(patter_match[i].pattern);
  380. if (!matcher) {
  381. continue;
  382. }
  383. console.log({ matcher });
  384.  
  385. if (patter_match[i].hasOwnProperty('resolver')) {
  386. // complex customize resolver
  387. console.log(patter_match[i].resolver)
  388. let realURI = await patter_match[i].resolver(href);
  389. return realURI;
  390. }
  391.  
  392. const encodeURI = matcher[1];
  393. // simple reg resolver
  394. return decodeURIComponent(encodeURI);
  395. }
  396. return fallbackURI;
  397. }
  398.  
  399. const patternResolve = async (patter_match, href) => {
  400.  
  401. if (patter_match.hasOwnProperty('resolver')) {
  402. // complex customize resolver
  403. console.log(patter_match.resolver)
  404. let realURI = await patter_match.resolver(href);
  405. return realURI;
  406. }
  407.  
  408. if (patter_match.hasOwnProperty('pattern')) {
  409. const matcher = href.match(patter_match.pattern);
  410. if (!matcher) { return href; }
  411. const encodeURI = matcher[1];
  412. return decodeURIComponent(encodeURI);
  413. }
  414.  
  415. return href;
  416. }
  417.  
  418. const getAnchorElement = (e) => {
  419. let target = e.target;
  420. while (target) {
  421. if (target.tagName.toLowerCase() === 'a' && target.hasAttribute('href')) {
  422. return target;
  423. }
  424. target = target.parentElement;
  425. }
  426. return null;
  427. }
  428.  
  429. /// 主体函数
  430. /// link: 原始链接
  431. /// e: 事件对象
  432. /// handleEventAfterMatch: 匹配到规则后的事件处理
  433. /// handleRealURI: 处理真实链接
  434. /// errCallback: 错误回调
  435. const otld = (link, e, handleEventAfterMatch, handleRealURI, errCallback) => {
  436. let url = getValidURL(link);
  437. if (url === null || !isHttpProtocol(url)) {
  438. return;
  439. }
  440.  
  441. console.log({ url });
  442.  
  443. const matchPattern = getMatchPattern(url);
  444. console.log({ matchPattern });
  445. if (!matchPattern) {
  446. console.log("不匹配处理规则, 如有误, 请反馈给我, 非常感谢");
  447. return;
  448. }
  449.  
  450. handleEventAfterMatch && handleEventAfterMatch(e);
  451.  
  452. const target = (() => {
  453. // csdn 打开新标签页兼容处理
  454. if (matchPattern == patter_match.csdn) {
  455. return '_blank';
  456. }
  457.  
  458. if (!e || !getAnchorElement(e)) {
  459. return '_self';
  460. }
  461.  
  462. let anchor = getAnchorElement(e);
  463. if (anchor.hasAttribute('target')) {
  464. return anchor.getAttribute('target');
  465. }
  466. return '_self';
  467. })();
  468.  
  469. patternResolve(matchPattern, url.href).then((realURI) => {
  470. console.log({ realURI });
  471. handleRealURI(realURI, target);
  472. }).catch(() => { errCallback && errCallback(target); });
  473. }
  474.  
  475.  
  476. document.addEventListener('click', (e) => {
  477. console.log({ e })
  478.  
  479. // 找到a标签
  480. let anchor = getAnchorElement(e);
  481. if (!anchor) {
  482. return;
  483. }
  484.  
  485. console.log({ anchor })
  486. let href = anchor.getAttribute('href');
  487. if (!href) {
  488. return;
  489. }
  490.  
  491. const stopEvent = (e) => {
  492. if (!e) {
  493. return;
  494. }
  495.  
  496. e.preventDefault();
  497. e.stopPropagation();
  498. }
  499.  
  500. otld(href, e, stopEvent, (realURI, target) => { window.open(realURI, target) }, (target) => { window.open(href, target); });
  501.  
  502. }, { capture: true })
  503.  
  504. /// 处理手动输入或者鼠标拖动的链接
  505. otld(window.location.href, null, null, (realURI, target) => { window.location.replace(realURI); }, null);
  506.  
  507. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址