├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── -------.md
│ └── bug---.md
└── workflows
│ └── build.yml
├── .gitignore
├── .npmignore
├── .vscode
├── extensions.json
└── settings.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── index.ts
├── package.json
├── renovate.json
├── rome.json
├── src
├── app.ts
├── provider.ts
├── sites
│ ├── 51.ruyo.net.ts
│ ├── addons.mozilla.org.ts
│ ├── afadian.net.ts
│ ├── app.yinxiang.com.ts
│ ├── blog.51cto.com.ts
│ ├── blog.csdn.net.ts
│ ├── daily.zhihu.com.ts
│ ├── docs.google.com.ts
│ ├── getpocket.com.ts
│ ├── gitee.com.ts
│ ├── gmail.google.com.ts
│ ├── infoq.cn.ts
│ ├── juejin.com.ts
│ ├── mail.qq.com.ts
│ ├── mijisou.com.ts
│ ├── oschina.com.ts
│ ├── play.google.com.ts
│ ├── sspai.com.ts
│ ├── steamcommunity.com.ts
│ ├── tieba.baidu.com.ts
│ ├── twitter.com.ts
│ ├── video.baidu.com.ts
│ ├── weibo.com.ts
│ ├── www.baidu.com.ts
│ ├── www.dogedoge.com.ts
│ ├── www.douban.com.ts
│ ├── www.google.com.ts
│ ├── www.jianshu.com.ts
│ ├── www.logonews.cn.ts
│ ├── www.so.com.ts
│ ├── www.sogou.com.ts
│ ├── www.youtube.com.ts
│ ├── www.zhihu.com.ts
│ ├── xueshu.baidu.com.ts
│ └── zhuanlan.zhihu.com.ts
└── utils.ts
├── tsconfig.json
└── webpack.config.ts
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: https://github.com/axetroy/buy-me-a-cup-of-tea
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/-------.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 请求支持新站点
3 | about: 请说出你想要支持的站点
4 | title: "支持网站: xxx"
5 | labels: enhancement
6 | assignees: ""
7 | ---
8 |
9 | ### 新的网站支持
10 |
11 | > 请输入你想要支持的网站
12 |
13 | ### 有重定向链接的网页地址
14 |
15 | > 请填写有重定向链接的网页地址,方便我定位支持
16 | >
17 | > 个人精力有限,我不太可能会在网站中一个一个的去找,感谢你的理解
18 | >
19 | > 否则我大概率不会支持
20 |
21 | ### 其他说明
22 |
23 | > 可选
24 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug---.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: BUG 反馈
3 | about: 请带上出现 BUG 的页面地址
4 | title: ""
5 | labels: bug
6 | assignees: ""
7 | ---
8 |
9 | ### BUG 描述
10 |
11 | > 请对这个 BUG 进行描述
12 |
13 | ### 相关的连接
14 |
15 | > 请填写有重定向链接的网页地址,方便我定位排查
16 | >
17 | > 个人精力有限,我不太可能会在网站中一个一个的去找,感谢你的理解
18 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build:
7 | runs-on: ${{ matrix.os }}
8 | strategy:
9 | matrix:
10 | node: ["12.14.1"]
11 | os: [ubuntu-latest, macOS-latest, windows-latest]
12 | name: node.js ${{ matrix.node }} test in ${{ matrix.os }}
13 | steps:
14 | - uses: actions/checkout@v3
15 |
16 | - name: Environment
17 | run: |
18 | node -v
19 | npm -v
20 | yarn --version
21 |
22 | - name: Install
23 | run: yarn
24 |
25 | - name: Linter
26 | run: yarn run lint
27 |
28 | - name: Formater
29 | run: yarn run format
30 |
31 | - name: Compile
32 | run: yarn run build
33 |
34 | - uses: actions/upload-artifact@v3
35 | if: runner.os == 'linux'
36 | with:
37 | name: dist
38 | path: ./dist
39 |
40 | - name: Deploy
41 | if: runner.os == 'linux' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
42 | uses: JamesIves/github-pages-deploy-action@v4.4.3
43 | with:
44 | branch: gh-pages # The branch the action should deploy to.
45 | folder: dist # The folder the action should deploy.
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 |
24 | # nyc test coverage
25 | .nyc_output
26 |
27 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
28 | .grunt
29 |
30 | # Bower dependency directory (https://bower.io/)
31 | bower_components
32 |
33 | # node-waf configuration
34 | .lock-wscript
35 |
36 | # Compiled binary addons (https://nodejs.org/api/addons.html)
37 | build/Release
38 |
39 | # Dependency directories
40 | node_modules/
41 | jspm_packages/
42 |
43 | # TypeScript v1 declaration files
44 | typings/
45 |
46 | # Optional npm cache directory
47 | .npm
48 |
49 | # Optional eslint cache
50 | .eslintcache
51 |
52 | # Optional REPL history
53 | .node_repl_history
54 |
55 | # Output of 'npm pack'
56 | *.tgz
57 |
58 | # Yarn Integrity file
59 | .yarn-integrity
60 |
61 | # dotenv environment variables file
62 | .env
63 | .env.test
64 |
65 | # parcel-bundler cache (https://parceljs.org/)
66 | .cache
67 |
68 | # next.js build output
69 | .next
70 |
71 | # nuxt.js build output
72 | .nuxt
73 |
74 | # vuepress build output
75 | .vuepress/dist
76 |
77 | # Serverless directories
78 | .serverless/
79 |
80 | # FuseBox cache
81 | .fusebox/
82 |
83 | # DynamoDB Local files
84 | .dynamodb/
85 |
86 | dist
87 | .idea
88 | yarn.lock
89 | package-lock.json
90 | .DS_Store
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Node template
3 | # Logs
4 | logs
5 | *.log
6 | npm-debug.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # node-waf configuration
27 | .lock-wscript
28 |
29 | # Compiled binary addons (http://nodejs.org/api/addons.html)
30 | build/Release
31 |
32 | # Dependency directories
33 | node_modules
34 | jspm_packages
35 |
36 | # Optional npm cache directory
37 | .npm
38 |
39 | # Optional eslint cache
40 | .eslintcache
41 |
42 | # Optional REPL history
43 | .node_repl_history
44 |
45 | # Output of 'npm pack'
46 | *.tgz
47 |
48 | # Yarn Integrity file
49 | .yarn-integrity
50 |
51 | # IDEA
52 | .idea
53 |
54 | # dist
55 | dist
56 |
57 | .travis.yml
58 | yarn.lock
59 | package-lock.json
60 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["rome.rome"]
3 | }
4 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "[typescript]": {
3 | "editor.defaultFormatter": "rome.rome"
4 | },
5 | "[javascript]": {
6 | "editor.defaultFormatter": "rome.rome"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## v2.21.1 (2023-07-21)
2 |
3 | ### Bugs fixed:
4 |
5 | - youtube([`ea804d1`](https://github.com/axetroy/anti-redirect/commit/ea804d125cb9de9fa3fccf290d4a87d1b8fb0905)) (by Axetroy)
6 |
7 | ## v2.21.0 (2023-05-29)
8 |
9 | ### New feature:
10 |
11 | - 增加少数派的支持, close #620([`27a53ed`](https://github.com/axetroy/anti-redirect/commit/27a53ed5f4f03985638fa0bff419e1b8aa4b6b7d)) (by Axetroy)
12 | - 支持 csdn 自定义域名, close #623([`30ae45f`](https://github.com/axetroy/anti-redirect/commit/30ae45fec6c3d7a4d91c05d61ae0152fe77f510e)) (by Axetroy)
13 | - 支持 gitee.com, close #641([`0dacbf2`](https://github.com/axetroy/anti-redirect/commit/0dacbf2883ddd5bed3f4b1bd11304df343bb34a6)) (by Axetroy)
14 | - add xie.infoq.cn([`d4df9b2`](https://github.com/axetroy/anti-redirect/commit/d4df9b24bdbef29d17eaa20e7ebf555f0d75771a)) (by Axetroy)
15 |
16 | ### Bugs fixed:
17 |
18 | - format([`bf5fb2a`](https://github.com/axetroy/anti-redirect/commit/bf5fb2a4be1ed2694eb87a7158086d6b163ff346)) (by Axetroy)
19 | - lint([`a059c15`](https://github.com/axetroy/anti-redirect/commit/a059c15f54a3d1f33a736388943bd350c132ff32)) (by Axetroy)
20 | - lint([`eaf94e8`](https://github.com/axetroy/anti-redirect/commit/eaf94e81adf8997aa29863c708c4a21644f66704)) (by Axetroy)
21 |
22 | ## v2.20.0 (2022-10-03)
23 |
24 | ### New feature:
25 |
26 | - 支持 51 CTO. close #566([`ddfa551`](https://github.com/axetroy/anti-redirect/commit/ddfa5510e01841d94a3fb0f20f687fb743a5100c)) (by Axetroy)
27 | - 支持爱发电. close #598([`35d0b1b`](https://github.com/axetroy/anti-redirect/commit/35d0b1bd392ceb6505cf5a60721d7c8627098449)) (by Axetroy)
28 |
29 | ### Bugs fixed:
30 |
31 | - 修复 Google 的其他地方站点. close #599([`2a89351`](https://github.com/axetroy/anti-redirect/commit/2a8935123243f89951ad40545444f8741eea33ee)) (by Axetroy)
32 |
33 | ## v2.19.6 (2022-05-02)
34 |
35 | ### Bugs fixed:
36 |
37 | - google([`c64381e`](https://github.com/axetroy/anti-redirect/commit/c64381e9f6bc7fee108e3daf3af5743ca5b0278c)) (by Axetroy)
38 | - google([`ddfcb55`](https://github.com/axetroy/anti-redirect/commit/ddfcb55ad05df07561ff477a454b3f7e7bc951af)) (by Axetroy)
39 |
40 | ## v2.19.5 (2022-04-10)
41 |
42 | ### Bugs fixed:
43 |
44 | - 提升百度的稳定性([`0efa80f`](https://github.com/axetroy/anti-redirect/commit/0efa80fde242eeaf092cc9d69b547dd3a8e78397)) (by Axetroy)
45 |
46 | ## v2.19.4 (2022-04-10)
47 |
48 | ### New feature:
49 |
50 | - 支持标志情报局. close #481([`84e7231`](https://github.com/axetroy/anti-redirect/commit/84e72315d65fbf75e4b84d56718fb8f81621bdba)) (by Axetroy)
51 |
52 | ## v2.19.3 (2022-04-10)
53 |
54 | ## v2.19.2 (2022-04-10)
55 |
56 | ### New feature:
57 |
58 | - 支持 juejin.cn([`e26c6a2`](https://github.com/axetroy/anti-redirect/commit/e26c6a25834c7638dffe0db3fc27cb2e51ee7880)) (by Axetroy)
59 |
60 | ### Bugs fixed:
61 |
62 | - twitter. close #565([`b43b527`](https://github.com/axetroy/anti-redirect/commit/b43b52715b64107bd71799d344e2ead2296c396a)) (by Axetroy)
63 |
64 | ## v2.19.0 (2021-03-22)
65 |
66 | ### New feature:
67 |
68 | - 支持 oschina([`e2e15c7`](https://github.com/axetroy/anti-redirect/commit/e2e15c77a90011b8dd301f506a2765e3260b9c90)) (by axetroy)
69 |
70 | ## v2.18.7 (2021-03-09)
71 |
72 | ## v2.18.6 (2021-03-09)
73 |
74 | ### Bugs fixed:
75 |
76 | - 修复 CSDN 的 TOC 重定向会把 hash 地址的跳转错误([`c1a2c2e`](https://github.com/axetroy/anti-redirect/commit/c1a2c2e8377d2a486ef0146d6e8c4a0f3d84c198)) (by axetroy)
77 |
78 | ## v2.18.5 (2021-01-20)
79 |
80 | ### Bugs fixed:
81 |
82 | - 修复简书有些链接没有去除重定向 close #366([`b5cb3c7`](https://github.com/axetroy/anti-redirect/commit/b5cb3c7eca7ad8fb79ec9c678710e8a205860db4)) (by axetroy)
83 |
84 | ## v2.18.4 (2021-01-02)
85 |
86 | ## v2.18.3 (2020-12-31)
87 |
88 | ## v2.18.2 (2020-12-31)
89 |
90 | ## v2.18.0 (2020-12-25)
91 |
92 | ### New feature:
93 |
94 | - 支持印象笔记. close #344([`ef4a5e2`](https://github.com/axetroy/anti-redirect/commit/ef4a5e2ebb251f37cfb02c17b92e5ff5f109930e)) (by axetroy)
95 |
96 | ## v2.17.0 (2020-12-25)
97 |
98 | ### New feature:
99 |
100 | - 支持 CSDN. close #350([`814b162`](https://github.com/axetroy/anti-redirect/commit/814b16297f3a4c8588c13644da06cd2eb1b6d4fd)) (by axetroy)
101 |
102 | ## v2.16.2 (2020-10-25)
103 |
104 | ### Bugs fixed:
105 |
106 | - #277([`4c0908d`](https://github.com/axetroy/anti-redirect/commit/4c0908d0dcd2254af5f18585e86ba9088a3f3bbe)) (by axetroy)
107 |
108 | ## v2.16.0 (2020-09-23)
109 |
110 | ### New feature:
111 |
112 | - 添加秘迹的支持 close #251([`6a10c3c`](https://github.com/axetroy/anti-redirect/commit/6a10c3cb40e351534d333cbc051f79887eb422da)) (by axetroy)
113 |
114 | ### Bugs fixed:
115 |
116 | - 修复在搜索结果中打不开的百度文库 close #212([`f845d24`](https://github.com/axetroy/anti-redirect/commit/f845d24d006c9db92829c9938976a52ee10ab8f8)) (by axetroy)
117 |
118 | ## v2.15.2 (2020-06-07)
119 |
120 | ### New feature:
121 |
122 | - 支持 youtube channel 页 (#243)([`5bfd7e9`](https://github.com/axetroy/anti-redirect/commit/5bfd7e942214cc34b3660995a9a057146666ac54)) (by 人造电子小猫咪)
123 |
124 | ## v2.15.1 (2020-04-10)
125 |
126 | ### Bugs fixed:
127 |
128 | - 修复 360 搜索. close #204([`f16b81c`](https://github.com/axetroy/anti-redirect/commit/f16b81cdd451527e69859aa65917214154737611)) (by axetroy)
129 |
130 | ## v2.15.0 (2020-03-24)
131 |
132 | ### New feature:
133 |
134 | - 支持 steam. close #48([`0256a46`](https://github.com/axetroy/anti-redirect/commit/0256a4667ccbdf38f3fcb4cf10cb70500a09d73e)) (by axetroy)
135 |
136 | ### Bugs fixed:
137 |
138 | - 修复简书部分页面没有去除重定向 close #199([`3d8c78b`](https://github.com/axetroy/anti-redirect/commit/3d8c78b7d95d6d69c78277ef2dea6e6253e57e39)) (by axetroy)
139 |
140 | ## v2.14.5 (2020-02-26)
141 |
142 | ### Bugs fixed:
143 |
144 | - 适配 Google 的新追踪策略. close #176([`5cb1da0`](https://github.com/axetroy/anti-redirect/commit/5cb1da06d1d6337c922363a539c50984ed17b556)) (by axetroy)
145 |
146 | ## v2.14.4 (2020-02-11)
147 |
148 | ## v2.14.3 (2020-02-11)
149 |
150 | ### New feature:
151 |
152 | - 部分网站的去除重定向请求,不再携带 cookie([`279d80c`](https://github.com/axetroy/anti-redirect/commit/279d80c39fe1859b6d9a61e1eb09f6c6e9f9ca7f)) (by axetroy)
153 |
154 | ## v2.14.2 (2019-12-11)
155 |
156 | ### Bugs fixed:
157 |
158 | - 简书([`56503b4`](https://github.com/axetroy/anti-redirect/commit/56503b45aac07708258a289595adf6248e7f31a1)) (by axetroy)
159 |
160 | ## v2.14.1 (2019-11-26)
161 |
162 | ### Bugs fixed:
163 |
164 | - update([`ea15955`](https://github.com/axetroy/anti-redirect/commit/ea15955d55445e57c905c74fb98815aac48e53cb)) (by axetroy)
165 |
166 | ## v2.14.0 (2019-11-26)
167 |
168 | ### New feature:
169 |
170 | - support ruyo. close #155([`4f3bded`](https://github.com/axetroy/anti-redirect/commit/4f3bdeda6a3f7f4da0c02c383f72372f63c8b812)) (by axetroy)
171 |
172 | ## v2.13.1 (2019-09-28)
173 |
174 | ## v2.13.0 (2019-09-23)
175 |
176 | ### New feature:
177 |
178 | - support dogedoge.com. close #127([`dba98e0`](https://github.com/axetroy/anti-redirect/commit/dba98e0cd6af30d8e5fd6222e15a677bb4264ca4)) (by Axetroy)
179 |
180 | ## v2.12.0 (2019-06-24)
181 |
182 | ## v2.11.3 (2019-05-21)
183 |
184 | ## v2.11.2 (2019-04-10)
185 |
186 | ### Bugs fixed:
187 |
188 | - 更精准的重定向去除. close #49([`0d0bb46`](https://github.com/axetroy/anti-redirect/commit/0d0bb466e81dfdb92671a848c59d9d7065e2fc30)) (by axetroy)
189 |
190 | ## v2.11.1 (2019-04-10)
191 |
192 | ## v2.11.0 (2019-01-17)
193 |
194 | ### New feature:
195 |
196 | - 支持去除 pocket 重定向, close #47([`a259e6c`](https://github.com/axetroy/anti-redirect/commit/a259e6c7789b6916823975f6ec4df222a6d04350)) (by axetroy)
197 |
198 | ## v2.10.1 (2019-01-10)
199 |
200 | ### Bugs fixed:
201 |
202 | - 修复 QQ 邮箱失效的问题 close #40([`ce9a6c0`](https://github.com/axetroy/anti-redirect/commit/ce9a6c0c67fc354966d52ff93e33f97fb7ebaecc)) (by axetroy)
203 | - 由于豆瓣的问题,暂时只支持部分页面生效去重定向([`9b7eaed`](https://github.com/axetroy/anti-redirect/commit/9b7eaed6652e558ff5b144ef27e8cf93b6b8a473)) (by axetroy)
204 |
205 | ## v2.10.0 (2018-12-20)
206 |
207 | ### New feature:
208 |
209 | - 支持豆瓣评分([`b11073f`](https://github.com/axetroy/anti-redirect/commit/b11073f8055a5e98994a5ceebe74e5c27b34deb3)) (by axetroy)
210 |
211 | ## v2.9.0 (2018-04-28)
212 |
213 | ### New feature:
214 |
215 | - 支持使用 boolean 和 function 来定义一个 provider 是否应该启动([`61dccf1`](https://github.com/axetroy/anti-redirect/commit/61dccf12532afa7be6ca7ad914520050026b8887)) (by axetroy)
216 | - support google play([`ae822b8`](https://github.com/axetroy/anti-redirect/commit/ae822b80c7ade51921b55f743a593d97a40d0cfa)) (by axetroy)
217 | - 支持 gmail([`6f21f91`](https://github.com/axetroy/anti-redirect/commit/6f21f9128adf3569fb57033f42d3497f56951a05)) (by axetroy)
218 |
219 | ## v2.8.0 (2018-04-27)
220 |
221 | ## v2.7.0 (2018-04-25)
222 |
223 | ### New feature:
224 |
225 | - 支持简书([`df87bf8`](https://github.com/axetroy/anti-redirect/commit/df87bf8c46d2fdbf881bd7850e7d52c521447d11)) (by axetroy)
226 |
227 | ## v2.6.1 (2018-03-15)
228 |
229 | ### New feature:
230 |
231 | - 添加支持火狐浏览器扩展页支持([`b9f9d4d`](https://github.com/axetroy/anti-redirect/commit/b9f9d4d2dd49c5219ef3be61a18755353d98eb84)) (by axetroy)
232 |
233 | ### Bugs fixed:
234 |
235 | - 修复 so.com 再翻页中有问题的 bug([`7ae798f`](https://github.com/axetroy/anti-redirect/commit/7ae798f4a0a8b8b8d2448b790fbd030bbfb55f6c)) (by axetroy)
236 |
237 | ## v2.6.0 (2018-02-21)
238 |
239 | ### New feature:
240 |
241 | - 添加支持火狐浏览器扩展页面的重定向([`51c91f7`](https://github.com/axetroy/anti-redirect/commit/51c91f7df1c42de5fd975e3ca7338890db0e8539)) (by axetroy)
242 |
243 | ## v2.5.0 (2018-02-21)
244 |
245 | ### New feature:
246 |
247 | - 支持去除 QQ 邮箱重定向([`34d0d66`](https://github.com/axetroy/anti-redirect/commit/34d0d66ad333ea5c1c395f8528ef9bf8045a7223)) (by axetroy)
248 |
249 | ## v2.4.1 (2018-01-04)
250 |
251 | ### Bugs fixed:
252 |
253 | - 修复百度学术有部分跳转链接没有去除的问题([`d10755a`](https://github.com/axetroy/anti-redirect/commit/d10755a516fd8024bc3514469782941661516f6e)) (by axetroy)
254 |
255 | ## v2.4.0 (2018-01-04)
256 |
257 | ### New feature:
258 |
259 | - 支持百度学术([`446f5b9`](https://github.com/axetroy/anti-redirect/commit/446f5b995cc2117f8f0c1f283d8b31bd7b0ae394)) (by axetroy)
260 |
261 | ## v2.3.1 (2017-09-19)
262 |
263 | ### Bugs fixed:
264 |
265 | - **sogou**: 修复搜狗搜索下, 和翻页脚本不兼容的问题([`6ebf396`](https://github.com/axetroy/anti-redirect/commit/6ebf396a1f932805e4027a39468cd2e83ded207c)) (by axetroy)
266 |
267 | ## v2.3.0 (2017-09-04)
268 |
269 | ### New feature:
270 |
271 | - 如果页面处于初始的状态,没有滚动过,则出发一次 onScroll 事件([`5f112d3`](https://github.com/axetroy/anti-redirect/commit/5f112d3ccb657679ae5209fc0dea89cf532c6241)) (by axetroy)
272 | - 百度贴吧去除重定向成功之后,添加成功的标记([`35c952a`](https://github.com/axetroy/anti-redirect/commit/35c952a6f0b45ade3e68f9ceff24a337875b65e3)) (by axetroy)
273 |
274 | ## v2.2.0 (2017-09-04)
275 |
276 | ### New feature:
277 |
278 | - 支持掘金去除重定向([`ad55ca9`](https://github.com/axetroy/anti-redirect/commit/ad55ca9d3e02ad66f65706ad72f2e2e4763c61d1)) (by axetroy)
279 | - provider 继承自事件发生器,监听页面滚动,会自动去除重定向([`168e4d8`](https://github.com/axetroy/anti-redirect/commit/168e4d861f6df0c53399f7ffd30581214ceaf28d)) (by axetroy)
280 | - **changelog**: support generate CHANGELOG([`bcb24ea`](https://github.com/axetroy/anti-redirect/commit/bcb24ea337ecf4e3f7fcb4033d7c6dbddfaa59cb)) (by axetroy)
281 |
282 | ## v2.1.0 (2017-09-04)
283 |
284 | ### New feature:
285 |
286 | - 支持 google docs 站点([`11eafae`](https://github.com/axetroy/anti-redirect/commit/11eafaee95e744ce7e6f57cd770d83aea3e99ac2)) (by axetroy)
287 | - add es6-promise polyfill([`46d95c6`](https://github.com/axetroy/anti-redirect/commit/46d95c6516b28b798f95beea06cc5a438d42e269)) (by axetroy)
288 |
289 | ## v2.0.0 (2017-09-02)
290 |
291 | ### New feature:
292 |
293 | - add meta info([`61dfab9`](https://github.com/axetroy/anti-redirect/commit/61dfab96348177bc1ee8ce6102e6cdbefd79e30f)) (by Axetroy)
294 |
295 | ### Bugs fixed:
296 |
297 | - 修复贴吧部分重定向无法去处的问题([`a8b92cb`](https://github.com/axetroy/anti-redirect/commit/a8b92cbf5fe57d55d703885f9be41d41f32d92c2)) (by axetroy)
298 | - typo([`56912cd`](https://github.com/axetroy/anti-redirect/commit/56912cd3dcce92b2c75500962f8298ecc83eb78e)) (by axetroy)
299 | - fix about class Query([`d3ca05d`](https://github.com/axetroy/anti-redirect/commit/d3ca05ddf572fd2f3d254eefc6da2bbed222589e)) (by axetroy)
300 | - fix a bug about class Query([`bd1505d`](https://github.com/axetroy/anti-redirect/commit/bd1505d0b87716ea7fec788c6f5a28fb6db35d56)) (by axetroy)
301 |
302 | ## 1.2.0 (2016-12-29)
303 |
304 | ### New feature:
305 |
306 | - 支持 sogou 搜索([`1316e26`](https://github.com/axetroy/anti-redirect/commit/1316e26352f8a05fd31ee16f9cf1d16b7d5f869a)) (by axetroy)
307 |
308 | ## 1.1.0 (2016-12-29)
309 |
310 | ### New feature:
311 |
312 | - 支持知乎专栏([`eed370d`](https://github.com/axetroy/anti-redirect/commit/eed370d8caf0908fea359f5644be5f1a88a468a1)) (by axetroy)
313 |
314 | ## 1.0.1 (2016-12-23)
315 |
316 | ### New feature:
317 |
318 | - add baidu-video support([`b421c6e`](https://github.com/axetroy/anti-redirect/commit/b421c6e39bb32af67d2ddce98a0074ae6f5f8def)) (by axetroy)
319 | - **tieba**: 添加对百度贴吧反重定向的支持([`516ef7e`](https://github.com/axetroy/anti-redirect/commit/516ef7e7b17c9558b0821c34bfbef8033fe7a055)) (by axetroy)
320 | - **new**: 新增 weibo/twitter 的去除跳转,以及重构一些代码([`cb07843`](https://github.com/axetroy/anti-redirect/commit/cb0784331c56c649bbc4b20df690ada50ae72300)) (by axetroy)
321 | - **\***: rebuild([`e8030dd`](https://github.com/axetroy/anti-redirect/commit/e8030dd0d39dbded7b298169932f488e576827cf)) (by axetroy)
322 | - **more**: 重构脚本,支持更多站点([`c366c51`](https://github.com/axetroy/anti-redirect/commit/c366c51d8596ce1aa2c40a67e619f7fed77d8763)) (by axetroy)
323 |
324 | ### Bugs fixed:
325 |
326 | - 修复 360so 解析地址不正确的问题(锅是 360 的,不关我事)([`859ab94`](https://github.com/axetroy/anti-redirect/commit/859ab9483769382f26d31eceb52ca51d218188ea)) (by axetroy)
327 | - **google**: 修复谷歌搜索反重定向不正确的问题,以及重构部分代码([`edc68d4`](https://github.com/axetroy/anti-redirect/commit/edc68d4e7f3017bd07336673c14b4ad6c7ec4f67)) (by axetroy)
328 | - **proto**: 修复获取下,获取协议的兼容性,以及应用启动的部分参数([`d2bb7ef`](https://github.com/axetroy/anti-redirect/commit/d2bb7ef9ffb9a5fe76fcfaf967a72d5c0e9a936b)) (by axetroy)
329 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2022 Axetroy
2 |
3 | Anti 996 License Version 1.0 (Draft)
4 |
5 | Permission is hereby granted to any individual or legal entity
6 | obtaining a copy of this licensed work (including the source code,
7 | documentation and/or related items, hereinafter collectively referred
8 | to as the "licensed work"), free of charge, to deal with the licensed
9 | work for any purpose, including without limitation, the rights to use,
10 | reproduce, modify, prepare derivative works of, distribute, publish
11 | and sublicense the licensed work, subject to the following conditions:
12 |
13 | 1. The individual or the legal entity must conspicuously display,
14 | without modification, this License and the notice on each redistributed
15 | or derivative copy of the Licensed Work.
16 |
17 | 2. The individual or the legal entity must strictly comply with all
18 | applicable laws, regulations, rules and standards of the jurisdiction
19 | relating to labor and employment where the individual is physically
20 | located or where the individual was born or naturalized; or where the
21 | legal entity is registered or is operating (whichever is stricter). In
22 | case that the jurisdiction has no such laws, regulations, rules and
23 | standards or its laws, regulations, rules and standards are
24 | unenforceable, the individual or the legal entity are required to
25 | comply with Core International Labor Standards.
26 |
27 | 3. The individual or the legal entity shall not induce or force its
28 | employee(s), whether full-time or part-time, or its independent
29 | contractor(s), in any methods, to agree in oral or written form, to
30 | directly or indirectly restrict, weaken or relinquish his or her
31 | rights or remedies under such laws, regulations, rules and standards
32 | relating to labor and employment as mentioned above, no matter whether
33 | such written or oral agreement are enforceable under the laws of the
34 | said jurisdiction, nor shall such individual or the legal entity
35 | limit, in any methods, the rights of its employee(s) or independent
36 | contractor(s) from reporting or complaining to the copyright holder or
37 | relevant authorities monitoring the compliance of the license about
38 | its violation(s) of the said license.
39 |
40 | THE LICENSED WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
41 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
42 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
43 | IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM,
44 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
45 | OTHERWISE, ARISING FROM, OUT OF OR IN ANY WAY CONNECTION WITH THE
46 | LICENSED WORK OR THE USE OR OTHER DEALINGS IN THE LICENSED WORK.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/axetroy/anti-redirect/actions)
2 | [](https://deepscan.io/dashboard#view=project&tid=5773&pid=7595&bid=79869)
3 | [](https://996.icu)
4 | [](https://github.com/996icu/996.ICU/blob/master/LICENSE)
5 |
6 | ### GM 脚本,反重定向
7 |
8 | 去除各搜索引擎/常用网站的重定向
9 |
10 | > 注意事项:
11 | >
12 | > 重定向一般有两种目的
13 | >
14 | > 1. 追踪用户打开了哪些 URL
15 | > 2. 在用户跳转到站外之前进行确认地址,防止打开不明的页面
16 |
17 | ### 反馈地址
18 |
19 | > 反馈最好能带上出问题的网页地址
20 |
21 | - https://github.com/axetroy/anti-redirect/issues/new/choose
22 | - https://github.com/axetroy/anti-redirect/issues/new/choose
23 | - https://github.com/axetroy/anti-redirect/issues/new/choose
24 |
25 | ### 如果这能够帮助到你, 不妨点个 star, 你的支持就是我更新的动力
26 |
27 | [点击从 Github 安装](https://github.com/axetroy/anti-redirect/raw/gh-pages/anti-redirect.user.js)
28 |
29 | [点击从 GreasyFork 安装](https://greasyfork.org/scripts/11915-anti-redirect-typescript/code/anti-redirect.user.js)
30 |
31 | [点击从 CDN 安装(国内用户)](https://cdn.jsdelivr.net/gh/axetroy/anti-redirect@gh-pages/anti-redirect.user.js)
32 |
33 | ### 工作原理
34 |
35 | 1. 根据 URL 上暴露出来的跳转链接,正则匹配提取真实的地址,例如知乎,Google
36 | 2. 如果 A 标签的内容为真实的地址,则替换,例如百度贴吧
37 | 3. 逐一发送请求,获取真实的地址,例如百度搜索
38 | 4. 根据请求特殊页面,这个特殊页面没有重定向地址,然后覆盖当前页,例如百度搜索,搜狗搜索
39 | 5. 覆盖原本的链接点击事件,比如 qq 邮箱
40 |
41 | ### 更新日志
42 |
43 | [https://github.com/axetroy/anti-redirect/blob/master/CHANGELOG.md](https://github.com/axetroy/anti-redirect/blob/master/CHANGELOG.md)
44 |
45 | ### 支持的站点
46 |
47 | - [x] 知乎
48 | - [x] 知乎专栏
49 | - [x] 知乎日报
50 | - [x] Google 搜索
51 | - [x] Google 文档
52 | - [x] Google Play
53 | - [x] Google Gmail
54 | - [x] Google Youtube
55 | - [x] Steam
56 | - [x] 360 搜索
57 | - [x] 新浪微博
58 | - [x] Twitter
59 | - [x] 搜狗搜索
60 | - [x] 百度搜索
61 | - [x] 百度视频
62 | - [x] 百度学术
63 | - [x] 百度贴吧
64 | - [x] 掘金
65 | - [x] QQ 邮箱
66 | - [x] Mozilla
67 | - [x] 简书
68 | - [x] 豆瓣
69 | - [x] Pocket
70 | - [x] DogeDoge
71 | - [x] 秘迹
72 | - [x] CSDN
73 | - [x] 开源中国
74 | - [x] 印象笔记
75 | - [x] 标志情报局
76 | - [x] 爱发电
77 | - [x] 51 CTO
78 | - [x] InfoQ
79 | - [x] Gitee
80 | - [x] 少数派
81 |
82 | 更多
83 |
84 | - [x] 51.ruyo.net
85 |
86 |
87 |
88 | ### 我想支持更多的站点
89 |
90 | 点击这个[链接](https://github.com/axetroy/anti-redirect/issues/new),提交 issues,说出你想要支持的站点
91 |
92 | ### 贡献代码
93 |
94 | 需要通过 NodeJs 把 TypeScript 编译成 javascript
95 |
96 | ```bash
97 | git clone https://github.com/axetroy/anti-redirect.git
98 |
99 | cd ./anti-redirect
100 |
101 | npm install
102 | npm run watch
103 | ```
104 |
105 | ### 开源许可
106 |
107 | The [Anti 996 License](https://github.com/axetroy/anti-redirect/blob/master/LICENSE)
108 |
109 | 请仔细阅读开源许可。
110 |
111 | 简而言之: 如果你正在 996,或者你的公司/单位正在 996 ,那么请不要安装这个脚本!
112 |
--------------------------------------------------------------------------------
/index.ts:
--------------------------------------------------------------------------------
1 | import { App } from "@/app";
2 | import { RuyoProvider } from "@/sites/51.ruyo.net";
3 | import { MozillaProvider } from "@/sites/addons.mozilla.org";
4 | import { YinXiangProvider } from "@/sites/app.yinxiang.com";
5 | import { CSDNProvider } from "@/sites/blog.csdn.net";
6 | import { OSChinaProvider } from "@/sites/oschina.com";
7 | import { ZhihuDailyProvider } from "@/sites/daily.zhihu.com";
8 | import { GoogleDocsProvider } from "@/sites/docs.google.com";
9 | import { PocketProvider } from "@/sites/getpocket.com";
10 | import { GmailProvider } from "@/sites/gmail.google.com";
11 | import { JuejinProvider } from "@/sites/juejin.com";
12 | import { QQMailProvider } from "@/sites/mail.qq.com";
13 | import { MiJiProvider } from "@/sites/mijisou.com";
14 | import { GooglePlayProvider } from "@/sites/play.google.com";
15 | import { SteamProvider } from "@/sites/steamcommunity.com";
16 | import { TiebaProvider } from "@/sites/tieba.baidu.com";
17 | import { TwitterProvider } from "@/sites/twitter.com";
18 | import { BaiduVideoProvider } from "@/sites/video.baidu.com";
19 | import { WeboProvider } from "@/sites/weibo.com";
20 | import { BaiduProvider } from "@/sites/www.baidu.com";
21 | import { DogeDogeProvider } from "@/sites/www.dogedoge.com";
22 | import { DouBanProvider } from "@/sites/www.douban.com";
23 | import { GoogleProvider } from "@/sites/www.google.com";
24 | import { JianShuProvider } from "@/sites/www.jianshu.com";
25 | import { SoProvider } from "@/sites/www.so.com";
26 | import { SoGouProvider } from "@/sites/www.sogou.com";
27 | import { YoutubeProvider } from "@/sites/www.youtube.com";
28 | import { ZhihuProvider } from "@/sites/www.zhihu.com";
29 | import { BaiduXueshuProvider } from "@/sites/xueshu.baidu.com";
30 | import { ZhihuZhuanlanProvider } from "@/sites/zhuanlan.zhihu.com";
31 | import { LogonewsProvider } from "@/sites/www.logonews.cn";
32 | import { AfDianNetProvider } from "@/sites/afadian.net";
33 | import { Blog51CTO } from "@/sites/blog.51cto.com";
34 | import { InfoQProvider } from "@/sites/infoq.cn";
35 | import { GiteeProvider } from "@/sites/gitee.com";
36 | import { SSPaiProvider } from "@/sites/sspai.com";
37 | import http from "gm-http";
38 |
39 | const app = new App();
40 | const isDebug: boolean = process.env.NODE_ENV !== "production";
41 |
42 | http.setConfig({ debug: isDebug });
43 |
44 | app
45 | .setConfig({ isDebug })
46 | .registerProvider([
47 | {
48 | // 测试地址: https://www.zhihu.com/question/25258775
49 | name: "知乎",
50 | test: /www\.zhihu\.com/,
51 | provider: ZhihuProvider,
52 | },
53 | {
54 | // 测试地址: https://zhuanlan.zhihu.com/p/20549978
55 | name: "知乎专栏",
56 | test: /zhuanlan\.zhihu\.com/,
57 | provider: ZhihuZhuanlanProvider,
58 | },
59 | {
60 | // 测试地址:
61 | name: "知乎日报",
62 | test: /daily\.zhihu\.com/,
63 | provider: ZhihuDailyProvider,
64 | },
65 | {
66 | name: "Google搜索",
67 | test: /\w+\.google\./,
68 | provider: GoogleProvider,
69 | },
70 | {
71 | // 测试地址: https://docs.google.com/spreadsheets/d/1TFcEXMcKrwoIAECIVyBU0GPoSmRqZ7A0VBvqeKYVSww/htmlview
72 | name: "Google Docs",
73 | test: /docs\.google\.com/,
74 | provider: GoogleDocsProvider,
75 | },
76 | {
77 | name: "Gmail",
78 | test: /mail\.google\.com/,
79 | provider: GmailProvider,
80 | },
81 | {
82 | // 测试地址: https://play.google.com/store/movies/details/%E7%A7%BB%E5%8B%95%E8%BF%B7%E5%AE%AE_%E6%AD%BB%E4%BA%A1%E8%A7%A3%E8%97%A5?id=YNy7gRqwtMk
83 | name: "Google Play",
84 | test: /play\.google\.com/,
85 | provider: GooglePlayProvider,
86 | },
87 | {
88 | // 测试地址: https://www.youtube.com/watch?v=XTXSRRSv1bY
89 | name: "Google Youtube",
90 | test: /www\.youtube\.com/,
91 | provider: YoutubeProvider,
92 | },
93 | {
94 | // 测试地址: https://www.so.com/s?ie=utf-8&fr=none&src=360sou_newhome&q=chrome
95 | name: "360搜索",
96 | test: /www\.so\.com/,
97 | provider: SoProvider,
98 | },
99 | {
100 | name: "新浪微博",
101 | test: /\.weibo\.com/,
102 | provider: WeboProvider,
103 | },
104 | // 测试: https://twitter.com/ftium4/status/1512815116810522631
105 | {
106 | name: "Twitter",
107 | test: /twitter\.com/,
108 | provider: TwitterProvider,
109 | },
110 | {
111 | // 测试: http://www.sogou.com/web?query=chrome&_asf=www.sogou.com&_ast=&w=01019900&p=40040100&ie=utf8&from=index-nologin&s_from=index&sut=1527&sst0=1504347367611&lkt=0%2C0%2C0&sugsuv=00091651B48CA45F593B61A29B131405&sugtime=1504347367611
112 | name: "搜狗搜索",
113 | test: /www\.sogou\.com/,
114 | provider: SoGouProvider,
115 | },
116 | {
117 | // 测试: https://www.baidu.com/s?wd=chrome&rsv_spt=1&rsv_iqid=0xcb136237000ed40e&issp=1&f=8&rsv_bp=0&rsv_idx=2&ie=utf-8&tn=baidulocal&rsv_enter=1&rsv_sug3=7&rsv_sug1=7&rsv_sug7=101&rsv_sug2=0&inputT=813&rsv_sug4=989×tamp=1504349229266&rn=50&vf_bl=1
118 | name: "百度搜索",
119 | test: /www\.baidu\.com/,
120 | provider: BaiduProvider,
121 | },
122 | {
123 | // 测试: https://www.baidu.com/s?wd=chrome&pn=20&oq=chrome&tn=baiduhome_pg&ie=utf-8&usm=3&rsv_idx=2&rsv_pq=e043900d0000752d&rsv_t=6bb0UqEwp2Tle6TAMBDlU3Wg%2BSxoqvvOhZKyQgM%2BVQP8Gc54QZLhcDcj62eGfNG75aq5&rsv_page=1
124 | name: "百度视频",
125 | test: /v\.baidu\.com/,
126 | provider: BaiduVideoProvider,
127 | },
128 | {
129 | // 测试: http://xueshu.baidu.com/s?wd=paperuri%3A%28ae4d6b5da05eca552dab05aeefb966e6%29&ie=utf-8&filter=sc_long_sign&sc_ks_para=q%3D%E2%80%9C%E4%BA%92%E8%81%94%E7%BD%91%2B%E5%81%A5%E5%BA%B7%E7%AE%A1%E7%90%86%E2%80%9D%E6%A8%A1%E5%BC%8F%E6%8E%A2%E8%AE%A8%E5%8F%8A%E5%85%B6%E5%BA%94%E7%94%A8&tn=SE_baiduxueshu_c1gjeupa
130 | name: "百度学术",
131 | test: /xueshu\.baidu\.com/,
132 | provider: BaiduXueshuProvider,
133 | },
134 | {
135 | // 测试地址: http://tieba.baidu.com/p/5300844180
136 | name: "百度贴吧",
137 | test: /tieba\.baidu\.com/,
138 | provider: TiebaProvider,
139 | },
140 | {
141 | // 测试地址: https://juejin.im/entry/59ac8fa551882524241a8802?utm_source=gold_browser_extension
142 | name: "掘金",
143 | test: /juejin\.(im|cn)/,
144 | provider: JuejinProvider,
145 | },
146 | {
147 | name: "QQ邮箱",
148 | test: /mail\.qq\.com/,
149 | provider: QQMailProvider,
150 | },
151 | {
152 | // 测试地址: https://addons.mozilla.org/zh-CN/firefox/addon/evernote-web-clipper/
153 | name: "Mozilla",
154 | test: /addons\.mozilla\.org/,
155 | provider: MozillaProvider,
156 | },
157 | {
158 | // 测试地址: https://www.jianshu.com/p/979776ca44b8
159 | // https://www.jianshu.com/p/fc8abc65bbb2
160 | name: "简书",
161 | test: /www\.jianshu\.com/,
162 | provider: JianShuProvider,
163 | },
164 | {
165 | // 测试地址: https://www.douban.com/doulist/240962/
166 | // 测试地址: https://www.douban.com/search?cat=1002&q=%E9%BB%91%E9%95%9C
167 | name: "豆瓣",
168 | test: /douban\.com/,
169 | provider: DouBanProvider,
170 | },
171 | {
172 | // 测试地址: https://getpocket.com/a/recommended/
173 | // 需要登陆
174 | name: "Pocket",
175 | test: /getpocket\.com/,
176 | provider: PocketProvider,
177 | },
178 | {
179 | // 测试地址: https://www.dogedoge.com/results?q=chrome
180 | name: "DogeDoge",
181 | test: /www\.dogedoge\.com/,
182 | provider: DogeDogeProvider,
183 | },
184 | {
185 | // 测试地址: https://51.ruyo.net/15053.html
186 | name: "Ruyo",
187 | test: /51\.ruyo\.net/,
188 | provider: RuyoProvider,
189 | },
190 | {
191 | // 测试地址: https://steamcommunity.com/sharedfiles/filedetails/?id=1311535531
192 | name: "Steam",
193 | test: /steamcommunity\.com/,
194 | provider: SteamProvider,
195 | },
196 | {
197 | // 测试地址: https://mijisou.com/?q=chrome&category_general=on&time_range=&language=zh-CN&pageno=1
198 | name: "秘迹",
199 | test: /mijisou\.com/,
200 | provider: MiJiProvider,
201 | },
202 | {
203 | // 测试地址: https://github.com/axetroy/anti-redirect/issues/350
204 | name: "CSDN",
205 | test: /blog\.csdn\.net/,
206 | provider: CSDNProvider,
207 | },
208 | {
209 | // 测试地址:https://my.oschina.net/chipo/blog/3067672
210 | name: "OS China",
211 | test: /oschina\.net/,
212 | provider: OSChinaProvider,
213 | },
214 | {
215 | // 测试地址: https://github.com/axetroy/anti-redirect/issues/350
216 | name: "印象笔记",
217 | test: /app\.yinxiang\.com/,
218 | provider: YinXiangProvider,
219 | },
220 | {
221 | // 测试地址: https://www.logonews.cn/2021073002420141.html
222 | name: "标志情报局",
223 | test: /www\.logonews\.cn/,
224 | provider: LogonewsProvider,
225 | },
226 | {
227 | // 测试地址: https://afdian.net/a/xiaofanEric
228 | name: "爱发电",
229 | test: /afdian\.net/,
230 | provider: AfDianNetProvider,
231 | },
232 | {
233 | // 测试地址: https://blog.51cto.com/u_11512826/2068421
234 | name: "51CTO博客",
235 | test: /blog\.51cto\.com/,
236 | provider: Blog51CTO,
237 | },
238 | {
239 | // 测试地址: https://xie.infoq.cn/link?target=https%3A%2F%2Fwww.finclip.com%2F%3Fchannel%3Dinfoqseo
240 | name: 'InfoQ',
241 | test: /infoq\.cn/,
242 | provider: InfoQProvider
243 | },
244 | {
245 | // 测试地址: https://gitee.com/Tencent/ncnn
246 | name: 'Gitee',
247 | test: /gitee.com/,
248 | provider: GiteeProvider
249 | },
250 | {
251 | // 测试地址: https://sspai.com/post/77499
252 | name: '少数派',
253 | test: /sspai\.com/,
254 | provider: SSPaiProvider
255 | }
256 | ])
257 | .bootstrap();
258 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "anti-redirect",
3 | "version": "2.21.7",
4 | "description": "去除重定向, 支持谷歌/百度/搜狗/360/知乎/贴吧/简书/豆瓣/微博...",
5 | "main": "./dist/anti-redirect.user.js",
6 | "scripts": {
7 | "test": "npm run build",
8 | "build": "cross-env NODE_ENV=production webpack --progress",
9 | "watch": "cross-env NODE_ENV=development webpack --progress --watch",
10 | "changelog": "npx conventional-changelog-cli -p angular -i CHANGELOG.md -s -r 0",
11 | "deploy": "npm run build && npx gh-pages -d ./dist",
12 | "lint": "rome check --apply-unsafe --formatter-enabled true --organize-imports-enabled true --linter-enabled true --verbose ./src",
13 | "format": "rome format --write ./src"
14 | },
15 | "author": "Axetroy",
16 | "license": "MIT",
17 | "keywords": [
18 | "greasy",
19 | "script",
20 | "javascript",
21 | "redirect"
22 | ],
23 | "devDependencies": {
24 | "@types/node": "18.16.10",
25 | "@types/webpack": "5.28.1",
26 | "cross-env": "7.0.3",
27 | "date-fns": "2.29.3",
28 | "rome": "^12.1.3",
29 | "ts-loader": "9.4.1",
30 | "ts-node": "10.9.1",
31 | "tsconfig-paths-webpack-plugin": "^4.1.0",
32 | "typescript": "^5.2.2",
33 | "webpack": "^5.88.2",
34 | "webpack-cli": "^5.1.4"
35 | },
36 | "dependencies": {
37 | "gm-http": "^0.2.1",
38 | "lodash.debounce": "^4.0.8",
39 | "lodash.throttle": "^4.1.1",
40 | "p-retry": "^6.0.0"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/rome.json:
--------------------------------------------------------------------------------
1 | {
2 | "linter": {
3 | "enabled": true,
4 | "rules": {
5 | "recommended": true
6 | }
7 | },
8 | "formatter": {
9 | "indentStyle": "space",
10 | "lineWidth": 120
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/app.ts:
--------------------------------------------------------------------------------
1 | import { IProvider, IProviderConstructor } from "./provider";
2 | import { Marker, debounceDecorator, getRedirect, isInView, throttleDecorator } from "./utils";
3 |
4 | type tester = () => boolean;
5 |
6 | interface IProviderConfig {
7 | name: string;
8 | test: RegExp | boolean | tester;
9 | provider: IProviderConstructor;
10 | }
11 |
12 | export interface IAppConfig {
13 | isDebug: boolean;
14 | }
15 |
16 | export class App {
17 | private config: IAppConfig;
18 | private provides: IProvider[] = [];
19 | constructor() {
20 | console.log(
21 | "%c Anti-Redirect %c Copyright \xa9 2015-%s %s",
22 | 'font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:64px;color:#00bbee;-webkit-text-fill-color:#00bbee;-webkit-text-stroke: 1px #00bbee;',
23 | "font-size:12px;color:#999999;",
24 | new Date().getFullYear(),
25 | "\n" + "Author @Axetroy",
26 | );
27 | console.log("[Anti Redirect]: 如果发现页面重定向未去除,欢迎反馈!");
28 | console.log(
29 | `%c[Anti Redirect]: 支付宝搜索 "%c511118132%c" 领取红包支持作者!`,
30 | "font-size: 12px;",
31 | "font-size: 16px;color: red",
32 | "font-size: 12px;",
33 | );
34 | }
35 | /**
36 | * A 标签是否匹配服务提供者
37 | * @param aElement
38 | * @param provider
39 | */
40 | private isMatchProvider(aElement: HTMLAnchorElement, provider: IProvider): boolean {
41 | if (aElement.getAttribute(Marker.RedirectStatusDone)) {
42 | return false;
43 | }
44 | if (provider.test instanceof RegExp && !provider.test.test(aElement.href)) {
45 | return false;
46 | }
47 | if (typeof provider.test === "function" && !provider.test(aElement)) {
48 | return false;
49 | }
50 | if (provider.test instanceof Boolean) {
51 | return provider.test as boolean;
52 | }
53 | return true;
54 | }
55 | /**
56 | * 当鼠标移动到 A 标签上时
57 | * @param event
58 | */
59 | @throttleDecorator(50)
60 | private onHover(event: Event) {
61 | const aElement: HTMLAnchorElement = event.target as HTMLAnchorElement;
62 | if (aElement.tagName !== "A") {
63 | return;
64 | }
65 | // trigger on hover handler
66 | for (const provider of this.provides) {
67 | if (this.isMatchProvider(aElement, provider)) {
68 | provider.resolve(aElement);
69 | }
70 | }
71 | }
72 | /**
73 | * 当页面滚动时
74 | */
75 | @debounceDecorator(300)
76 | private onScroll() {
77 | // 筛选所有在可视区域内的A标签
78 | const visibleElements: HTMLAnchorElement[] = [].slice
79 | .call(document.querySelectorAll("a[href]"))
80 | .filter((aElement: HTMLAnchorElement) => {
81 | return aElement.href.indexOf("http") > -1 && isInView(aElement) && getRedirect(aElement) <= 2;
82 | });
83 | // trigger scroll handler
84 | for (const provider of this.provides) {
85 | for (const aElement of visibleElements) {
86 | if (this.isMatchProvider(aElement, provider)) {
87 | provider.resolve(aElement);
88 | }
89 | }
90 | }
91 | }
92 | /**
93 | * 当页面准备就绪时,进行初始化动作
94 | */
95 | private async pageOnReady() {
96 | for (const provider of this.provides) {
97 | if (provider.onInit) {
98 | await provider.onInit();
99 | }
100 | // 如果页面处于初始的状态,没有滚动过,则出发一次onScroll事件
101 | if (window.scrollY <= 0) {
102 | this.onScroll();
103 | }
104 | }
105 | }
106 | /**
107 | * 设置配置
108 | * @param config
109 | */
110 | public setConfig(config: IAppConfig): this {
111 | this.config = config;
112 | return this;
113 | }
114 | /**
115 | * 注册服务提供者
116 | * @param providers
117 | */
118 | public registerProvider(providers: IProviderConfig[]): this {
119 | for (const provideConfig of providers) {
120 | // test 如果是 boolean
121 | if (provideConfig.test === false) {
122 | continue;
123 | }
124 | // test 如果是正则表达式
125 | if (provideConfig.test instanceof RegExp && !provideConfig.test.test(document.domain)) {
126 | continue;
127 | }
128 | // test 如果是一个function
129 | if (typeof provideConfig.test === "function" && provideConfig.test() === false) {
130 | continue;
131 | }
132 | const provider = new provideConfig.provider();
133 | provider.isDebug = this.config.isDebug;
134 | this.provides.push(provider);
135 | console.info(`[Anti-redirect]: 加载引擎 ${provideConfig.name}`);
136 | console.info(`当前页面: '${location.href}'`);
137 | }
138 | return this;
139 | }
140 | /**
141 | * 启动应用
142 | */
143 | public bootstrap() {
144 | addEventListener("scroll", this.onScroll.bind(this));
145 | addEventListener("mousemove", this.onHover.bind(this));
146 | addEventListener("DOMContentLoaded", this.pageOnReady.bind(this));
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/provider.ts:
--------------------------------------------------------------------------------
1 | export type IProviderConstructor = new () => IProvider;
2 |
3 | export type tester = (aElement: HTMLAnchorElement) => boolean;
4 |
5 | export interface IProvider {
6 | isDebug?: boolean;
7 | test: RegExp | tester | boolean | null;
8 | onInit?(): Promise;
9 | resolve(aElementList: HTMLAnchorElement): void;
10 | }
11 |
--------------------------------------------------------------------------------
/src/sites/51.ruyo.net.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class RuyoProvider implements IProvider {
5 | public test = /\/[^\?]*\?u=(.*)/;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | antiRedirect(aElement, new URL(aElement.href).searchParams.get("u"));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/sites/addons.mozilla.org.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect, matchLinkFromUrl } from "@/utils";
3 |
4 | export class MozillaProvider implements IProvider {
5 | public test = /outgoing\.prod\.mozaws\.net\/v\d\/\w+\/(.*)/;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | antiRedirect(aElement, matchLinkFromUrl(aElement, this.test));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/sites/afadian.net.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class AfDianNetProvider implements IProvider {
5 | public test = /afdian\.net\/link\?target=(.*)/;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | antiRedirect(aElement, new URL(aElement.href).searchParams.get("target"));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/sites/app.yinxiang.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class YinXiangProvider implements IProvider {
5 | public test = /^http:\/\//;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | // 编辑器
8 | if (aElement.hasAttribute("data-mce-href")) {
9 | if (!aElement.onclick) {
10 | antiRedirect(aElement, aElement.href, { force: true });
11 | aElement.onclick = (e) => {
12 | // 阻止事件冒泡, 因为上层元素绑定的click事件会重定向
13 | if (e.stopPropagation) {
14 | e.stopPropagation();
15 | }
16 | aElement.setAttribute("target", "_blank");
17 | window.top ? window.top.open(aElement.href) : window.open(aElement.href);
18 | };
19 | }
20 | }
21 | // 分享页面
22 | else if (/^https:\/\/app\.yinxiang\.com\/OutboundRedirect\.action\?dest=/.test(aElement.href)) {
23 | antiRedirect(aElement, new URL(aElement.href).searchParams.get("dest"));
24 | }
25 | }
26 | public async onInit(): Promise {
27 | const handler = (e) => {
28 | const dom = e.target as HTMLElement;
29 |
30 | const tagName = dom.tagName.toUpperCase();
31 |
32 | switch (tagName) {
33 | case "A": {
34 | this.resolve(dom as HTMLAnchorElement);
35 | break;
36 | }
37 | case "IFRAME": {
38 | if (dom.hasAttribute("anti-redirect-handled")) {
39 | return;
40 | }
41 | dom.setAttribute("anti-redirect-handled", "1");
42 | (dom as HTMLIFrameElement).contentWindow.document.addEventListener("mouseover", handler);
43 | break;
44 | }
45 | }
46 | };
47 |
48 | document.addEventListener("mouseover", handler);
49 | return this;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/sites/blog.51cto.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 |
3 | export class Blog51CTO implements IProvider {
4 | public test = true;
5 | private container: HTMLElement;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | this.container = document.querySelector(".article-detail");
8 | if (this.container?.contains(aElement)) {
9 | if (!aElement.onclick && aElement.href) {
10 | aElement.onclick = function antiRedirectOnClickFn(e) {
11 | e.stopPropagation();
12 | e.preventDefault();
13 | e.stopImmediatePropagation();
14 |
15 | const $a = document.createElement("a");
16 |
17 | $a.href = aElement.href;
18 | $a.target = aElement.target;
19 |
20 | $a.click();
21 | };
22 | }
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/sites/blog.csdn.net.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class CSDNProvider implements IProvider {
5 | public test = /^https?:\/\//;
6 | private container: HTMLElement;
7 | public resolve(aElement: HTMLAnchorElement) {
8 | this.container = document.querySelector("#content_views");
9 | if (this.container?.contains(aElement)) {
10 | if (!aElement.onclick && aElement.origin !== window.location.origin) {
11 | antiRedirect(aElement, aElement.href, { force: true });
12 | aElement.onclick = (e) => {
13 | // 阻止事件冒泡, 因为上层元素绑定的click事件会重定向
14 | if (e.stopPropagation) {
15 | e.stopPropagation();
16 | }
17 |
18 | aElement.setAttribute("target", "_blank");
19 | };
20 | }
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/sites/daily.zhihu.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class ZhihuDailyProvider implements IProvider {
5 | public test = /zhihu\.com\/\?target=(.*)/;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | antiRedirect(aElement, new URL(aElement.href).searchParams.get("target"));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/sites/docs.google.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class GoogleDocsProvider implements IProvider {
5 | public test = /www\.google\.com\/url\?q=(.*)/;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | antiRedirect(aElement, new URL(aElement.href).searchParams.get("q"));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/sites/getpocket.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class PocketProvider implements IProvider {
5 | public test = /getpocket\.com\/redirect\?url=(.*)/;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | antiRedirect(aElement, new URL(aElement.href).searchParams.get("url"));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/sites/gitee.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class GiteeProvider implements IProvider {
5 | public test = /gitee\.com\/link\?target=(.*)/;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | antiRedirect(aElement, new URL(aElement.href).searchParams.get("target"));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/sites/gmail.google.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 | export class GmailProvider implements IProvider {
4 | public test = true;
5 | private REDIRECT_PROPERTY = "data-saferedirecturl";
6 | public resolve(aElement: HTMLAnchorElement) {
7 | // 移除这个属性,那么 a 链接就不会跳转
8 | // FIXME: gmail 是多层 iframe 嵌套
9 | if (aElement.getAttribute(this.REDIRECT_PROPERTY)) {
10 | aElement.removeAttribute(this.REDIRECT_PROPERTY);
11 | antiRedirect(aElement, aElement.href);
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/sites/infoq.cn.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class InfoQProvider implements IProvider {
5 | public test = /infoq\.cn\/link\?target=(.*)/;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | antiRedirect(aElement, new URL(aElement.href).searchParams.get("target"));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/sites/juejin.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class JuejinProvider implements IProvider {
5 | public test = /link\.juejin\.(im|cn)\/\?target=(.*)/;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | const finalURL = new URL(aElement.href).searchParams.get("target");
8 | antiRedirect(aElement, finalURL);
9 |
10 | if (this.test.test(aElement.title)) {
11 | aElement.title = finalURL;
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/sites/mail.qq.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 |
3 | export class QQMailProvider implements IProvider {
4 | public test = true;
5 | private container: HTMLElement;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | this.container = document.querySelector("#contentDiv");
8 | if (this.container?.contains(aElement)) {
9 | if (aElement.onclick) {
10 | aElement.onclick = (e) => {
11 | // 阻止事件冒泡, 因为上层元素绑定的click事件会重定向
12 | if (e.stopPropagation) {
13 | e.stopPropagation();
14 | }
15 | };
16 | }
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/sites/mijisou.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class MiJiProvider implements IProvider {
5 | public test = /mijisou\.com\/url_proxy\?proxyurl=(.*)/;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | antiRedirect(aElement, new URL(aElement.href).searchParams.get("proxyurl"));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/sites/oschina.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class OSChinaProvider implements IProvider {
5 | public test = /oschina\.net\/action\/GoToLink\?url=(.*)/;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | antiRedirect(aElement, new URL(aElement.href).searchParams.get("url"));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/sites/play.google.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { Marker, antiRedirect } from "@/utils";
3 | export class GooglePlayProvider implements IProvider {
4 | public test(aElement: HTMLAnchorElement) {
5 | if (/google\.com\/url\?q=(.*)/.test(aElement.href)) {
6 | return true;
7 | } else if (/^\/store\/apps\/details/.test(location.pathname)) {
8 | return true;
9 | }
10 |
11 | return false;
12 | }
13 | public resolve(aElement: HTMLAnchorElement) {
14 | antiRedirect(aElement, new URL(aElement.href).searchParams.get("q"));
15 |
16 | // 移除开发者栏目下的重定向
17 | const eles = [].slice.call(document.querySelectorAll("a.hrTbp"));
18 |
19 | for (const ele of eles) {
20 | if (!ele.href) {
21 | continue;
22 | }
23 | if (ele.getAttribute(Marker.RedirectStatusDone)) {
24 | continue;
25 | }
26 |
27 | ele.setAttribute(Marker.RedirectStatusDone, ele.href);
28 | ele.setAttribute("target", "_blank");
29 |
30 | ele.addEventListener(
31 | "click",
32 | (event) => {
33 | event.stopPropagation();
34 | },
35 | true,
36 | );
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/sites/sspai.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class SSPaiProvider implements IProvider {
5 | public test = /sspai\.com\/link\?target=(.*)/;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | antiRedirect(aElement, new URL(aElement.href).searchParams.get("target"));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/sites/steamcommunity.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class SteamProvider implements IProvider {
5 | public test = /steamcommunity\.com\/linkfilter\/\?url=(.*)/;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | antiRedirect(aElement, new URL(aElement.href).searchParams.get("url"));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/sites/tieba.baidu.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 | export class TiebaProvider implements IProvider {
4 | public test = /jump\d*\.bdimg\.com/;
5 | public resolve(aElement: HTMLAnchorElement) {
6 | if (!this.test.test(aElement.href)) {
7 | return;
8 | }
9 | let url = "";
10 | const text: string = aElement.innerText || aElement.textContent || "";
11 | try {
12 | if (/https?:\/\//.test(text)) {
13 | url = decodeURIComponent(text);
14 | }
15 | } catch (e) {
16 | url = /https?:\/\//.test(text) ? text : "";
17 | }
18 | if (url) {
19 | antiRedirect(aElement, url);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/sites/twitter.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class TwitterProvider implements IProvider {
5 | public test = /t\.co\/\w+/;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | if (!this.test.test(aElement.href)) {
8 | return;
9 | }
10 |
11 | if (/https?:\/\//.test(aElement.title)) {
12 | const url: string = decodeURIComponent(aElement.title);
13 |
14 | antiRedirect(aElement, url);
15 | return;
16 | }
17 |
18 | const innerText = aElement.innerText.replace(/…$/, "");
19 |
20 | if (/https?:\/\//.test(innerText)) {
21 | antiRedirect(aElement, innerText);
22 | return;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/sites/video.baidu.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 | import http from "gm-http";
4 |
5 | export class BaiduVideoProvider implements IProvider {
6 | public test = /v\.baidu\.com\/link\?url=/;
7 | public resolve(aElement: HTMLAnchorElement) {
8 | http
9 | .request({
10 | url: aElement.href,
11 | method: "GET",
12 | anonymous: true,
13 | })
14 | .then((res: Response$) => {
15 | if (res.finalUrl) {
16 | antiRedirect(aElement, res.finalUrl);
17 | }
18 | })
19 | .catch((err) => {
20 | console.error(err);
21 | });
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/sites/weibo.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class WeboProvider implements IProvider {
5 | public test = /t\.cn\/\w+/;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | if (!(this.test.test(aElement.href) && /^https?:\/\//.test(aElement.title))) {
8 | return;
9 | }
10 |
11 | const url: string = decodeURIComponent(aElement.title);
12 |
13 | if (url) {
14 | aElement.href = url;
15 | antiRedirect(aElement, url);
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/sites/www.baidu.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect, decreaseRedirect, getRedirect, increaseRedirect } from "@/utils";
3 | import http from "gm-http";
4 | import pRetry from "p-retry";
5 |
6 | export class BaiduProvider implements IProvider {
7 | public test = /www\.baidu\.com\/link\?url=/;
8 | public resolve(aElement: HTMLAnchorElement) {
9 | if (getRedirect(aElement) <= 2 && this.test.test(aElement.href)) {
10 | increaseRedirect(aElement);
11 |
12 | pRetry(() => this.handlerOneElement(aElement), { retries: 3 })
13 | .then((res) => {
14 | decreaseRedirect(aElement);
15 | })
16 | .catch((err) => {
17 | decreaseRedirect(aElement);
18 | });
19 | }
20 | }
21 |
22 | private async handlerOneElement(aElement: HTMLAnchorElement): Promise {
23 | try {
24 | const res = await http.request({
25 | url: aElement.href,
26 | method: "GET",
27 | anonymous: true,
28 | });
29 |
30 | if (res.finalUrl) {
31 | antiRedirect(aElement, res.finalUrl);
32 | }
33 |
34 | return res;
35 | } catch (err) {
36 | console.error(err);
37 | return Promise.reject(new Error(`[http]: ${aElement.href} fail`));
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/sites/www.dogedoge.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect, decreaseRedirect, getRedirect, increaseRedirect } from "@/utils";
3 | import http from "gm-http";
4 |
5 | export class DogeDogeProvider implements IProvider {
6 | public test = /www\.dogedoge\.com\/rd\/.{1,}/;
7 | public resolve(aElement: HTMLAnchorElement) {
8 | if (getRedirect(aElement) <= 2 && this.test.test(aElement.href)) {
9 | increaseRedirect(aElement);
10 | this.handlerOneElement(aElement)
11 | .then((res) => {
12 | decreaseRedirect(aElement);
13 | })
14 | .catch((err) => {
15 | decreaseRedirect(aElement);
16 | });
17 | }
18 | }
19 |
20 | private async handlerOneElement(aElement: HTMLAnchorElement): Promise {
21 | try {
22 | const res: Response$ = await http.request({
23 | url: aElement.href,
24 | method: "GET",
25 | anonymous: true,
26 | });
27 | if (res.finalUrl) {
28 | antiRedirect(aElement, res.finalUrl);
29 | }
30 | return res;
31 | } catch (err) {
32 | console.error(err);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/sites/www.douban.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class DouBanProvider implements IProvider {
5 | public test = /douban\.com\/link2\/?\?url=(.*)/;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | antiRedirect(aElement, new URL(aElement.href).searchParams.get("url"));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/sites/www.google.com.ts:
--------------------------------------------------------------------------------
1 | import { antiRedirect } from "../utils";
2 | import { IProvider } from "@/provider";
3 |
4 | export class GoogleProvider implements IProvider {
5 | public test = true;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | const traceProperties = ["ping", "data-jsarwt", "data-usg", "data-ved"];
8 |
9 | // 移除追踪
10 | for (const property of traceProperties) {
11 | if (aElement.getAttribute(property)) {
12 | aElement.removeAttribute(property);
13 | }
14 | }
15 |
16 | // 移除多余的事件
17 | if (aElement.getAttribute("onmousedown")) {
18 | aElement.removeAttribute("onmousedown");
19 | }
20 |
21 | // 尝试去除重定向
22 | if (aElement.getAttribute("data-href")) {
23 | const realUrl: string = aElement.getAttribute("data-href");
24 | antiRedirect(aElement, realUrl);
25 | }
26 |
27 | const url = new URL(aElement.href);
28 |
29 | if (url.searchParams.get("url")) {
30 | antiRedirect(aElement, url.searchParams.get("url"));
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/sites/www.jianshu.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class JianShuProvider implements IProvider {
5 | public test = (aElement: HTMLAnchorElement) => {
6 | const isLink1 = /links\.jianshu\.com\/go/.test(aElement.href);
7 | const isLink2 = /link\.jianshu\.com(\/)?\?t=/.test(aElement.href);
8 | const isLink3 = /jianshu\.com\/go-wild\/?\?(.*)url=/.test(aElement.href);
9 |
10 | if (isLink1 || isLink2 || isLink3) {
11 | return true;
12 | }
13 |
14 | return false;
15 | };
16 | public resolve(aElement: HTMLAnchorElement) {
17 | const search = new URL(aElement.href).searchParams;
18 | antiRedirect(aElement, search.get("to") || search.get("t") || search.get("url"));
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/sites/www.logonews.cn.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class LogonewsProvider implements IProvider {
5 | public test = /link\.logonews\.cn\/\?url=(.*)/;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | antiRedirect(aElement, new URL(aElement.href).searchParams.get("url"));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/sites/www.so.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class SoProvider implements IProvider {
5 | public test = /so\.com\/link\?(.*)/;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | const url = aElement.getAttribute("data-mdurl") || aElement.getAttribute("e-landurl");
8 |
9 | if (url) {
10 | antiRedirect(aElement, url);
11 | }
12 |
13 | // remove track
14 | aElement.removeAttribute("e_href");
15 | aElement.removeAttribute("data-res");
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/sites/www.sogou.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect, decreaseRedirect, getRedirect, getText, increaseRedirect, queryParser } from "@/utils";
3 | import http from "gm-http";
4 |
5 | export class SoGouProvider implements IProvider {
6 | public test = /www\.sogou\.com\/link\?url=/;
7 | public async resolve(aElement: HTMLAnchorElement) {
8 | try {
9 | if (getRedirect(aElement) <= 2 && this.test.test(aElement.href)) {
10 | increaseRedirect(aElement);
11 | const res = await http.request({
12 | url: aElement.href,
13 | method: "GET",
14 | anonymous: true,
15 | });
16 | decreaseRedirect(aElement);
17 | const finalUrl = res.finalUrl;
18 | if (finalUrl && !this.test.test(finalUrl)) {
19 | antiRedirect(aElement, res.finalUrl);
20 | } else {
21 | const matcher = res.responseText.match(/URL=['"]([^'"]+)['"]/);
22 | if (matcher?.[1]) {
23 | antiRedirect(aElement, res.finalUrl);
24 | }
25 | }
26 | }
27 | } catch (err) {
28 | decreaseRedirect(aElement);
29 | console.error(err);
30 | }
31 | }
32 | private parsePage(res: Response$): void {
33 | const responseText: string = res.responseText.replace(/(src=[^>]*|link=[^>])/g, "");
34 | const html: HTMLHtmlElement = document.createElement("html");
35 | html.innerHTML = responseText;
36 |
37 | // let selector = '#main .results div.vrwrap>h3';
38 | // let selector = '#main .results h3>a';
39 | const selector = '#main .results a[href*="www.sogou.com/link?url="]';
40 | const remotes = [].slice.call(html.querySelectorAll("#main .results a[href]"));
41 | const locals = [].slice.call(document.querySelectorAll(selector));
42 |
43 | for (const localEle of locals) {
44 | for (const remoteEle of remotes) {
45 | let localText = getText(localEle);
46 | let remoteText = getText(remoteEle);
47 |
48 | // 通用按钮,例如【点击下载】
49 | if (localEle.classList.contains("str-public-btn")) {
50 | localText = getText(localEle.parentNode);
51 | remoteText = getText(remoteEle.parentNode);
52 | } else if (localEle.classList.contains("str_img")) {
53 | // 图片
54 | localText = getText(localEle.parentNode.parentNode);
55 | remoteText = getText(remoteEle.parentNode.parentNode);
56 | }
57 |
58 | if (!localText || localText !== remoteText) {
59 | return;
60 | }
61 | antiRedirect(localEle, remoteEle.href);
62 | }
63 | }
64 | }
65 | public async onInit() {
66 | if (!/www\.sogou\.com\/web/.test(window.top.location.href)) {
67 | return;
68 | }
69 | const query = queryParser(window.top.location.search);
70 |
71 | // 搜索使用http搜索,得到的是直接链接
72 | const url: string = `${location.protocol.replace(/:$/, "").replace("s", "")}://${
73 | location.host + location.pathname + query
74 | }`;
75 |
76 | http
77 | .get(url)
78 | .then((res: Response$) => {
79 | this.parsePage(res);
80 | })
81 | .catch((err) => {
82 | console.error(err);
83 | });
84 | return this;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/sites/www.youtube.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class YoutubeProvider implements IProvider {
5 | public test = /www\.youtube\.com\/redirect\?.{1,}/;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | antiRedirect(aElement, new URL(aElement.href).searchParams.get("q"));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/sites/www.zhihu.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class ZhihuProvider implements IProvider {
5 | public test = /zhihu\.com\/\?target=(.*)/;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | antiRedirect(aElement, new URL(aElement.href).searchParams.get("target"));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/sites/xueshu.baidu.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class BaiduXueshuProvider implements IProvider {
5 | public test = /xueshu\.baidu\.com\/s?\?(.*)/; // 此处无用
6 | public resolve(aElement: HTMLAnchorElement) {
7 | const realHref: string = aElement.getAttribute("data-link") || aElement.getAttribute("data-url");
8 | if (realHref) {
9 | antiRedirect(aElement, decodeURIComponent(realHref));
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/sites/zhuanlan.zhihu.com.ts:
--------------------------------------------------------------------------------
1 | import { IProvider } from "@/provider";
2 | import { antiRedirect } from "@/utils";
3 |
4 | export class ZhihuZhuanlanProvider implements IProvider {
5 | public test = /link\.zhihu\.com\/\?target=(.*)/;
6 | public resolve(aElement: HTMLAnchorElement) {
7 | antiRedirect(aElement, new URL(aElement.href).searchParams.get("target"));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import * as debounce from "lodash.debounce";
2 | import * as throttle from "lodash.throttle";
3 |
4 | export enum Marker {
5 | RedirectCount = "redirect-count",
6 | RedirectStatusDone = "anti-redirect-origin-href",
7 | }
8 |
9 | // rome-ignore lint/suspicious/noExplicitAny: allow return anything
10 | type DecoratorMethodFunction = (originMethod: Function, context: ClassMemberDecoratorContext) => any;
11 |
12 | /**
13 | * 根据url上的路径匹配,去除重定向
14 | * @param {HTMLAnchorElement} aElement
15 | * @param {RegExp} tester
16 | * @returns {boolean}
17 | */
18 | export function matchLinkFromUrl(aElement: HTMLAnchorElement, tester: RegExp): string {
19 | const matcher: string[] = tester.exec(aElement.href);
20 | if (!(matcher?.length && matcher[1])) {
21 | return "";
22 | }
23 |
24 | let url = "";
25 | try {
26 | url = decodeURIComponent(matcher[1]);
27 | } catch (e) {
28 | url = /https?:\/\//.test(matcher[1]) ? matcher[1] : "";
29 | }
30 | return url;
31 | }
32 |
33 | class Query {
34 | private object: Record = {};
35 |
36 | constructor(public queryStr: string) {
37 | this.object = this.toObject(queryStr.replace(/^\?+/, ""));
38 | }
39 |
40 | private toObject(queryStr: string) {
41 | const obj: Record = {};
42 | queryStr.split("&").forEach((item) => {
43 | const arr: string[] = item.split("=") || [];
44 | let key: string = arr[0] || "";
45 | let value: string = arr[1] || "";
46 | try {
47 | key = decodeURIComponent(arr[0] || "");
48 | value = decodeURIComponent(arr[1] || "");
49 | } catch (err) {
50 | //
51 | }
52 | if (key) {
53 | obj[key] = value;
54 | }
55 | });
56 | return obj;
57 | }
58 |
59 | public toString(): string {
60 | const arr: string[] = [];
61 | for (const key in this.object) {
62 | if (Object.prototype.hasOwnProperty.call(this.object, key)) {
63 | const value = this.object[key];
64 | arr.push(`${key}=${value}`);
65 | }
66 | }
67 | return arr.length ? `?${arr.join("&")}` : "";
68 | }
69 | }
70 |
71 | export function queryParser(queryString: string): Query {
72 | return new Query(queryString);
73 | }
74 |
75 | export function getText(htmlElement: HTMLElement): string {
76 | return (htmlElement.innerText || htmlElement.textContent).trim();
77 | }
78 |
79 | export function throttleDecorator(wait: number, options = {}): DecoratorMethodFunction {
80 | return (originMethod, context: ClassMemberDecoratorContext) => {
81 | return throttle(originMethod, wait, options);
82 | };
83 | }
84 |
85 | export function debounceDecorator(wait: number, options = {}): DecoratorMethodFunction {
86 | return (originMethod, context: ClassMemberDecoratorContext) => {
87 | return debounce(originMethod, wait, options);
88 | };
89 | }
90 |
91 | export function isInView(element: HTMLElement): boolean {
92 | const rect = element.getBoundingClientRect();
93 |
94 | const vWidth = window.innerWidth || document.documentElement.clientWidth;
95 | const vHeight = window.innerHeight || document.documentElement.clientHeight;
96 |
97 | const efp = (x, y) => {
98 | return document.elementFromPoint(x, y);
99 | };
100 |
101 | // Return false if it's not in the viewport
102 | if (rect.right < 0 || rect.bottom < 0 || rect.left > vWidth || rect.top > vHeight) {
103 | return false;
104 | }
105 |
106 | // Return true if any of its four corners are visible
107 | return (
108 | element.contains(efp(rect.left, rect.top)) ||
109 | element.contains(efp(rect.right, rect.top)) ||
110 | element.contains(efp(rect.right, rect.bottom)) ||
111 | element.contains(efp(rect.left, rect.bottom))
112 | );
113 | }
114 |
115 | export function getRedirect(aElement: HTMLAnchorElement): number {
116 | return +(aElement.getAttribute(Marker.RedirectCount) || 0);
117 | }
118 |
119 | export function increaseRedirect(aElement: HTMLAnchorElement): void {
120 | const num: number = getRedirect(aElement);
121 | aElement.setAttribute(Marker.RedirectCount, `${num}${1}`);
122 | }
123 |
124 | export function decreaseRedirect(aElement: HTMLAnchorElement): void {
125 | const num: number = getRedirect(aElement);
126 | if (num > 0) {
127 | aElement.setAttribute(Marker.RedirectCount, `${num - 1}`);
128 | }
129 | }
130 |
131 | interface IAntiRedirectOption {
132 | debug?: boolean;
133 | force?: boolean;
134 | }
135 |
136 | /**
137 | * 去除重定向
138 | * @param aElement A标签元素
139 | * @param realUrl 真实的地址
140 | * @param options
141 | */
142 | export function antiRedirect(aElement: HTMLAnchorElement, realUrl: string, options: IAntiRedirectOption = {}) {
143 | options.debug = typeof options.debug === "undefined" ? process.env.NODE_ENV !== "production" : options.debug;
144 |
145 | options.force = options.force;
146 |
147 | if (!options.force && (!realUrl || aElement.href === realUrl)) {
148 | return;
149 | }
150 | if (options.debug) {
151 | aElement.style.backgroundColor = "green";
152 | }
153 | aElement.setAttribute(Marker.RedirectStatusDone, aElement.href);
154 | aElement.href = realUrl;
155 | }
156 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "module": "none",
5 | "target": "ES2015",
6 | "lib": ["es6", "dom"],
7 | "moduleResolution": "node",
8 | "sourceMap": true,
9 | "typeRoots": ["node_modules/@types"],
10 | "paths": {
11 | "@/*": ["src/*"]
12 | }
13 | },
14 | "exclude": ["node_modules"]
15 | }
16 |
--------------------------------------------------------------------------------
/webpack.config.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by axetroy on 16-9-15.
3 | */
4 |
5 | import { format } from "date-fns";
6 | import * as path from "path";
7 | import * as webpack from "webpack";
8 | const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");
9 | const pkg = require("./package.json");
10 |
11 | // webpack.config.js
12 | const webpackConfig: webpack.Configuration = {
13 | entry: {
14 | "anti-redirect": path.join(__dirname, "index.ts"),
15 | },
16 | output: {
17 | path: path.join(__dirname, "/dist"),
18 | filename: "[name].user.js",
19 | },
20 | resolve: {
21 | modules: ["node_modules"],
22 | extensions: [".js", ".ts"],
23 | plugins: [new TsconfigPathsPlugin({ configFile: "./tsconfig.json" })],
24 | },
25 | module: {
26 | rules: [{ test: /\.tsx?$/, loader: "ts-loader" }],
27 | },
28 | mode: "none",
29 | plugins: [
30 | new webpack.DefinePlugin(
31 | (() => {
32 | const result = { "process.env.NODE_ENV": '"development"' };
33 | for (const key in process.env) {
34 | if (process.env.hasOwnProperty(key)) {
35 | result[`process.env.${key}`] = JSON.stringify(process.env[key]);
36 | }
37 | }
38 | return result;
39 | })()
40 | ),
41 | new webpack.LoaderOptionsPlugin({
42 | minimize: true,
43 | debug: false,
44 | }),
45 | new webpack.BannerPlugin({
46 | banner: `// ==UserScript==
47 | // @name ${pkg.name}
48 | // @author ${pkg.author}
49 | // @description ${pkg.description}
50 | // @version ${pkg.version}
51 | // @update ${format(new Date(), "yyyy-MM-dd HH:mm:ss")}
52 | // @grant GM_xmlhttpRequest
53 | // @match *://www.baidu.com/*
54 | // @match *://tieba.baidu.com/*
55 | // @match *://v.baidu.com/*
56 | // @match *://xueshu.baidu.com/*
57 | // @include *://www.google*
58 | // @match *://www.google.com/*
59 | // @match *://docs.google.com/*
60 | // @match *://mail.google.com/*
61 | // @match *://play.google.com/*
62 | // @match *://www.youtube.com/*
63 | // @match *://encrypted.google.com/*
64 | // @match *://www.so.com/*
65 | // @match *://www.zhihu.com/*
66 | // @match *://daily.zhihu.com/*
67 | // @match *://zhuanlan.zhihu.com/*
68 | // @match *://weibo.com/*
69 | // @match *://twitter.com/*
70 | // @match *://www.sogou.com/*
71 | // @match *://juejin.im/*
72 | // @match *://juejin.cn/*
73 | // @match *://mail.qq.com/*
74 | // @match *://addons.mozilla.org/*
75 | // @match *://www.jianshu.com/*
76 | // @match *://www.douban.com/*
77 | // @match *://getpocket.com/*
78 | // @match *://www.dogedoge.com/*
79 | // @match *://51.ruyo.net/*
80 | // @match *://steamcommunity.com/*
81 | // @match *://mijisou.com/*
82 | // @match *://blog.csdn.net/*
83 | // @match *://*.blog.csdn.net/*
84 | // @match *://*.oschina.net/*
85 | // @match *://app.yinxiang.com/*
86 | // @match *://www.logonews.cn/*
87 | // @match *://afdian.net/*
88 | // @match *://blog.51cto.com/*
89 | // @match *://xie.infoq.cn/*
90 | // @match *://gitee.com/*
91 | // @match *://sspai.com/*
92 | // @connect www.baidu.com
93 | // @connect *
94 | // @compatible chrome 完美运行
95 | // @compatible firefox 完美运行
96 | // @supportURL https://github.com/axetroy/anti-redirect/issues/new/choose
97 | // @homepage https://github.com/axetroy/anti-redirect
98 | // @run-at document-start
99 | // @contributionURL troy450409405@gmail.com|alipay.com
100 | // @downloadURL https://github.com/axetroy/anti-redirect/raw/gh-pages/anti-redirect.user.js
101 | // @updateURL https://github.com/axetroy/anti-redirect/raw/gh-pages/anti-redirect.user.js
102 | // @namespace https://greasyfork.org/zh-CN/users/3400-axetroy
103 | // @license Anti 996 License; https://github.com/axetroy/anti-redirect/blob/master/LICENSE
104 | // ==/UserScript==
105 |
106 | // Github源码: https://github.com/axetroy/anti-redirect
107 |
108 | // 如果这能帮助到你,欢迎在 Github 上点击 star 和 follow.
109 |
110 | // 或者在支付宝搜索 " 511118132 " 领取红包
111 |
112 | // 你的支持就是我更新的动力
113 |
114 | `,
115 | entryOnly: true,
116 | raw: true,
117 | }),
118 | ],
119 | devtool: 'inline-source-map',
120 | };
121 |
122 | module.exports = webpackConfig;
123 |
--------------------------------------------------------------------------------