open the link directly

点击链接直接跳转

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

  1. // ==UserScript==
  2. // @name open the link directly
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1.3
  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. // @run-at document-start
  24. // @license GPLv3 License
  25. // @icon https://www.google.com/s2/favicons?sz=64&domain=greasyfork.org
  26. // @grant GM_xmlhttpRequest
  27. // @grant unsafeWindow
  28. // ==/UserScript==
  29.  
  30. (function () {
  31. 'use strict';
  32.  
  33. // 保存原始方法
  34. unsafeWindow.__otld_open = unsafeWindow.open;
  35. // 重写 open 方法
  36. var myopen = function (url, name, features) {
  37. console.log({ url }, { name }, { features });
  38. // debugger;
  39. return unsafeWindow.__otld_open(url, name, features);
  40. }
  41. // 屏蔽 JS 中对原生函数 native 属性的检测
  42. var _myopen = myopen.bind(null);
  43. _myopen.toString = unsafeWindow.__otld_open.toString;
  44. Object.defineProperty(unsafeWindow, 'open', {
  45. value: _myopen
  46. });
  47.  
  48. const getValidURL = (url) => {
  49. try {
  50. let u = new URL(url);
  51. return u;
  52. } catch (error) {
  53. return getValidURLWithBase(url);
  54. }
  55. }
  56.  
  57. const getValidURLWithBase = (url) => {
  58. try {
  59. let u = new URL(url, getCurrentURLBase());
  60. return u;
  61. } catch (error) {
  62. return null;
  63. }
  64. }
  65.  
  66. const getCurrentURLBase = () => {
  67. return window.location.origin;
  68. }
  69.  
  70. const urlReg = /\bhttps?:\/\/\S+/gi;
  71.  
  72. const weiboResolver = async (href) => {
  73. return new Promise((resolve, reject) => {
  74. GM_xmlhttpRequest({
  75. method: "GET",
  76. url: href,
  77. onload: function (response) {
  78. console.log({ response });
  79. if (response.status != 200) {
  80. reject(href);
  81. return;
  82. }
  83.  
  84. // weibo跳转会区分目标网址是否备案
  85. // 未备案的, 中转
  86. // 已备案的, 直跳
  87. let realURI = href;
  88. if (response.finalUrl === href) {
  89. // 未备案, 中转网址
  90. // 网址在html里
  91. let doc = new DOMParser().parseFromString(response.responseText, "text/html");
  92. let node = doc.querySelector('body > div > div:nth-child(2)');
  93. let str = node.innerText;
  94. console.log({ doc });
  95. console.log({ node });
  96. console.log({ str });
  97.  
  98. let extractURL = str.match(urlReg);
  99. console.log({ extractURL });
  100. if (extractURL) {
  101. realURI = extractURL[0];
  102. }
  103. }
  104. else {
  105. // 已备案
  106. // 网址在finalUrl里
  107. let extractURL = response.finalUrl.match(urlReg);
  108. if (extractURL) {
  109. realURI = extractURL[0];
  110. }
  111. }
  112.  
  113. resolve(realURI)
  114. },
  115. onerror: function (error) {
  116. console.log({ error });
  117. reject(href)
  118. }
  119. });
  120. });
  121. }
  122.  
  123. const segfaultResolver = async (href) => {
  124. return new Promise((resolve, reject) => {
  125. GM_xmlhttpRequest({
  126. method: "GET",
  127. url: href,
  128. onload: function (response) {
  129. console.log({ response });
  130. if (response.status == 200) {
  131. let doc = new DOMParser().parseFromString(response.responseText, "text/html");
  132. let node = doc.querySelector('body > p')
  133. let str = node.innerText;
  134. console.log({ doc });
  135. console.log({ node });
  136. console.log({ str });
  137.  
  138. let realURI = href;
  139. let extractURL = str.match(urlReg);
  140. console.log({ extractURL });
  141. if (extractURL) {
  142. realURI = extractURL[0];
  143. }
  144. resolve(realURI)
  145. } else {
  146. reject(href)
  147. }
  148. },
  149. onerror: function (error) {
  150. console.log({ error });
  151. reject(href)
  152. }
  153. });
  154. });
  155. }
  156.  
  157. const bmeResolver = async (href) => {
  158. return new Promise((resolve, reject) => {
  159. GM_xmlhttpRequest({
  160. method: "GET",
  161. url: href,
  162. onload: function (response) {
  163. console.log({ response });
  164. if (response.status == 200) {
  165. let doc = new DOMParser().parseFromString(response.responseText, "text/html");
  166. let node = doc.querySelector('body > div.row.box > div.col-lg-6.jump-box > div > div.content > p.link');
  167. let str = node.innerText;
  168. console.log({ doc });
  169. console.log({ node });
  170. console.log({ str });
  171.  
  172. let realURI = href;
  173. let extractURL = str.match(urlReg);
  174. console.log({ extractURL });
  175. if (extractURL) {
  176. realURI = extractURL[0];
  177. }
  178. resolve(realURI)
  179. } else {
  180. reject(href)
  181. }
  182. },
  183. onerror: function (error) {
  184. console.log({ error });
  185. reject(href)
  186. }
  187. });
  188. });
  189. }
  190.  
  191. const patter_match = {
  192.  
  193. // 注意这里的pattern需要去看对应网站dom里的a标签的实际herf值, console也会打印日志, 可以自己添加正则来增加网站支持
  194.  
  195. // https://zhuanlan.zhihu.com/p/23333042
  196. // https://link.zhihu.com/?target=https%3A//getkap.co/
  197. // 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
  198. zhihu: { pattern: /https?:\/\/link\.zhihu\.com\/?\?target=(.+)$/ },
  199.  
  200. // https://www.jianshu.com/p/a6a63a0c6e53
  201. // https://links.jianshu.com/go?to=https%3A%2F%2Fnpm.taobao.org%2Fmirrors%2Felectron
  202. jianshu2: { pattern: /https?:\/\/links\.jianshu\.com\/go\?to=(.+)$/ },
  203.  
  204. // href="https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.aliyun.com%2Fgroup%2Falisoftwaretech%2F"
  205. juejin: { pattern: /https?:\/\/link\.juejin\.cn\/?\?target=(.+)$/ },
  206.  
  207. // https://gitee.com/meetqy/acss-dnd
  208. // href="https://gitee.com/link?target=https%3A%2F%2Fcuyang.me%2Facss-dnd%2F"
  209. // https://blog.gitee.com/2022/01/20/lowcodetools/
  210. // https://link.juejin.cn/?target=https%3A%2F%2Flink.zhihu.com%2F%3Ftarget%3Dhttps%253A%2F%2Fgitee.com%2Fnocobase%2Fnocobase
  211. gitee: { pattern: /https?:\/\/gitee\.com\/link\?target=(.+)$/ },
  212.  
  213. // https://www.uisdc.com/build-b-end-grid-system
  214. // https://link.uisdc.com/?redirect=https%3A%2F%2Fuxdesign.cc%2Fresponsive-grids-and-how-to-actually-use-them-970de4c16e01
  215. uisdc: { pattern: /https?:\/\/link\.uisdc\.com\/?\?redirect=(.+)$/ },
  216.  
  217. // https://www.logonews.cn/apple-sues-sex-topic-blog-for-logo-infringement.html
  218. // https://link.logonews.cn/?url=http://aasd.k12.wi.us/district
  219. logonews: { pattern: /https?:\/\/link\.logonews\.cn\/?\?url=(.+)$/ },
  220.  
  221. // https://afdian.net/@AdventCirno
  222. // https://afdian.net/link?target=https%3A%2F%2Fwww.patreon.com%2Fuser%3Fu%3D6139561
  223. afdian: { pattern: /https?:\/\/afdian\.net\/link\?target=(.+)$/ },
  224.  
  225. // https://www.pixiv.net/users/25237
  226. // href="/jump.php?url=https%3A%2F%2Ftwitter.com%2Fomiya_io"
  227. // href="/jump.php?https%3A%2F%2Finstagram.com%2Fsnatti89%2F"
  228. pixiv: { pattern: /https?:\/\/www\.pixiv\.net\/jump\.php\?(?:url=)?(.+)$/ },
  229.  
  230. // https://my.oschina.net/androiddevs/blog/5496556
  231. // 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
  232. // 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"
  233. oschina: { pattern: /https?:\/\/www\.oschina\.net\/action\/GoToLink\?url=(.+)$/ },
  234.  
  235. // https://weibo.cn/sinaurl?u=https%3A%2F%2Fwww.freebsd.org%2F
  236. // https://weibo.cn/sinaurl?toasturl=https%3A%2F%2Ftime.geekbang.org%2F
  237. // https://weibo.cn/sinaurl?luicode=10000011&lfid=230259&u=http%3A%2F%2Ft.cn%2FA6qHeVlf
  238. // https://weibo.cn/sinaurl?f=w&u=http%3A%2F%2Ft.cn%2FA66XY2gI&ep=LlAsNz3HD%2C1683963007%2CLlpkandl6%2C7276218544
  239. // href="https://weibo.cn/sinaurl?f=w&u=http%3A%2F%2Ft.cn%2FA66XY2gI&ep=LlAsNz3HD%2C1683963007%2CLlpkandl6%2C7276218544"
  240. weibo: { pattern: /https?:\/\/weibo\.cn\/sinaurl\?f=w&u=(.+)$/, resolver: weiboResolver },
  241.  
  242. // http://t.cn/A66926Pm 未备案的, 跳转到中转网址, response.finalUrl仍然还是http://t.cn/A66926Pm 目标网址出现在response.responseText里
  243. // http://t.cn/A669K964 已备案的, 直接跳转到目标网址, 出现在response.finalUrl里
  244. weibo2: { pattern: /(https?:\/\/t\.cn\/.+)$/, resolver: weiboResolver },
  245.  
  246.  
  247. // segmentfault对链接进行加密处理, 不知道如何decode, 所以只能写一个函数去单独处理
  248. // https://link.segmentfault.com/?enc=LZyRulLABKpXOHl2vbA%2F4w%3D%3D.MWhFMvjhyBk1ReIRoGxyxa0VxGtg%2Foyk0DMtfzZTJoKbsgoJFtGCPHe8%2BZ1HbRdcvNsGaVfll9oGQXLsZCHK7w%3D%3D
  249. // https://segmentfault.com/a/1190000017434150
  250. segfault: { pattern: /https?:\/\/link\.segmentfault\.com\/?\?enc=(.+)$/, resolver: segfaultResolver },
  251.  
  252. // https://www.bookmarkearth.com/detail/097c687c98974691b2174bc1e85103d4
  253. // https://show.bookmarkearth.com/view/801
  254. bookmarkearth: { pattern: /(https?:\/\/show\.bookmarkearth\.com\/view\/.+)$/, resolver: bmeResolver },
  255.  
  256.  
  257. // 以下网站a标签的herf未修改, 推测是js做的弹窗, 所以不需要匹配, 也匹配不出来
  258. // csdn https://link.csdn.net/?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fzh-CN%2Fdocs%2FWeb%2FJavaScript%2FReference%2FGlobal_Objects%2FRegExp
  259. // 语雀 https://www.yuque.com/r/goto?url=https%3A%2F%2Fwww.canva.cn%2F
  260.  
  261. // https://blog.csdn.net/weixin_41010294/article/details/85289852
  262. csdn: { host: 'csdn.net' },
  263.  
  264. // https://www.yuque.com/yuque/gpvawt/fuu6h3
  265. yuque: { host: 'yuque.com' },
  266.  
  267. // https://www.tianyancha.com/company/28723141
  268. // href="https://ss.knet.cn/verifyseal.dll?sn=e18042711010873571xsuv000000&pa=111332"
  269. // https://www.tianyancha.com/security?target=https%3A%2F%2Fss.knet.cn%2Fverifyseal.dll%3Fsn%3De18042711010873571xsuv000000%26pa%3D111332
  270. tianyancha: { host: 'tianyancha.com' },
  271.  
  272. }
  273.  
  274. const matchHostResolver = (url, host) => {
  275. // 1. 需要当前网站与host匹配
  276. if (!window.location.host.includes(host)) {
  277. return false;
  278. }
  279. // 2. url为外链
  280. if (url.host.includes(host)) {
  281. return false;
  282. }
  283.  
  284. return true;
  285. }
  286.  
  287. const getMatchPattern = (url) => {
  288. for (let i in patter_match) {
  289.  
  290. if (patter_match[i].hasOwnProperty('host') && matchHostResolver(url, patter_match[i].host)) {
  291. return patter_match[i];
  292. }
  293. if (patter_match[i].hasOwnProperty('pattern') && url.href.match(patter_match[i].pattern)) {
  294. return patter_match[i];
  295. }
  296. }
  297. return null;
  298. }
  299.  
  300. const resolveRealURI = async (href) => {
  301. // TODO 兼容套娃链接, 如 https://link.juejin.cn/?target=https%3A%2F%2Flink.zhihu.com%2F%3Ftarget%3Dhttps%253A%2F%2Fgitee.com%2Fnocobase%2Fnocobase
  302. const fallbackURI = href;
  303.  
  304. for (let i in patter_match) {
  305. const matcher = href.match(patter_match[i].pattern);
  306. if (!matcher) {
  307. continue;
  308. }
  309. console.log({ matcher });
  310.  
  311. if (patter_match[i].hasOwnProperty('resolver')) {
  312. // complex customize resolver
  313. console.log(patter_match[i].resolver)
  314. let realURI = await patter_match[i].resolver(href);
  315. return realURI;
  316. }
  317.  
  318. const encodeURI = matcher[1];
  319. // simple reg resolver
  320. return decodeURIComponent(encodeURI);
  321. }
  322. return fallbackURI;
  323. }
  324.  
  325. const patternResolve = async (patter_match, href) => {
  326.  
  327. if (patter_match.hasOwnProperty('resolver')) {
  328. // complex customize resolver
  329. console.log(patter_match.resolver)
  330. let realURI = await patter_match.resolver(href);
  331. return realURI;
  332. }
  333.  
  334. if (patter_match.hasOwnProperty('pattern')) {
  335. const matcher = href.match(patter_match.pattern);
  336. if (!matcher) { return href; }
  337. const encodeURI = matcher[1];
  338. return decodeURIComponent(encodeURI);
  339. }
  340.  
  341. return href;
  342. }
  343.  
  344. const getAnchorElement = (e) => {
  345. let target = e.target;
  346. while (target) {
  347. if (target.tagName.toLowerCase() === 'a' && target.hasAttribute('href')) {
  348. return target;
  349. }
  350. target = target.parentElement;
  351. }
  352. return null;
  353. }
  354.  
  355. document.addEventListener('click', (e) => {
  356. console.log({ e })
  357.  
  358. // 找到a标签
  359. let anchor = getAnchorElement(e);
  360. if (!anchor) {
  361. return;
  362. }
  363.  
  364. console.log({ anchor })
  365.  
  366. let href = anchor.getAttribute('href');
  367. if (!href) {
  368. return;
  369. }
  370.  
  371. // 不是url, 则不做处理
  372. // 兼容如 href设置为 'javascript:void(0);' 等的情况
  373. let url = getValidURL(href);
  374. if (url === null) {
  375. return;
  376. }
  377.  
  378. console.log({ url });
  379.  
  380. const matchPattern = getMatchPattern(url);
  381. console.log({ matchPattern });
  382. if (!matchPattern) {
  383. console.log("不匹配处理规则, 如有误, 请反馈给我, 非常感谢");
  384. return;
  385. }
  386.  
  387. e.stopPropagation();
  388. e.preventDefault();
  389.  
  390. let target = '_self';
  391. if (anchor.hasAttribute('target')) {
  392. target = anchor.getAttribute('target');
  393. }
  394.  
  395. // csdn 打开新标签页兼容处理
  396. if (matchPattern == patter_match.csdn) {
  397. target = '_blank';
  398. }
  399.  
  400. console.log({ target });
  401. patternResolve(matchPattern, url.href).then((realURI) => {
  402. console.log({ realURI });
  403. window.open(realURI, target);
  404. }).catch(() => { window.open(href, target); });
  405. }, { capture: true })
  406.  
  407. })();