Compare commits
354 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ccddaf4386 | ||
|
|
0f0ba46989 | ||
|
|
f83db38a6b | ||
|
|
ba5582ab4c | ||
|
|
fb4e1d9b31 | ||
|
|
2dea6e382b | ||
|
|
fa8eada97b | ||
|
|
c102f66234 | ||
|
|
68a50fdb4a | ||
|
|
aac83acb8f | ||
|
|
4be4d5e9ef | ||
|
|
1f925b307f | ||
|
|
49afe032d6 | ||
|
|
f0f054c8cd | ||
|
|
699e9ca31a | ||
|
|
76cace4ff6 | ||
|
|
9f3218ab7f | ||
|
|
8a61f518f5 | ||
|
|
e9d5023263 | ||
|
|
05b1e21bba | ||
|
|
4a496fdab3 | ||
|
|
8ae3a6534d | ||
|
|
3269376c94 | ||
|
|
7a92de4d62 | ||
|
|
5fbc1e3fb3 | ||
|
|
d73c3154f9 | ||
|
|
b3fbed4a94 | ||
|
|
71fe3bde51 | ||
|
|
c739f58cc5 | ||
|
|
2c50c72fcb | ||
|
|
8d16c549fc | ||
|
|
d74d6d8775 | ||
|
|
853e6c09e1 | ||
|
|
1f21ac9a7e | ||
|
|
28fd94eff5 | ||
|
|
7460935acc | ||
|
|
109ae6f319 | ||
|
|
6b3acf5787 | ||
|
|
b1ecbd4f9a | ||
|
|
4d091e4dd9 | ||
|
|
ae6559c850 | ||
|
|
5cfaebceab | ||
|
|
88f55cbab9 | ||
|
|
1204336612 | ||
|
|
49a65a5f11 | ||
|
|
7e5b836a49 | ||
|
|
18f80400ef | ||
|
|
eca4b41da4 | ||
|
|
a24a533165 | ||
|
|
d2b575fe85 | ||
|
|
cb0aa2049e | ||
|
|
981b9e4859 | ||
|
|
eeee1b6811 | ||
|
|
e6852d0c9d | ||
|
|
23ea08f981 | ||
|
|
580a3d7fd5 | ||
|
|
6eabb60c28 | ||
|
|
7fd432ffee | ||
|
|
351143e59d | ||
|
|
725e8ae8c9 | ||
|
|
1e9de46fc5 | ||
|
|
543ab5c8bd | ||
|
|
972cd02874 | ||
|
|
9c37cb9df3 | ||
|
|
2a8a6b6fa7 | ||
|
|
e4b4c747eb | ||
|
|
eabd33e6b2 | ||
|
|
233af7181f | ||
|
|
62fb19f0c5 | ||
|
|
54178ddcf1 | ||
|
|
92a65d8308 | ||
|
|
eaaa7dabf0 | ||
|
|
e797c8203f | ||
|
|
9ecf50c153 | ||
|
|
c02b7eecc9 | ||
|
|
6af8b6b25d | ||
|
|
7081aa77cc | ||
|
|
5e48edb030 | ||
|
|
5f0f677f59 | ||
|
|
7f0c3e3e29 | ||
|
|
c03f6ca9ba | ||
|
|
66f3ba7d06 | ||
|
|
511ba90378 | ||
|
|
01806d20dd | ||
|
|
049278c458 | ||
|
|
0aaa2d44a0 | ||
|
|
3f139a593e | ||
|
|
bee6fef69c | ||
|
|
f283a8a4c6 | ||
|
|
0d2433d850 | ||
|
|
a6b01bd8d5 | ||
|
|
8f2279b7e2 | ||
|
|
d37d26708d | ||
|
|
6234d7c49a | ||
|
|
f9a7bba32a | ||
|
|
068734da16 | ||
|
|
09a4108bcc | ||
|
|
a7f08ec7a7 | ||
|
|
f075fcce36 | ||
|
|
0bb96169e5 | ||
|
|
34320b2385 | ||
|
|
aeed9f0e57 | ||
|
|
c04a4c41e4 | ||
|
|
3b28ead0e4 | ||
|
|
d0ea5d09b1 | ||
|
|
a119973ec0 | ||
|
|
b8b91ac17e | ||
|
|
34f2c7e123 | ||
|
|
172f1e0209 | ||
|
|
1d5ed5d0cb | ||
|
|
1e150f26f1 | ||
|
|
03e735a44b | ||
|
|
011c1faad4 | ||
|
|
2325a8abdf | ||
|
|
201df489db | ||
|
|
2d680c5fc7 | ||
|
|
e77b7c0319 | ||
|
|
2283da7421 | ||
|
|
4a28f7f395 | ||
|
|
9a38fb62d0 | ||
|
|
fefd5dc60c | ||
|
|
89aa1ead33 | ||
|
|
a5f2fb53ed | ||
|
|
bd26f1011d | ||
|
|
cfaf156e03 | ||
|
|
75cf50ce0c | ||
|
|
e2b6bbc347 | ||
|
|
cd5160d1ea | ||
|
|
040827aa27 | ||
|
|
d00317a645 | ||
|
|
60317b4444 | ||
|
|
a0bb0f30f2 | ||
|
|
cc6f7bf958 | ||
|
|
583e0b00d8 | ||
|
|
c27f2fced9 | ||
|
|
8baa141339 | ||
|
|
2eceb43ac3 | ||
|
|
8ce9eb256b | ||
|
|
bf2cfb7da0 | ||
|
|
5dfaa19cca | ||
|
|
3404666b8e | ||
|
|
b64ab95658 | ||
|
|
f19d165ece | ||
|
|
602919dc18 | ||
|
|
722db9f6d1 | ||
|
|
970867f3c7 | ||
|
|
e22d8d3cd0 | ||
|
|
cfe6dd59c8 | ||
|
|
b46e257545 | ||
|
|
3e9a82a657 | ||
|
|
4cf687f436 | ||
|
|
e88cf4ac5b | ||
|
|
898349d22e | ||
|
|
9ea188cb6d | ||
|
|
03d4c6160a | ||
|
|
cf4433cdd6 | ||
|
|
ae80f25c67 | ||
|
|
5b2c294c61 | ||
|
|
a1595ab0c5 | ||
|
|
0be1e92ac5 | ||
|
|
cf3e5dca2f | ||
|
|
902f612d31 | ||
|
|
45475f14b7 | ||
|
|
30f396aefe | ||
|
|
7cf7117615 | ||
|
|
062305d126 | ||
|
|
f451aea643 | ||
|
|
64342b2846 | ||
|
|
d96b6e769a | ||
|
|
539de62eee | ||
|
|
d514106e65 | ||
|
|
7f9c8a4dea | ||
|
|
31014e571d | ||
|
|
f5aa68a8dc | ||
|
|
63bbbd549e | ||
|
|
5e40ed426b | ||
|
|
aebf3a9e90 | ||
|
|
8a3d7c305b | ||
|
|
2d580eaea2 | ||
|
|
d7ab695372 | ||
|
|
0c250dfd17 | ||
|
|
52b12c7057 | ||
|
|
2166753569 | ||
|
|
46e418dfd0 | ||
|
|
673e0ce3e6 | ||
|
|
6203249782 | ||
|
|
05e708ed21 | ||
|
|
c4faa102cf | ||
|
|
4a1b32278c | ||
|
|
584ead387e | ||
|
|
efdc62ba68 | ||
|
|
67618587e4 | ||
|
|
a2c045a841 | ||
|
|
9b35c87cb4 | ||
|
|
e6c6e66e4b | ||
|
|
f1d36a2148 | ||
|
|
41906d0570 | ||
|
|
dc7d1c0f40 | ||
|
|
9768bed637 | ||
|
|
fa202a0405 | ||
|
|
b8bcd09134 | ||
|
|
ef8dc033cc | ||
|
|
03a54bcca0 | ||
|
|
f4f93d8955 | ||
|
|
07cac792f0 | ||
|
|
50edad9e25 | ||
|
|
69edf12620 | ||
|
|
0b448e2b7e | ||
|
|
d7b95136dd | ||
|
|
80d74111c4 | ||
|
|
223d7c4b56 | ||
|
|
de3b4ca6d3 | ||
|
|
423a765e5f | ||
|
|
ca74c31b96 | ||
|
|
9366240d62 | ||
|
|
1fa2cd10b1 | ||
|
|
3210c8cfcc | ||
|
|
d7e1e8fc8f | ||
|
|
a81f0564f1 | ||
|
|
3b20630e9e | ||
|
|
7321b76bb5 | ||
|
|
8016b0531c | ||
|
|
c7c93ea22e | ||
|
|
9aef79831c | ||
|
|
43f2e98459 | ||
|
|
5627c0115a | ||
|
|
f252b26088 | ||
|
|
12e6619ab9 | ||
|
|
a0bad1acf0 | ||
|
|
01d65972fd | ||
|
|
b1334fc06a | ||
|
|
918c73b5dc | ||
|
|
28f04a4eb9 | ||
|
|
26328e66c4 | ||
|
|
1d85837e79 | ||
|
|
ba8e2f1625 | ||
|
|
6b809138e5 | ||
|
|
6f4e871c7c | ||
|
|
fe2c9de80b | ||
|
|
6c0a589395 | ||
|
|
42a128fd6b | ||
|
|
a891f2017a | ||
|
|
66fdfb1908 | ||
|
|
aa2874fdcd | ||
|
|
120dd264c2 | ||
|
|
455f98fafd | ||
|
|
2c1041ebed | ||
|
|
0af3ff5cd8 | ||
|
|
6faa7b2c2a | ||
|
|
873b6996ca | ||
|
|
84832395bd | ||
|
|
96a7400be8 | ||
|
|
ba565978c6 | ||
|
|
b53032cc92 | ||
|
|
e96eacd6f8 | ||
|
|
4cac51673a | ||
|
|
be75e5b65c | ||
|
|
c7ece6620c | ||
|
|
7e9da4b82a | ||
|
|
c657c92b27 | ||
|
|
d43163703e | ||
|
|
1150450718 | ||
|
|
df6483afa8 | ||
|
|
29de2eadf8 | ||
|
|
318689f163 | ||
|
|
6b9563b858 | ||
|
|
2c7491449c | ||
|
|
f698bc4b92 | ||
|
|
c18a10dbd5 | ||
|
|
69e7803c40 | ||
|
|
b2a5afe562 | ||
|
|
45fda5f498 | ||
|
|
1459d5efec | ||
|
|
b7375e5e7d | ||
|
|
4a7f88a0da | ||
|
|
a0ec759dd7 | ||
|
|
fe1e4c0e6f | ||
|
|
62770ba50c | ||
|
|
11bdece9e2 | ||
|
|
2c5df879c0 | ||
|
|
862de2e49e | ||
|
|
26e3fd435d | ||
|
|
e279fc6e38 | ||
|
|
acff304a35 | ||
|
|
9fe39fc274 | ||
|
|
9eccace3bc | ||
|
|
cf410fce61 | ||
|
|
0ffb173bb5 | ||
|
|
edd6425fe5 | ||
|
|
16b6c3f20a | ||
|
|
e3750f4565 | ||
|
|
d3face9621 | ||
|
|
aeeb9e1a43 | ||
|
|
09c6bab04a | ||
|
|
2e73aa8833 | ||
|
|
03d88f4856 | ||
|
|
3cab2b2379 | ||
|
|
87faeae8f7 | ||
|
|
19f0bb7c36 | ||
|
|
01305f98b5 | ||
|
|
a3ae3b29f9 | ||
|
|
053c9805d4 | ||
|
|
be51aab0e0 | ||
|
|
426fa74ff1 | ||
|
|
9904246e45 | ||
|
|
06f52cbe1d | ||
|
|
33d75925a6 | ||
|
|
52fe92efe5 | ||
|
|
7fec21280f | ||
|
|
84b5fef640 | ||
|
|
9d2aa9fcd5 | ||
|
|
ca2608756d | ||
|
|
27ebcfd418 | ||
|
|
62deadda1e | ||
|
|
990d60f63a | ||
|
|
0571fcfd32 | ||
|
|
4a77cb66d4 | ||
|
|
26fdb8f899 | ||
|
|
5635c5d851 | ||
|
|
00ec07b88a | ||
|
|
ef84979f91 | ||
|
|
d63df0c48e | ||
|
|
002e5f304d | ||
|
|
4bfe9ea9ca | ||
|
|
b7988dc666 | ||
|
|
5463372b66 | ||
|
|
85525580c6 | ||
|
|
c1cfdb17f3 | ||
|
|
f8b4777ed8 | ||
|
|
f7bbfe9010 | ||
|
|
690aa2c189 | ||
|
|
989db0b49b | ||
|
|
90fc8adb74 | ||
|
|
1b7d7e7b64 | ||
|
|
472ead88b0 | ||
|
|
cb788ab730 | ||
|
|
3730bd638a | ||
|
|
7e9da94881 | ||
|
|
f023707809 | ||
|
|
2160ec4f29 | ||
|
|
2965408c3d | ||
|
|
30132f34b2 | ||
|
|
c34f53591c | ||
|
|
47c11b76f4 | ||
|
|
d1b5e9df81 | ||
|
|
ef06dc1fc3 | ||
|
|
c30f34aea6 | ||
|
|
b5bb178708 | ||
|
|
aebf70ea3b | ||
|
|
7deee7a764 | ||
|
|
9681bc16b5 | ||
|
|
14f0f792b2 | ||
|
|
b881db6b62 | ||
|
|
e584a2a0c3 |
52
.github/workflows/daily_keywords.yml
vendored
Normal file
52
.github/workflows/daily_keywords.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
name: Daily Data Factory
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# 每天 UTC 18:00 (北京时间凌晨 02:00) 执行,赶在节点凌晨3点更新前造好子弹
|
||||
- cron: '0 18 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
update-data:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Execute Trends Engine (搜索词库)
|
||||
run: python scripts/fetch_trends.py
|
||||
|
||||
- name: Execute Trust URL Engine (活体新闻流融合)
|
||||
run: python scripts/fetch_trust_urls.py
|
||||
|
||||
- name: Commit and Push All Data
|
||||
run: |
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
# 一揽子添加搜索词库和地区白名单的变化
|
||||
git add data/keywords/
|
||||
git add data/regions/
|
||||
|
||||
# 防御机制:如果没有新数据,就静默退出,不产生空提交
|
||||
if git diff --staged --quiet; then
|
||||
echo "No changes, skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 策略:将两路数据的更新合并为一个原子提交
|
||||
git commit -m "chore(data): 🤖 自动机兵:同步全战区热点词库与活体新闻流 [$(date +'%Y-%m-%d')]"
|
||||
git push origin main
|
||||
33
.github/workflows/ua_factory.yml
vendored
Normal file
33
.github/workflows/ua_factory.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Automated Massive UA Factory
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# 每个月 1 号凌晨 00:00 执行
|
||||
- cron: '0 0 1 * *'
|
||||
workflow_dispatch: # 允许手动点击运行
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write # 必须赋予写入权限,否则无法更新仓库文件
|
||||
|
||||
steps:
|
||||
- name: 📥 Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: ⚙️ Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: 🚀 Run UA Factory Generator
|
||||
run: python scripts/ua_generator.py
|
||||
|
||||
- name: 📤 Commit and Push to Main
|
||||
run: |
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git add data/user_agents.txt
|
||||
# 只有在文件内容确实发生变化时才执行提交
|
||||
git diff --quiet && git diff --staged --quiet || (git commit -m "chore(data): 🤖 自动机兵:刷新 4000 条绝对坐标指纹库" && git push)
|
||||
661
LICENSE
661
LICENSE
@@ -0,0 +1,661 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
151
README.md
151
README.md
@@ -1,16 +1,32 @@
|
||||
# 🛡️ IP-Sentinel (分布式 IP 哨兵集群)
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
> **一个极度轻量、零感知、支持中枢遥控的 VPS IP 自动化养护与区域纠偏引擎。**
|
||||
|
||||
专为解决 VPS IPv4 被 Google 等数据库错误定位到中国大陆/香港(俗称“送中”)等问题而生。IP-Sentinel 已从单机脚本全面跃升为 **Master-Agent 分布式架构**。它像影子一样潜伏在全球各地的服务器后台,通过高度拟真的真实用户行为为你默默积累 IP 权重,并允许你通过 Telegram 随时随地对整个舰队进行毫秒级“点名”与“遥控”。
|
||||
📢 官方战术交流频道: 🛰️ [IP-Sentinel Matrix](https://t.me/IP_Sentinel_Matrix)
|
||||
|
||||
## ✨ 核心极客特性
|
||||
专为解决 VPS IP 被 Google 等数据库错误定位到中国大陆/香港(俗称“送中”)等问题而生。IP-Sentinel 已从单机脚本全面跃升为 **Master-Agent 分布式架构**。它像影子一样潜伏在全球各地的服务器后台,通过高度拟真的真实用户行为为你默默积累 IP 权重,并允许你通过 Telegram 随时随地对整个舰队进行毫秒级“点名”与“遥控”。
|
||||
|
||||
* 🧠 **分布式中枢 (Master-Agent)**:彻底解决多台 VPS 的管理痛点。一台 Master 主控集成 SQLite 数据库与 Telegram 机器人,统管无数台 Agent 边缘节点,告别 API 消息冲突。
|
||||
* 🎮 **TG 战术面板 (Command Center)**:无需记忆繁琐命令,原生 Inline Keyboard 按钮驱动。支持一键调出节点列表、一键下发伪装指令、一键索要精准战报、**毫秒级抓取实时运行日志**。
|
||||
* 🛡️ **NAT 穿透与安全网关 (NAT-Friendly)**:边缘节点采用 Python3 极轻量 Webhook 监听,**完全自定义通信端口**,完美支持受限 NAT 小鸡。独创 TG 转发授权机制,杜绝野生节点恶意接入。
|
||||
* 👻 **高仿真人类行为 (Human-Like)**:摒弃死板的 Ping/Curl,引入单次会话指纹锁定、10 米级 GPS 坐标微抖动、以及 60~150 秒的真实阅读停顿拉伸,完美避开 AI 封控。
|
||||
* 📡 **OTA 静默进化 (Smart Updates)**:系统每周日凌晨自动从云端拉取最新的“热搜词汇”和“真实设备指纹池”,确保养护行为与时俱进、永不过时。
|
||||
## ✨ 核心极客特性 (Core Architecture)
|
||||
|
||||
- 📊 **深海声呐全维探针 (Deep Sea Sonar v4.0)**:内嵌强效正则清洗的 JSON 提取引擎,异步生成情报级 IP 质量战报。聚合 Scamalytics、AbuseIPDB 等五大权威防欺诈库,精准嗅探代理/VPN特征、25端口封堵情况及原生流媒体(Netflix/Disney+/TikTok等)解锁状态,并自带 Google “送中”高危预警与基于 SQLite 的历史污染趋势追踪图谱。
|
||||
- ⚡ **无损高并发引擎 (WAL Concurrency)**:司令部 SQLite 数据库全面激活 `WAL` (Write-Ahead Logging) 模式与毫秒级排队削峰算法。即使您同时对 500 台边缘节点发起全军总攻,也能完美规避 `database is locked` 与 Telegram `429` 频率拦截,实现 100% 战报送达。
|
||||
- 🪶 **抽脂级极简部署 (Zero-Bloat Native)**:全栈剔除 `pip`、`flask` 等臃肿第三方依赖,完全基于 Python3 原生标准库运行。安装底层强制注入 `--no-install-recommends` 防捆绑参数。无论是 128MB 内存的极简 NAT 小鸡,还是 Alpine/Arch Linux 特种系统,均可如丝般顺滑运行。
|
||||
- 🎛️ **扁平化指挥矩阵 (Flat Command Matrix)**:[v3.6.1 重构] 引入扁平化 L0-L3 四级战区降维视图与双轨身份制。深度定制 Inline Keyboard 逃生舱交互,支持在统一哨兵终端进行原位丝滑重绘 (In-place UI Edit),实现毫秒级模块热启停与日志抓取,彻底告别刷屏烦恼。
|
||||
- 🔄 **全栈零信任 OTA 引擎 (Zero-Trust OTA Upgrade)**:首创双端物理熔断机制。长官可通过私有中枢,一键向全舰队下发静默热重载指令;更支持**「司令部金蝉脱壳」**,中枢大脑可在此面板自我抛出幽灵进程进行免交互直装覆盖,实现真正的全栈去 SSH 化运维。
|
||||
- 🛡️ **SSOT 溯源与热更新装甲 (Smooth Upgrade Engine)**:全系脚本彻底消灭硬编码,部署时动态抓取云端版本信标。自带状态机嗅探逻辑与防撞甲探测,即使是手动在终端运行安装,也仅需回车瞬间完成配置继承、数据同步与无损换代。
|
||||
- 🗺️ **全球拓扑矩阵与活体词库 (Global Nexus)**:守护版图横跨亚欧美三大洲。接入 GitHub Actions 云端流水线,每日静默同步全球各大区当日 Google 真实热搜榜单与高权重本土站点,让伪装行为永远贴合当地网络脉搏。
|
||||
- 👻 **资产持久化与错峰调度 (Hash-Seeded Persona)**:摒弃随机抽取指纹,基于节点物理 IP 哈希永久锁定 3 个绝对专属设备,完美构建高权重真实家庭内网画像。叠加按需智能分频与随机防并发休眠,化解“惊群效应”。
|
||||
- 🖧 **底层路由死锁与高精度探针 (Hard-Bind Routing)**:底层探测引擎强力接管 curl 核心参数 (`--interface`),将发出的每一滴伪装流量死死绑定在物理网卡或隧道 IP 上。配合多级 ISP 容灾链路,彻底杜绝双栈环境下的流量溢出与 API 误判。
|
||||
|
||||
**—— 💎 骨干基建特征 ——**
|
||||
- 🏭 **自动化指纹兵工厂**:依托 GitHub Actions CI/CD 流水线,每月 1 日无人值守锻造 4000+ 带绝对物理分区的真实终端设备数据。
|
||||
- 🔒 **叹息之墙 (Zero-Trust HMAC)**:底层通讯引入 时间戳 + HMAC-SHA256 军用级动态签名。指令有效期仅 60 秒(阅后即焚),未授权请求直接触发系统级 403 物理熔断,彻底免疫中间人抓包与重放攻击。
|
||||
- ☁️ **云端中枢 (Public Master)**:官方公共机器人 [@OmniBeacon_bot](https://t.me/OmniBeacon_bot) ,新手免自建,一键接入极速入伍!同时支持硬核极客私有化 SQLite 分布式部署。
|
||||
- 👁️🗨️ **玻璃房透明遥测 (Glasshouse)**:基于 Cloudflare Workers 的全透明计数中枢,绝对零隐私收集,仅作原子累加,底层网关源码全开源。
|
||||
|
||||
## 📂 项目架构 (Monorepo)
|
||||
|
||||
@@ -18,43 +34,118 @@
|
||||
|
||||
```text
|
||||
📦 IP-Sentinel
|
||||
┣ 📂 master/ # 🧠 司令部:SQLite 存储、TG 监听与 Webhook 调度中心
|
||||
┣ 📂 core/ # 🛡️ 边缘哨兵:Webhook 被动监听、高拟真养护引擎
|
||||
┗ 📂 data/ # 🗂️ 全球数据规则库
|
||||
┣ 📂 regions/ # 🧊 冷数据:各地区 GPS 基准配置 (固化)
|
||||
┣ 📂 keywords/ # 🔥 热数据:动态搜索词库 (OTA 自动更新)
|
||||
┗ 📜 user_agents.txt # 🔥 热数据:全局真实设备指纹池
|
||||
┣ 📂 .github/workflows/ # 🏭 自动化兵工厂:每月定时触发指纹生成的 CI/CD 流水线
|
||||
┣ 📂 master/ # 🧠 司令部:SQLite 存储 (含 ip_trend_log 趋势跟踪表)、TG 监听与 Webhook 调度
|
||||
┣ 📂 core/ # 🛡️ 边缘哨兵:Webhook 被动监听、哈希锚定执行引擎 (集成深海声呐探测模块)
|
||||
┣ 📂 scripts/ # 🐍 兵工厂引擎:基于 Python 的多物理分区 UA 生成器
|
||||
┣ 📂 data/ # 🗂️ 全球数据规则库 (动态拓扑)
|
||||
┃ ┣ 📜 map.json # 🌍 全球区域大脑 (v3.5.0 大洲战区拓扑)
|
||||
┃ ┣ 📂 regions/ # 🧊 冷数据:按 [国家/省州/城市] 深度细分的 LBS 锚点
|
||||
┃ ┣ 📂 keywords/ # 🔥 热数据:按国家归类的动态搜索词库 (OTA 自动更新)
|
||||
┃ ┗ 📜 user_agents.txt # 🔥 热数据:由兵工厂每月锻造的绝对坐标专属设备库
|
||||
┣ 📜 version.txt # 🚩 双端版本信标:Agent/Master 独立解耦的 KV 环境配置
|
||||
┗ 📂 telemetry/ # 👁️🗨️ 玻璃房计划:Cloudflare Workers 透明计数器网关源码
|
||||
```
|
||||
|
||||
🚀 极速部署 (Quick Start)
|
||||
请准备一个 Telegram Bot Token 和你的个人 Chat ID。整个集群的部署分为两步:
|
||||
## 🚀 极速部署 (Quick Start)
|
||||
> 🛡️ **跨平台装甲支持**:Debian / Ubuntu / CentOS / RHEL / Alpine Linux / Arch Linux
|
||||
系统现提供两种接入模式,请根据您的战术需求选择:
|
||||
|
||||
第一步:部署中枢司令部 (Master)
|
||||
找一台网络稳定的 VPS 作为大脑(仅需部署一台),以 root 身份执行:
|
||||
### 🔹 模式 A:私有独立模式 (全自主、强烈推荐)
|
||||
适合追求绝对数据隐私与舰队最高控制权的领主。
|
||||
|
||||
> ☢️ **核按钮系统已就绪**:采用私有部署,您将解锁 **OTA 远程静默升级** 权限!所有私有前线节点均可通过您的 TG 面板实现一键全网代码热重载换代!
|
||||
|
||||
- **部署 Master (中枢大脑)**:找一台 VPS 作为司令部(仅需部署一台),执行:
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/master/install_master.sh -o /tmp/ins_master.sh && sudo bash /tmp/ins_master.sh
|
||||
```
|
||||
- 部署 Agent (边缘哨兵):在需要养护的机器上执行 Agent 脚本,安装时选择私有独立中枢,并分别输入您自建机器人的 [Token](https://blog.iot-architect.com/engineering-practice/create-private-telegram-bot-via-botfather) 以及您的个人 [Chat ID](https://blog.iot-architect.com/engineering-practice/get-telegram-personal-id-via-userinfobot) :
|
||||
|
||||
```Bash
|
||||
bash <(curl -sL https://git.94211762.xyz/hotyue/IP-Sentinel/raw/branch/main/master/install_master.sh)
|
||||
curl -fsSL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/core/install.sh -o /tmp/ins_agent.sh && sudo bash /tmp/ins_agent.sh
|
||||
```
|
||||
完成后,你的机器人将开始全局接客,等待边缘节点注册。
|
||||
- 激活节点:安装完成后,您的手机会收到一条 #REGISTER# 注册暗号,将其转发给您自己的机器人即可完成编队入库。
|
||||
|
||||
第二步:部署边缘哨兵 (Agent)
|
||||
在你需要养护 IP 的无数台目标机器(包括 NAT 机器)上执行:
|
||||
### 🔸 模式 B:官方公共模式 (最简体验)
|
||||
适合不想折腾、只想快速体验养护效果的新兵。
|
||||
|
||||
- 关注机器人:在 TG 中关注官方安全网关 [@OmniBeacon_bot](https://t.me/OmniBeacon_bot) 并发送 /start。
|
||||
|
||||
- 部署 Agent:在目标 VPS 上执行以下指令,安装过程中选择官方公共网关,并输入您的 Chat ID:
|
||||
|
||||
```Bash
|
||||
bash <(curl -sL https://git.94211762.xyz/hotyue/IP-Sentinel/raw/branch/main/core/install.sh)
|
||||
curl -fsSL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/core/install.sh -o /tmp/ins_agent.sh && sudo bash /tmp/ins_agent.sh
|
||||
```
|
||||
安装时输入与 Master 相同的 Bot Token 及自定义 Webhook 端口。安装完毕后,请在手机 TG 端将生成的 #REGISTER# 暗号转发给机器人,完成安全授权入库!
|
||||
- 激活节点:同上,将收到的暗号转发给官方机器人即可。
|
||||
|
||||
🗑️ 一键无痕卸载
|
||||
如果你需要清理某个边缘节点,只需重新运行 core/install.sh 并选择 [3],或直接在节点终端执行:
|
||||
## 🆙 架构级无损热升级指引 (Upgrade Guide)
|
||||
|
||||
### 📡 方式一:OTA 远程静默升级 (私有中枢专属)
|
||||
如果您是私有中枢领主,当司令部首页 (`/start`) 或每日战报提示发现新版本时:
|
||||
|
||||
1. **升级 Master 司令部自身**:在司令部顶级菜单,点击最上方的 `[ 🆙 升级司令部至 vX.X.X ]`。中枢将释放幽灵进程静默重构,数秒后向您发送捷报。
|
||||
2. **升级全舰队 Agent**:在司令部顶级菜单,点击 `[ ☢️ 全舰队 OTA 热重载 ]`。
|
||||
3. **升级单节点 Agent**:进入 `🌍 全球战区雷达` -> 选择目标节点 -> 在统一终端面板点击 `[ 🆙 OTA 静默升级 ]`。
|
||||
*(⚠️ 节点收到指令后会在后台挂起静默拉取,全程无需登录 SSH,完成后将主动发回心跳确认!)*
|
||||
|
||||
### 💻 方式二:SSH 终端平滑直装 (适用于官方网关或老旧节点)
|
||||
如果您的节点不支持 OTA,或者您的节点版本过于陈旧 (如 v3.3.1):
|
||||
|
||||
- 登录该节点的 SSH 终端,再次运行上面的 core/install.sh 官方安装指令。
|
||||
|
||||
- 安装引擎自带状态机嗅探逻辑,它会自动读取老旧数据,您只需一路回车,3 秒即可在本地完成配置继承、数据同步与新内核的无损覆盖热重载!
|
||||
|
||||
## 🗑️ 一键无痕卸载
|
||||
如果你需要清理某个边缘节点,只需重新运行 `core/install.sh` 并选择 **[2]**,或直接在节点终端执行:
|
||||
|
||||
```Bash
|
||||
bash /opt/ip_sentinel/core/uninstall.sh
|
||||
|
||||
```
|
||||
|
||||
🤝 参与贡献
|
||||
如果你想为项目增加新的节点区域(例如德国、英国、新加坡等),或者提供更丰富的本土化搜索词库,非常欢迎提交 Pull Request!
|
||||
只需在 data/regions/ 新增对应国家的 JSON 规则,并在 data/keywords/ 新增词库 txt 即可。
|
||||
## 🧓 传家宝老旧系统专用通道 (Debian 9)
|
||||
|
||||
⚠️ 免责声明
|
||||
本项目仅供网络原理研究、个人 VPS 维护学习使用。请遵守当地法律法规及目标服务商的 TOS(服务条款),切勿用于恶意高频请求或任何非法用途。使用者需自行承担因不当使用造成的 IP 封禁或其他相关风险。
|
||||
如果你的小鸡系统版本过低(如 Debian 9),由于官方 APT 源已关闭且 Python 版本过旧,无法使用主线版本,请使用 **Legacy 兼容分支** 部署。
|
||||
*(注意:该分支仅作基础维护,不享受新功能迭代,请尽可能升级你的系统)*
|
||||
|
||||
```bash
|
||||
bash <(curl -sL https://raw.githubusercontent.com/hotyue/IP-Sentinel/legacy/core/install.sh)
|
||||
```
|
||||
|
||||
## 📡 战术联络 (Community)
|
||||
|
||||
如果你在使用过程中遇到任何疑难杂症,或者想围观大佬们的养护战报,欢迎加入我们的基地:
|
||||
- Telegram 频道: [@IP_Sentinel_Matrix](https://t.me/IP_Sentinel_Matrix)
|
||||
|
||||
## 🤝 参与贡献 (Contributors)
|
||||
|
||||
**🌟 感谢以下所有为 IP-Sentinel 添砖加瓦的指挥官们!** 你们的每一次 PR 都在让这艘战舰的全球雷达覆盖得更广。
|
||||
|
||||
<a href="https://github.com/hotyue/IP-Sentinel/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=hotyue/IP-Sentinel" alt="Contributors" />
|
||||
</a>
|
||||
|
||||
如果你想为项目增加新的节点区域(例如德国、英国、大洋洲等),或者提供更丰富的本土化搜索词库,非常欢迎提交 Pull Request!
|
||||
|
||||
|
||||
> - 感谢 @xykt 本项目IP质量检测采用[xykt/IPQuality](https://github.com/xykt/IPQuality) 脚本!
|
||||
|
||||
**💡 全球节点贡献规范:**
|
||||
1. 在 `data/regions/国家代码/省州代码/` 目录下新增对应城市的配置 `.json`。
|
||||
2. 在 `data/keywords/` 目录下新增或完善配套国家的词库 `kw_XX.txt`。
|
||||
3. **最重要的一步:** 在 `data/map.json` 中登记你的国家、省州与城市信息。安装脚本将自动读取地图,在全球雷达中点亮你的节点!
|
||||
|
||||
## ⚠️ 免责声明
|
||||
|
||||
本项目仅供网络原理研究、个人 VPS 维护学习使用。请遵守当地法律法规及目标服务商的 TOS(服务条款),切勿用于恶意高频请求或任何非法用途。使用者需自行承担因不当使用造成的 IP 封禁或其他相关风险。
|
||||
|
||||
## 保持联系
|
||||
|
||||
[](https://blog.iot-architect.com)
|
||||
|
||||
如果你觉得这个项目对你有帮助,欢迎关注我的个人博客,我会定期分享技术教程。
|
||||
|
||||
|
||||
## Stargazers over time
|
||||
[](https://starchart.cc/hotyue/IP-Sentinel)
|
||||
523
core/agent_daemon.sh
Normal file → Executable file
523
core/agent_daemon.sh
Normal file → Executable file
@@ -1,13 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: agent_daemon.sh (受控节点 Webhook 守护进程 V1.2)
|
||||
# 核心功能: 智能防打扰注册、进程防冲突自检、后台静默监听
|
||||
# 脚本名称: agent_daemon.sh (受控节点 Webhook 守护进程 - 动态锚点版)
|
||||
# 核心功能: 智能防打扰注册、进程自检、模块级路由分发(403拦截)
|
||||
# ==========================================================
|
||||
|
||||
INSTALL_DIR="/opt/ip_sentinel"
|
||||
CONFIG_FILE="${INSTALL_DIR}/config.conf"
|
||||
IP_CACHE="${INSTALL_DIR}/core/.last_ip" # 【新增】本地 IP 状态缓存文件
|
||||
IP_CACHE="${INSTALL_DIR}/core/.last_ip"
|
||||
|
||||
[ ! -f "$CONFIG_FILE" ] && exit 1
|
||||
source "$CONFIG_FILE"
|
||||
@@ -17,91 +17,514 @@ source "$CONFIG_FILE"
|
||||
|
||||
# 默认 Webhook 监听端口
|
||||
AGENT_PORT=${AGENT_PORT:-9527}
|
||||
NODE_NAME=$(hostname | cut -c 1-15)
|
||||
|
||||
# --- [重点升级 1: 守护进程防冲突自检] ---
|
||||
# 检查是否已经有 webhook 进程在监听当前端口,如果有,直接安静退出 (Cron 友好)
|
||||
if pgrep -f "webhook.py $AGENT_PORT" > /dev/null; then
|
||||
# 保持静默,不输出多余日志,防止打扰系统的 syslog
|
||||
exit 0
|
||||
# [v3.5.2 核心] 载入不可变主键与可变展示名 (双轨身份)
|
||||
if [ -z "$NODE_NAME" ]; then
|
||||
IP_HASH=$(echo "${PUBLIC_IP:-127.0.0.1}" | md5sum | cut -c 1-4 | tr 'a-z' 'A-Z')
|
||||
NODE_NAME="$(hostname | tr -cd 'a-zA-Z0-9' | cut -c 1-10)-${IP_HASH}"
|
||||
fi
|
||||
NODE_ALIAS="${NODE_ALIAS:-$NODE_NAME}"
|
||||
|
||||
# 1. 获取本机原生公网 IPv4
|
||||
AGENT_IP=$(curl -4 -s -m 5 api.ip.sb/ip)
|
||||
|
||||
# 1. 尝试获取实时公网 IP
|
||||
RAW_IP=$(curl -${IP_PREF:-4} -s -m 5 api.ip.sb/ip | tr -d '[:space:]')
|
||||
|
||||
# [v3.3.1 修改] 为新获取到的 v6 自动加方括号;如果网络波动没抓到,强制信任本地 config 中的公网面孔
|
||||
if [ -n "$RAW_IP" ]; then
|
||||
if [[ "$RAW_IP" == *":"* ]] && [[ "$RAW_IP" != *"["* ]]; then
|
||||
AGENT_IP="[${RAW_IP}]"
|
||||
else
|
||||
AGENT_IP="$RAW_IP"
|
||||
fi
|
||||
else
|
||||
AGENT_IP="${PUBLIC_IP:-${BIND_IP:-Unknown}}"
|
||||
fi
|
||||
|
||||
if [ -n "$AGENT_IP" ]; then
|
||||
# --- [重点升级 2: 智能防打扰注册机制] ---
|
||||
LAST_IP=""
|
||||
[ -f "$IP_CACHE" ] && LAST_IP=$(cat "$IP_CACHE")
|
||||
[ -f "$IP_CACHE" ] && LAST_IP=$(cat "$IP_CACHE" | tr -d '[:space:]')
|
||||
|
||||
# 只有当这是第一次运行,或者公网 IP 发生变动时,才发送 Telegram 申请
|
||||
if [ "$AGENT_IP" != "$LAST_IP" ]; then
|
||||
REG_MSG="👋 **[边缘节点接入申请]**%0A节点: \`${NODE_NAME}\`%0A地址: \`${AGENT_IP}:${AGENT_PORT}\`%0A%0A⚠️ **安全验证**: 为防止非法节点接入,请长按复制下方代码,并**发送给我**以完成最终授权录入:%0A%0A\`#REGISTER#|${NODE_NAME}|${AGENT_IP}|${AGENT_PORT}\`"
|
||||
# [v3.6.0 核心修复] 携带 7 字段身份发起注册申请 (追加 ENABLE_OTA,防止 IP 变动重新注册时丢失 OTA 权限)
|
||||
REG_MSG="👋 **[边缘节点接入申请]**%0A大区: \`${REGION_CODE}\`%0A节点: \`${NODE_ALIAS}\`%0A地址: \`${AGENT_IP}:${AGENT_PORT}\`%0A%0A⚠️ **安全验证**: 为防止非法节点接入,请长按复制下方代码,并**发送给我**以完成最终授权录入:%0A%0A\`#REGISTER#|${REGION_CODE}|${NODE_NAME}|${AGENT_IP}|${AGENT_PORT}|${NODE_ALIAS}|${ENABLE_OTA:-false}\`"
|
||||
|
||||
curl -s -m 5 -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
|
||||
curl -s -m 5 -X POST "${TG_API_URL}" \
|
||||
-d "chat_id=${CHAT_ID}" \
|
||||
-d "text=${REG_MSG}" \
|
||||
-d "parse_mode=Markdown" > /dev/null
|
||||
|
||||
echo "✅ [Agent] 已向司令部发送接入申请,请在 Telegram 手机端完成授权!"
|
||||
# 记录当前 IP 到缓存文件
|
||||
echo "$AGENT_IP" > "$IP_CACHE"
|
||||
else
|
||||
echo "ℹ️ [Agent] IP 未变动 ($AGENT_IP),跳过重复注册申请。"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 3. 启动轻量级 Python3 Webhook 监听服务
|
||||
# ================== [v3.6.3 新增: 自动生成自签名 TLS 加密证书] ==================
|
||||
# [修复] 彻底废除官方网关免 TLS 的裸奔逻辑,全网强制生成证书装甲
|
||||
CERT_FILE="${INSTALL_DIR}/core/cert.pem"
|
||||
KEY_FILE="${INSTALL_DIR}/core/key.pem"
|
||||
if [ ! -f "$CERT_FILE" ] || [ ! -f "$KEY_FILE" ]; then
|
||||
echo "🔐 [Agent] 正在生成本地自签名 TLS 加密证书 (2048位 RSA)..."
|
||||
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
|
||||
-keyout "$KEY_FILE" -out "$CERT_FILE" \
|
||||
-subj "/C=US/O=IP-Sentinel/CN=Agent-Sec" >/dev/null 2>&1 || true
|
||||
fi
|
||||
# ==============================================================================
|
||||
|
||||
# 3. 启动轻量级 Python3 Webhook 监听服务 (v3.0.4 动态 HMAC 签名防重放)
|
||||
cat > "${INSTALL_DIR}/core/webhook.py" << 'EOF'
|
||||
import http.server
|
||||
import socketserver
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
import html
|
||||
# ================== [v3.0.4 新增密码学与解析依赖] ==================
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
import hmac
|
||||
import hashlib
|
||||
import time
|
||||
# ====================================================================
|
||||
|
||||
PORT = int(sys.argv[1])
|
||||
|
||||
# 🛡️ 防重放攻击 (Nonce 缓存池)
|
||||
USED_SIGNS = {}
|
||||
def clean_used_signs():
|
||||
now = time.time()
|
||||
# 清理过期签名 (超 60 秒的安全窗口)
|
||||
expired = [s for s, t in USED_SIGNS.items() if now - t > 65]
|
||||
for s in expired:
|
||||
del USED_SIGNS[s]
|
||||
|
||||
# 🛡️ 提取全局鉴权 Token (利用 CHAT_ID 作为 PSK 预共享密钥)
|
||||
AUTH_TOKEN = ""
|
||||
if os.path.exists('/opt/ip_sentinel/config.conf'):
|
||||
with open('/opt/ip_sentinel/config.conf', 'r') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line.startswith('CHAT_ID='):
|
||||
AUTH_TOKEN = line.split('=', 1)[1].strip('"\'')
|
||||
break
|
||||
|
||||
class AgentHandler(http.server.BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
# 统一返回成功,防止 Master 请求超时阻塞
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.end_headers()
|
||||
self.wfile.write(b"Agent Received Action\n")
|
||||
# 🛡️ [v3.0.4 核心] URL 解析与动态 HMAC-SHA256 签名校验
|
||||
parsed = urllib.parse.urlparse(self.path)
|
||||
req_path = parsed.path
|
||||
|
||||
# 路由分发
|
||||
if self.path == '/trigger_run':
|
||||
subprocess.Popen(['bash', '/opt/ip_sentinel/core/mod_google.sh'])
|
||||
elif self.path == '/trigger_report':
|
||||
subprocess.Popen(['bash', '/opt/ip_sentinel/core/tg_report.sh'])
|
||||
elif self.path == '/trigger_log':
|
||||
bash_cmd = """
|
||||
source /opt/ip_sentinel/config.conf
|
||||
LOG_DATA=$(tail -n 15 /opt/ip_sentinel/logs/sentinel.log)
|
||||
NODE=$(hostname | cut -c 1-15)
|
||||
curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
|
||||
-d "chat_id=${CHAT_ID}" \
|
||||
-d "text=📄 **[${NODE}] 实时运行日志:**%0A\`\`\`log%0A${LOG_DATA}%0A\`\`\`" \
|
||||
-d "parse_mode=Markdown"
|
||||
"""
|
||||
subprocess.Popen(['bash', '-c', bash_cmd])
|
||||
if AUTH_TOKEN:
|
||||
query = urllib.parse.parse_qs(parsed.query)
|
||||
req_t = query.get('t', [''])[0]
|
||||
req_sign = query.get('sign', [''])[0]
|
||||
|
||||
# 校验 1:参数是否齐全
|
||||
if not req_t or not req_sign:
|
||||
self.send_response(401)
|
||||
self.end_headers()
|
||||
self.wfile.write(b"401 Unauthorized: Missing Signature\n")
|
||||
return
|
||||
|
||||
try:
|
||||
current_time = int(time.time())
|
||||
# 校验 2:时间戳防重放 (误差 ±60秒 内有效,拒绝隔夜抓包重放)
|
||||
if abs(current_time - int(req_t)) > 60:
|
||||
self.send_response(401)
|
||||
self.end_headers()
|
||||
self.wfile.write(b"401 Unauthorized: Request Expired\n")
|
||||
return
|
||||
except ValueError:
|
||||
self.send_response(401)
|
||||
self.end_headers()
|
||||
return
|
||||
|
||||
# 校验 2.5:基于 60秒 窗口的精确重放拦截 (拦截 MITM 并发洗劫)
|
||||
clean_used_signs()
|
||||
if req_sign in USED_SIGNS:
|
||||
self.send_response(401)
|
||||
self.end_headers()
|
||||
self.wfile.write(b"401 Unauthorized: Replay Attack Detected\n")
|
||||
return
|
||||
|
||||
# 校验 3:HMAC 数据完整性与身份合法性校验
|
||||
msg = f"{req_path}:{req_t}".encode('utf-8')
|
||||
expected_sign = hmac.new(AUTH_TOKEN.encode('utf-8'), msg, hashlib.sha256).hexdigest()
|
||||
|
||||
# 使用 compare_digest 防御时序攻击
|
||||
if not hmac.compare_digest(expected_sign, req_sign):
|
||||
self.send_response(401)
|
||||
self.end_headers()
|
||||
self.wfile.write(b"401 Unauthorized: Signature Mismatch\n")
|
||||
return
|
||||
|
||||
# 鉴权通过,记录该签名至防重放内存池
|
||||
USED_SIGNS[req_sign] = current_time
|
||||
|
||||
# ================== 路由分发 (恢复为安全的精确匹配) ==================
|
||||
|
||||
# 路由 0: 全局统筹调度
|
||||
if req_path == '/trigger_run':
|
||||
if os.path.exists('/opt/ip_sentinel/core/runner.sh'):
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.end_headers()
|
||||
self.wfile.write(b"Action Accepted: runner\n")
|
||||
os.system("nohup bash /opt/ip_sentinel/core/runner.sh >/dev/null 2>&1 &")
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
|
||||
# 路由 1: Google 区域纠偏
|
||||
elif req_path == '/trigger_google':
|
||||
if os.path.exists('/opt/ip_sentinel/core/mod_google.sh'):
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.end_headers()
|
||||
self.wfile.write(b"Action Accepted: mod_google\n")
|
||||
os.system("nohup bash /opt/ip_sentinel/core/mod_google.sh >/dev/null 2>&1 &")
|
||||
else:
|
||||
self.send_response(403)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.end_headers()
|
||||
self.wfile.write(b"403 Forbidden: Google Module Disabled\n")
|
||||
|
||||
# 路由 2: IP 信用净化
|
||||
elif req_path == '/trigger_trust':
|
||||
if os.path.exists('/opt/ip_sentinel/core/mod_trust.sh'):
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.end_headers()
|
||||
self.wfile.write(b"Action Accepted: mod_trust\n")
|
||||
os.system("nohup bash /opt/ip_sentinel/core/mod_trust.sh >/dev/null 2>&1 &")
|
||||
else:
|
||||
self.send_response(403)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.end_headers()
|
||||
self.wfile.write(b"403 Forbidden: Trust Module Disabled\n")
|
||||
|
||||
# 路由 3: 触发战报推送
|
||||
elif req_path == '/trigger_report':
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.end_headers()
|
||||
self.wfile.write(b"Action Accepted: tg_report\n")
|
||||
os.system("nohup bash /opt/ip_sentinel/core/tg_report.sh >/dev/null 2>&1 &")
|
||||
|
||||
# 路由 4: 抓取并回传实时日志
|
||||
elif req_path == '/trigger_log':
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.end_headers()
|
||||
self.wfile.write(b"Action Accepted: fetch_log\n")
|
||||
|
||||
try:
|
||||
config = {}
|
||||
if os.path.exists('/opt/ip_sentinel/config.conf'):
|
||||
with open('/opt/ip_sentinel/config.conf', 'r') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if '=' in line and not line.startswith('#'):
|
||||
key, val = line.split('=', 1)
|
||||
config[key] = val.strip('"\'')
|
||||
|
||||
log_data = "日志文件不存在或为空"
|
||||
log_path = '/opt/ip_sentinel/logs/sentinel.log'
|
||||
if os.path.exists(log_path):
|
||||
with open(log_path, 'r', errors='ignore') as f:
|
||||
lines = f.readlines()
|
||||
if lines:
|
||||
log_data = html.escape("".join(lines[-15:]))
|
||||
|
||||
# [v3.5.2 核心] 获取版本与节点展示别名
|
||||
local_ver = config.get('AGENT_VERSION', '未知')
|
||||
node_alias = config.get('NODE_ALIAS', config.get('NODE_NAME', 'Unknown-Node'))
|
||||
|
||||
text_msg = f"📄 <b>[{node_alias}] 实时日志 (v{local_ver}):</b>\n<pre><code>{log_data}</code></pre>"
|
||||
|
||||
# [v4.0.3 体验升级] 引入 json 模块并改用 JSON Payload,挂载返回控制台按钮
|
||||
import json
|
||||
node_name_cb = config.get('NODE_NAME', 'Unknown')
|
||||
payload = {
|
||||
'chat_id': config.get('CHAT_ID', ''),
|
||||
'text': text_msg,
|
||||
'parse_mode': 'HTML',
|
||||
'reply_markup': {
|
||||
'inline_keyboard': [[{'text': '⚙️ 调出该节点控制台', 'callback_data': f'manage:{node_name_cb}'}]]
|
||||
}
|
||||
}
|
||||
data = json.dumps(payload).encode('utf-8')
|
||||
|
||||
req = urllib.request.Request(
|
||||
config.get('TG_API_URL', ''),
|
||||
data=data,
|
||||
# [动态化] 彻底消灭硬编码,使用运行态版本号,并声明 JSON 头
|
||||
headers={
|
||||
'User-Agent': f'IP-Sentinel-Agent/{local_ver}',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
)
|
||||
urllib.request.urlopen(req, timeout=10)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Log transmission failed: {e}")
|
||||
|
||||
# ================== [v4.0.0 新增: 触发深海声呐] ==================
|
||||
elif req_path == '/trigger_quality':
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.end_headers()
|
||||
self.wfile.write(b"Action Accepted: trigger_quality\n")
|
||||
|
||||
if os.path.exists('/opt/ip_sentinel/core/mod_quality.sh'):
|
||||
os.system("nohup bash /opt/ip_sentinel/core/mod_quality.sh >/dev/null 2>&1 &")
|
||||
# =================================================================
|
||||
|
||||
|
||||
# 路由 5: 节点重命名展示别名同步接口 (Base64 终极防御版)
|
||||
elif req_path == '/trigger_rename':
|
||||
b64_alias = query.get('b64', [''])[0]
|
||||
if not b64_alias:
|
||||
self.send_response(400)
|
||||
self.end_headers()
|
||||
self.wfile.write(b"400 Bad Request: Alias is empty\n")
|
||||
return
|
||||
|
||||
import re
|
||||
import base64
|
||||
try:
|
||||
# 1. 还原 URL 安全的 Base64 字符并解码 (杜绝乱码与 WAF 拦截)
|
||||
pad = len(b64_alias) % 4
|
||||
if pad > 0:
|
||||
b64_alias += '=' * (4 - pad)
|
||||
b64_alias = b64_alias.replace('-', '+').replace('_', '/')
|
||||
raw_alias = base64.b64decode(b64_alias).decode('utf-8', errors='ignore')
|
||||
|
||||
# 2. 强清洗:杜绝 TG Markdown 崩溃,严格限制中英数,最大20字符
|
||||
decoded_alias = raw_alias.replace('_', '-')
|
||||
safe_alias = re.sub(r'[^a-zA-Z0-9\-\u4e00-\u9fa5]', '', decoded_alias)[:20]
|
||||
|
||||
if safe_alias:
|
||||
# 3. 强容错读写 config.conf (引入 fcntl 排他锁与 r+ 模式防并发清空)
|
||||
config_path = '/opt/ip_sentinel/config.conf'
|
||||
import fcntl
|
||||
with open(config_path, 'r+', encoding='utf-8', errors='ignore') as f:
|
||||
fcntl.flock(f, fcntl.LOCK_EX)
|
||||
lines = f.readlines()
|
||||
|
||||
alias_found = False
|
||||
for i, line in enumerate(lines):
|
||||
if line.startswith('NODE_ALIAS='):
|
||||
lines[i] = f'NODE_ALIAS="{safe_alias}"\n'
|
||||
alias_found = True
|
||||
break
|
||||
|
||||
if not alias_found:
|
||||
lines.append(f'NODE_ALIAS="{safe_alias}"\n')
|
||||
|
||||
f.seek(0)
|
||||
f.writelines(lines)
|
||||
f.truncate()
|
||||
fcntl.flock(f, fcntl.LOCK_UN)
|
||||
|
||||
# [v3.5.2 极致丝滑] 移除向 TG 推送冗余报文的逻辑,直接向 Master 回执成功状态即可
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.end_headers()
|
||||
self.wfile.write(b"Action Accepted: trigger_rename\n")
|
||||
return
|
||||
except Exception as e:
|
||||
self.send_response(500)
|
||||
self.end_headers()
|
||||
self.wfile.write(f"500 Internal Error: {str(e)}\n".encode('utf-8'))
|
||||
return
|
||||
|
||||
self.send_response(400)
|
||||
self.end_headers()
|
||||
self.wfile.write(b"400 Bad Request: Invalid Characters\n")
|
||||
|
||||
# ================== [v3.5.3 新增: 模块动态启停接口] ==================
|
||||
elif req_path == '/trigger_toggle':
|
||||
mod_name = query.get('mod', [''])[0]
|
||||
target_state = query.get('state', [''])[0].lower()
|
||||
|
||||
if mod_name not in ['google', 'trust'] or target_state not in ['true', 'false']:
|
||||
self.send_response(400)
|
||||
self.end_headers()
|
||||
self.wfile.write(b"400 Bad Request: Invalid parameters\n")
|
||||
return
|
||||
|
||||
config_key = f"ENABLE_{mod_name.upper()}="
|
||||
|
||||
try:
|
||||
config_path = '/opt/ip_sentinel/config.conf'
|
||||
import fcntl
|
||||
|
||||
with open(config_path, 'r+', encoding='utf-8', errors='ignore') as f:
|
||||
fcntl.flock(f, fcntl.LOCK_EX)
|
||||
lines = f.readlines()
|
||||
|
||||
found = False
|
||||
for i, line in enumerate(lines):
|
||||
if line.startswith(config_key):
|
||||
lines[i] = f'{config_key}"{target_state}"\n'
|
||||
found = True
|
||||
break
|
||||
|
||||
if not found:
|
||||
lines.append(f'{config_key}"{target_state}"\n')
|
||||
|
||||
f.seek(0)
|
||||
f.writelines(lines)
|
||||
f.truncate()
|
||||
fcntl.flock(f, fcntl.LOCK_UN)
|
||||
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.end_headers()
|
||||
self.wfile.write(b"Action Accepted: trigger_toggle\n")
|
||||
|
||||
except Exception as e:
|
||||
self.send_response(500)
|
||||
self.end_headers()
|
||||
self.wfile.write(f"500 Internal Error: {str(e)}\n".encode('utf-8'))
|
||||
|
||||
# ================== [v3.6.0 新增: 零信任 OTA 远程静默升级路由] ==================
|
||||
elif req_path == '/trigger_ota':
|
||||
try:
|
||||
# 动态读取最新 config 内存态
|
||||
config_mem = {}
|
||||
config_path = '/opt/ip_sentinel/config.conf'
|
||||
if os.path.exists(config_path):
|
||||
with open(config_path, 'r', errors='ignore') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if '=' in line and not line.startswith('#'):
|
||||
key, val = line.split('=', 1)
|
||||
config_mem[key] = val.strip('"\'')
|
||||
|
||||
# 🛡️ 熔断校验 1: Agent 本地是否开启了 OTA 授权
|
||||
if config_mem.get('ENABLE_OTA', 'false').lower() != 'true':
|
||||
self.send_response(403)
|
||||
self.end_headers()
|
||||
self.wfile.write(b"403 Forbidden: OTA Upgrade Disabled locally\n")
|
||||
return
|
||||
|
||||
# 🛡️ 熔断校验 2: 是否处于官方公共网关下 (强行硬编码拦截)
|
||||
if config_mem.get('TG_TOKEN', '') == 'OFFICIAL_GATEWAY_MODE':
|
||||
self.send_response(403)
|
||||
self.end_headers()
|
||||
self.wfile.write(b"403 Forbidden: OTA strictly disabled under Public Gateway mode\n")
|
||||
return
|
||||
|
||||
# 校验通过,立即返回 200 回执,释放 Master 连接池
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.end_headers()
|
||||
self.wfile.write(b"Action Accepted: trigger_ota\n")
|
||||
|
||||
# [修复] 逃逸 Systemd Cgroup,并引入 bash -n 语法树校验防砖机制
|
||||
import shutil
|
||||
import base64
|
||||
# 动态提取部署时的源地址,废除强制写死 main 分支,保障隔离测试环境
|
||||
repo_url = "https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||
if os.path.exists('/opt/ip_sentinel/core/install.sh'):
|
||||
with open('/opt/ip_sentinel/core/install.sh', 'r') as f:
|
||||
for line in f:
|
||||
if line.startswith('REPO_RAW_URL='):
|
||||
repo_url = line.split('=', 1)[1].strip('"\'')
|
||||
break
|
||||
|
||||
# 动态构建报错回执文本 (第一层 Base64 隔离换行与特殊字符)
|
||||
err_msg = f"❌ **OTA 熔断告警**\n📍 节点: `{config_mem.get('NODE_ALIAS', '未知')}`\n⚠️ 原因: 脚本语法校验(bash -n)未通过,下载可能不完整。\n🚀 状态: 升级已取消,节点安全。"
|
||||
err_msg_b64 = base64.b64encode(err_msg.encode('utf-8')).decode('utf-8')
|
||||
|
||||
tg_url = config_mem.get('TG_API_URL', '')
|
||||
chat_id = config_mem.get('CHAT_ID', '')
|
||||
|
||||
# [v3.6.3 究极防御] 采用 Base64 将整个 OTA 执行脚本封装 (第二层隔离)
|
||||
# 彻底免疫因为 python 变量掺杂引号而导致的 shell 注入或截断
|
||||
ota_script = f"""
|
||||
export SILENT_OTA="true"
|
||||
curl -fsSL {repo_url}/core/install.sh -o /tmp/ota_agent.sh
|
||||
if bash -n /tmp/ota_agent.sh; then
|
||||
bash /tmp/ota_agent.sh > /opt/ip_sentinel/logs/ota_upgrade.log 2>&1
|
||||
else
|
||||
MSG=$(echo '{err_msg_b64}' | base64 -d)
|
||||
curl -s -m 10 -X POST "{tg_url}" -d "chat_id={chat_id}" -d "text=$MSG" -d "parse_mode=Markdown" > /dev/null 2>&1
|
||||
echo "OTA Checksum Failed: Script corrupted" > /opt/ip_sentinel/logs/ota_upgrade.log
|
||||
fi
|
||||
"""
|
||||
ota_script_b64 = base64.b64encode(ota_script.encode('utf-8')).decode('utf-8')
|
||||
|
||||
# 安全解包并执行
|
||||
if shutil.which("systemd-run"):
|
||||
full_cmd = f"systemd-run --quiet --no-block bash -c \"echo '{ota_script_b64}' | base64 -d | bash\""
|
||||
else:
|
||||
full_cmd = f"nohup bash -c \"echo '{ota_script_b64}' | base64 -d | bash\" >/dev/null 2>&1 &"
|
||||
|
||||
# 彻底统一为 os.system,消灭最后一个可能游离的 Popen 僵尸进程
|
||||
os.system(full_cmd)
|
||||
|
||||
except Exception as e:
|
||||
self.send_response(500)
|
||||
self.end_headers()
|
||||
self.wfile.write(f"500 Internal Error: {str(e)}\n".encode('utf-8'))
|
||||
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
|
||||
def log_message(self, format, *args):
|
||||
# 关闭默认的控制台日志输出,保持后台清爽
|
||||
pass
|
||||
|
||||
import socket
|
||||
# ================== [v3.0.3 变更: 引入多线程模型抵抗 Slowloris 攻击] ==================
|
||||
class ThreadedServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
||||
allow_reuse_address = True # 开启端口复用,防止热重启时端口冲突
|
||||
|
||||
# [核心修复] 显式关闭 V6ONLY 参数,治愈大量云主机纯双栈下的 IPv4 耳聋现象
|
||||
def server_bind(self):
|
||||
if self.address_family == socket.AF_INET6:
|
||||
try:
|
||||
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
|
||||
except Exception:
|
||||
pass
|
||||
super().server_bind()
|
||||
|
||||
try:
|
||||
with socketserver.TCPServer(("", PORT), AgentHandler) as httpd:
|
||||
httpd.serve_forever()
|
||||
# 1. 优先尝试监听双栈/IPv6
|
||||
ThreadedServer.address_family = socket.AF_INET6
|
||||
httpd = ThreadedServer(("::", PORT), AgentHandler)
|
||||
except Exception:
|
||||
# 2. [核心修复 Issue #23] 若系统内核已禁用 IPv6,抛弃报错,智能回退至纯 IPv4 监听
|
||||
ThreadedServer.address_family = socket.AF_INET
|
||||
httpd = ThreadedServer(("0.0.0.0", PORT), AgentHandler)
|
||||
|
||||
# ================== [v3.6.3 核心: 挂载 TLS 加密隧道 (强制装甲版)] ==================
|
||||
import ssl
|
||||
cert_path = '/opt/ip_sentinel/core/cert.pem'
|
||||
key_path = '/opt/ip_sentinel/core/key.pem'
|
||||
|
||||
# 全网强制启用 TLS 装甲,彻底消灭 HTTP 裸奔漏洞
|
||||
if os.path.exists(cert_path) and os.path.exists(key_path):
|
||||
try:
|
||||
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||
context.load_cert_chain(certfile=cert_path, keyfile=key_path)
|
||||
httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
|
||||
except Exception as e:
|
||||
print(f"SSL 隧道构建失败,退化为 HTTP: {e}")
|
||||
# ======================================================================================
|
||||
|
||||
try:
|
||||
httpd.serve_forever()
|
||||
except Exception as e:
|
||||
sys.exit(1)
|
||||
# ====================================================================================
|
||||
EOF
|
||||
|
||||
# --- [重点升级 3: 真正的静默后台启动] ---
|
||||
echo "🚀 [Agent] 正在后台启动 Webhook 监听服务 (端口: $AGENT_PORT)..."
|
||||
# 使用 nohup 和 & 将进程完全推入后台,不阻塞当前终端
|
||||
nohup python3 "${INSTALL_DIR}/core/webhook.py" "$AGENT_PORT" > /dev/null 2>&1 &
|
||||
|
||||
# 尝试脱离终端会话控制 (忽略报错以兼容不同 shell 环境)
|
||||
disown 2>/dev/null || true
|
||||
|
||||
echo "✅ [Agent] 守护进程启动完毕,可安全关闭终端。"
|
||||
# --- [重点升级 3: 移交系统级守护进程接管 (阻塞模式)] ---
|
||||
echo "🚀 [Agent] 正在启动 Webhook 监听服务 (端口: $AGENT_PORT)..."
|
||||
exec python3 "${INSTALL_DIR}/core/webhook.py" "$AGENT_PORT"
|
||||
977
core/install.sh
977
core/install.sh
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: mod_google.sh (Google 业务逻辑模块)
|
||||
# 脚本名称: mod_google.sh (Google 业务逻辑模块 - 动态锚点版)
|
||||
# 核心功能: 执行坐标微抖动、模拟真实阅读时长、会话行为拉伸
|
||||
# ==========================================================
|
||||
|
||||
@@ -16,11 +16,26 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 容错机制:如果父进程没有传递 log 函数,则本地定义一个作为 fallback
|
||||
# 容错机制:如果父进程没有传递 log 函数,则本地定义一个作为 fallback (v3.4.0 引入版本探针)
|
||||
if ! type log >/dev/null 2>&1; then
|
||||
log() {
|
||||
# [v3.4.0 核心] 提取当前配置中的版本锚点
|
||||
local local_ver="${AGENT_VERSION:-未知}"
|
||||
|
||||
# 保证日志目录存在
|
||||
mkdir -p "${INSTALL_DIR}/logs"
|
||||
printf "[$(date '+%Y-%m-%d %H:%M:%S')] [%-5s] [%-7s] [%s] %s\n" "$2" "$1" "$REGION_CODE" "$3" >> "${INSTALL_DIR}/logs/sentinel.log"
|
||||
|
||||
# 日志格式注入 [版本号] 追踪标识
|
||||
local core_msg=$(printf "[v%-5s] [%-5s] [%-7s] [%s] %s" "$local_ver" "$2" "$1" "$REGION_CODE" "$3")
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $core_msg" >> "${INSTALL_DIR}/logs/sentinel.log"
|
||||
|
||||
# 强制推送到 Systemd Journal (如果系统支持)
|
||||
if command -v logger >/dev/null 2>&1; then
|
||||
logger -t ip-sentinel "$core_msg"
|
||||
else
|
||||
# 降级输出到 stdout,让 Systemd 捕获
|
||||
echo "$core_msg"
|
||||
fi
|
||||
}
|
||||
fi
|
||||
|
||||
@@ -48,11 +63,32 @@ get_random_coord() {
|
||||
}
|
||||
|
||||
# --- [环境初始化] ---
|
||||
# 获取当前出口 IP 仅用于日志记录
|
||||
CURRENT_V4=$(curl -4 -m 10 -s https://api.ip.sb/ip || echo "获取IP失败")
|
||||
# [v3.3.1修改] 优先读取对外公网面孔作为哈希种子,兼容 NAT 机的空 BIND_IP
|
||||
CURRENT_IP="${PUBLIC_IP:-${BIND_IP:-Unknown}}"
|
||||
|
||||
# 会话锁定:单次执行内使用固定的浏览器指纹
|
||||
SESSION_UA=${UA_POOL[$RANDOM % ${#UA_POOL[@]}]}
|
||||
# -----------------------------------------------------------
|
||||
# [V3.1.5] 哈希锚定法 (Hash-Seeded Persona)
|
||||
# 利用 IP 算力固定 3 个永久化专属指纹,破除僵尸网络同质化特征
|
||||
# -----------------------------------------------------------
|
||||
TOTAL_UA=${#UA_POOL[@]}
|
||||
if [ "$TOTAL_UA" -gt 0 ]; then
|
||||
# 1. 以本地锁定的公网 IP 为种子,计算固定不变的 CRC32 哈希值
|
||||
SEED=$(echo -n "$CURRENT_IP" | cksum | awk '{print $1}')
|
||||
|
||||
# 2. 利用确定的种子和质数乘数,在全球 4000 的库中计算出本机的 3 个绝对专属坐标
|
||||
IDX1=$(( SEED % TOTAL_UA ))
|
||||
IDX2=$(( (SEED * 17) % TOTAL_UA ))
|
||||
IDX3=$(( (SEED * 31) % TOTAL_UA ))
|
||||
|
||||
# 3. 将绝对坐标映射为该节点的“专属设备库”
|
||||
MY_UA_POOL=("${UA_POOL[$IDX1]}" "${UA_POOL[$IDX2]}" "${UA_POOL[$IDX3]}")
|
||||
|
||||
# 4. 本次会话从这 3 台专属设备中随机挑选 1 台进行模拟
|
||||
SESSION_UA=${MY_UA_POOL[$RANDOM % 3]}
|
||||
else
|
||||
# 兜底容错机制
|
||||
SESSION_UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
|
||||
fi
|
||||
# 位置锁定:在基准点(比如东京新宿)附近 3 公里内随机生成本次上网的“固定咖啡馆”坐标
|
||||
SESSION_BASE_LAT=$(get_random_coord $BASE_LAT 270)
|
||||
SESSION_BASE_LON=$(get_random_coord $BASE_LON 270)
|
||||
@@ -60,10 +96,36 @@ SESSION_BASE_LON=$(get_random_coord $BASE_LON 270)
|
||||
# 【核心升级】随机决定本次上网深度 (6 - 10 个复合动作,配合高频长效拉伸)
|
||||
TOTAL_ACTIONS=$((6 + RANDOM % 5))
|
||||
|
||||
log "$MODULE_NAME" "INFO " "当前出网 IP: $CURRENT_V4"
|
||||
log "$MODULE_NAME" "INFO " "当前出网 IP: $CURRENT_IP"
|
||||
log "$MODULE_NAME" "INFO " "设备指纹锁定: ${SESSION_UA:0:45}..."
|
||||
log "$MODULE_NAME" "INFO " "虚拟驻留坐标: $SESSION_BASE_LAT, $SESSION_BASE_LON"
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# [V3.2.1 热修复] 网络锚定与协议自适应构建
|
||||
# 强制 curl 绑定网卡,并自动匹配 IPv4/v6 协议,杜绝 curl 冲突报错
|
||||
# -----------------------------------------------------------
|
||||
CURL_BIND_OPT=""
|
||||
DYNAMIC_IP_PREF="-${IP_PREF:-4}" # 默认提取用户配置
|
||||
|
||||
if [[ -n "$BIND_IP" && "$BIND_IP" =~ ^[0-9a-fA-F:\.]+$ ]]; then
|
||||
# [v3.6.3 容错层补丁] 探测物理网卡/虚拟 IP 存活状态
|
||||
RAW_BIND_IP=$(echo "$BIND_IP" | tr -d '[]')
|
||||
if ! ip addr show 2>/dev/null | grep -qw "$RAW_BIND_IP"; then
|
||||
log "$MODULE_NAME" "WARN " "检测到配置的出口 IP ($RAW_BIND_IP) 已丢失,自动降级为系统默认路由出网!"
|
||||
CURL_BIND_OPT=""
|
||||
else
|
||||
CURL_BIND_OPT="--interface $BIND_IP"
|
||||
# 智能探测:带冒号为 V6,带点号为 V4
|
||||
if [[ "$BIND_IP" == *":"* ]]; then
|
||||
DYNAMIC_IP_PREF="-6"
|
||||
log "$MODULE_NAME" "INFO " "底层路由锁定: 绑定 IPv6 出口及协议 ($BIND_IP)"
|
||||
elif [[ "$BIND_IP" == *"."* ]]; then
|
||||
DYNAMIC_IP_PREF="-4"
|
||||
log "$MODULE_NAME" "INFO " "底层路由锁定: 绑定 IPv4 出口及协议 ($BIND_IP)"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- [行为循环模拟] ---
|
||||
for ((i=1; i<=TOTAL_ACTIONS; i++)); do
|
||||
# 模拟真实移动设备拿在手里时的 GPS 信号微抖动 (范围约 10 米)
|
||||
@@ -77,21 +139,22 @@ for ((i=1; i<=TOTAL_ACTIONS; i++)); do
|
||||
# 随机选择一种上网行为
|
||||
ACTION_TYPE=$((1 + RANDOM % 4))
|
||||
|
||||
# [V3.2.1 热修复] 注入 $CURL_BIND_OPT 与 $DYNAMIC_IP_PREF 协议自适应
|
||||
case $ACTION_TYPE in
|
||||
1) # 搜索行为
|
||||
CODE=$(curl -4 -m 15 -s -L -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
|
||||
CODE=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 15 -s -L -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
|
||||
"https://www.google.com/search?q=${ENCODED_KEY}&${LANG_PARAMS}")
|
||||
;;
|
||||
2) # 浏览本土新闻
|
||||
CODE=$(curl -4 -m 15 -s -L -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
|
||||
CODE=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 15 -s -L -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
|
||||
"https://news.google.com/home?${LANG_PARAMS}")
|
||||
;;
|
||||
3) # 地图坐标查询
|
||||
CODE=$(curl -4 -m 15 -s -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
|
||||
"https://www.google.com/maps/search/${ENCODED_KEY}/@${ACTION_LAT},${ACTION_LON},17z?${LANG_PARAMS}")
|
||||
CODE=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 15 -s -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
|
||||
"https://www.google.com/maps/search/$${ENCODED_KEY}/@${ACTION_LAT},${ACTION_LON},17z?${LANG_PARAMS}")
|
||||
;;
|
||||
4) # 触发移动端系统底层位置检测像素
|
||||
CODE=$(curl -4 -m 10 -s -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
|
||||
CODE=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 10 -s -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
|
||||
"https://connectivitycheck.gstatic.com/generate_204")
|
||||
;;
|
||||
esac
|
||||
@@ -107,16 +170,32 @@ for ((i=1; i<=TOTAL_ACTIONS; i++)); do
|
||||
fi
|
||||
done
|
||||
|
||||
# --- [结果纠偏自检] ---
|
||||
# 去掉所有语言参数,进行一次最干净的直连测试
|
||||
FINAL_URL=$(curl -4 -m 15 -s -L -o /dev/null -w "%{url_effective}" https://www.google.com)
|
||||
# --- [结果纠偏自检 (V4.0.3 终极真理版: 穿透 Google 真实 GeoIP 数据库)] ---
|
||||
# 彻底抛弃不可靠的前端 URL 重定向判定 (解决 Issue #35 和 #14 的假阳性问题)
|
||||
# 战术揭秘:Google 与 YouTube 共享绝对一致的底层 GeoIP 库。
|
||||
# 我们直接抓取 YouTube 源码内部的 "GL" (Geo-Location) 环境变量,拿到 Google 视角的 2 位国家代码!
|
||||
|
||||
if [[ "$FINAL_URL" == *"$VALID_URL_SUFFIX"* ]]; then
|
||||
STATUS="✅ 目标区域达成 ($VALID_URL_SUFFIX)"
|
||||
elif [[ "$FINAL_URL" == *"google.com.hk"* ]]; then
|
||||
STATUS="❌ 判定为送中区 (CN/HK)"
|
||||
log "$MODULE_NAME" "INFO " "正在穿透获取 Google 底层真实 GeoIP 锚点..."
|
||||
YT_HTML=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 15 -s -L https://www.youtube.com)
|
||||
|
||||
# 精准正则提取 "GL":"XX" 中的两位字母 (完美兼容所有老旧系统环境)
|
||||
REAL_REGION=$(echo "$YT_HTML" | grep -o '"GL":"[A-Za-z]\{2\}"' | head -n 1 | cut -d'"' -f4 | tr 'a-z' 'A-Z')
|
||||
|
||||
if [ -z "$REAL_REGION" ]; then
|
||||
STATUS="🚨 探针失效 (网络阻断,或已被 Google 验证码/5秒盾拦截)"
|
||||
else
|
||||
STATUS="⚠️ 其他分站跳板 ($FINAL_URL)"
|
||||
# [基准对齐] 提取配置大区 (兼容州级穿透,如 US-TX -> US),并修正英国的 ISO 标准代码
|
||||
TARGET_CC="${REGION_CODE%%-*}"
|
||||
[ "$TARGET_CC" == "UK" ] && TARGET_CC="GB"
|
||||
|
||||
# 终极审判逻辑
|
||||
if [ "$REAL_REGION" == "CN" ]; then
|
||||
STATUS="❌ 严重高危!IP 已被 Google 判定为中国大陆 (送中)!"
|
||||
elif [ "$REAL_REGION" == "$TARGET_CC" ]; then
|
||||
STATUS="✅ 目标区域达成 (底层真实锚定: $REAL_REGION)"
|
||||
else
|
||||
STATUS="⚠️ 区域发生漂移!目标 $TARGET_CC,实际归属 $REAL_REGION (跨区送医/送美)"
|
||||
fi
|
||||
fi
|
||||
|
||||
log "$MODULE_NAME" "SCORE" "自检结论: $STATUS"
|
||||
|
||||
206
core/mod_quality.sh
Executable file
206
core/mod_quality.sh
Executable file
@@ -0,0 +1,206 @@
|
||||
#!/bin/bash
|
||||
# ==========================================================
|
||||
# IP-Sentinel: 深海声呐 (IP 质量全维异步检测模块 v4.0.0)
|
||||
# ==========================================================
|
||||
|
||||
source /opt/ip_sentinel/config.conf
|
||||
|
||||
# ==========================================
|
||||
# 1. 动态网络锚定与协议自适应 (专为多 IP / NAT 架构打造)
|
||||
# ==========================================
|
||||
DYNAMIC_IP_PREF="${IP_PREF:-4}"
|
||||
PROBE_ARGS=("-y" "-j" "-f") # 默认注入: 自动确认、JSON格式、明文无掩码IP
|
||||
|
||||
# 强壮正则:支持 V4, V6 以及带有 [] 护甲的 V6 (兼容多 IP 站群机)
|
||||
if [[ -n "$BIND_IP" && "$BIND_IP" =~ ^[0-9a-fA-F:\[\]\.]+$ ]]; then
|
||||
RAW_BIND_IP=$(echo "$BIND_IP" | tr -d '[]')
|
||||
# 严格探测物理网卡/虚拟 IP 存活状态,防止 IP 漂移导致探针彻底报错
|
||||
if ip addr show 2>/dev/null | grep -qw "$RAW_BIND_IP"; then
|
||||
# 核心:精准锁定多 IP 机器的出口网卡,指哪打哪
|
||||
PROBE_ARGS+=("-i" "$RAW_BIND_IP")
|
||||
|
||||
# 智能识别 V4 / V6,强制覆盖系统默认的 IP_PREF
|
||||
if [[ "$RAW_BIND_IP" == *":"* ]]; then
|
||||
DYNAMIC_IP_PREF="6"
|
||||
elif [[ "$RAW_BIND_IP" == *"."* ]]; then
|
||||
DYNAMIC_IP_PREF="4"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# 补齐协议版本参数 (-4 或 -6)
|
||||
PROBE_ARGS+=("-${DYNAMIC_IP_PREF}")
|
||||
|
||||
# 2. 静默拉取原始数据 (消除短链接 RCE 劫持风险,收编为本地固化执行)
|
||||
PROBE_SCRIPT="/opt/ip_sentinel/core/ip_probe.sh"
|
||||
if [ ! -x "$PROBE_SCRIPT" ]; then
|
||||
# 若本地探针尚未就绪,直接从 GitHub 官方主干拉取底层源码,绕过未知域名
|
||||
curl -sL "https://raw.githubusercontent.com/xykt/IPQuality/main/ip.sh" -o "$PROBE_SCRIPT" 2>/dev/null
|
||||
chmod +x "$PROBE_SCRIPT" 2>/dev/null
|
||||
fi
|
||||
|
||||
# 采用本地执行,将动态参数阵列展开,彻底封死外部投毒与 NAT 死锁陷阱
|
||||
RAW_OUTPUT=$(timeout 180 bash "$PROBE_SCRIPT" "${PROBE_ARGS[@]}" 2>/dev/null)
|
||||
|
||||
# 2. 极致截取 JSON (无视开头的赞助商广告与不可见字符,精准提取)
|
||||
JSON_DATA="{${RAW_OUTPUT#*\{}"
|
||||
|
||||
# 2. 提取基础物理定位与身份特征 (兼作合法性校验)
|
||||
IP_ADDR=$(echo "$JSON_DATA" | jq -r '.Head.IP // empty' 2>/dev/null)
|
||||
|
||||
if [ -z "$IP_ADDR" ]; then
|
||||
curl -s -X POST "${TG_API_URL}" \
|
||||
-d "chat_id=${CHAT_ID}" \
|
||||
-d "parse_mode=Markdown" \
|
||||
-d "text=❌ *深海声呐探测失败*
|
||||
📍 节点:\`${NODE_ALIAS}\`
|
||||
🌐 锁定IP:\`${PUBLIC_IP}\`
|
||||
⚠️ *未收到有效回波。检测源超时或数据解析受阻。*" >/dev/null
|
||||
exit 1
|
||||
fi
|
||||
|
||||
[ -z "$IP_ADDR" ] && IP_ADDR="$PUBLIC_IP"
|
||||
ASN=$(echo "$JSON_DATA" | jq -r '.Info.ASN // "Unknown"' 2>/dev/null)
|
||||
ORG=$(echo "$JSON_DATA" | jq -r '.Info.Organization // "Unknown"' 2>/dev/null)
|
||||
CITY=$(echo "$JSON_DATA" | jq -r '.Info.City.Name // "Unknown"' 2>/dev/null)
|
||||
COUNTRY=$(echo "$JSON_DATA" | jq -r '.Info.Region.Name // "Unknown"' 2>/dev/null)
|
||||
IP_TYPE=$(echo "$JSON_DATA" | jq -r '.Info.Type // "未知属性"' 2>/dev/null)
|
||||
USAGE_TYPE=$(echo "$JSON_DATA" | jq -r '.Type.Usage.IPinfo // "未知场景"' 2>/dev/null)
|
||||
|
||||
# 3. 深度欺诈与信用评估 (各大权威库联查)
|
||||
SCAM_SCORE=$(echo "$JSON_DATA" | jq -r '.Score.SCAMALYTICS // "0"' 2>/dev/null)
|
||||
ABUSE_SCORE=$(echo "$JSON_DATA" | jq -r '.Score.AbuseIPDB // "0"' 2>/dev/null)
|
||||
IPQS_SCORE=$(echo "$JSON_DATA" | jq -r '.Score.IPQS // "0"' 2>/dev/null)
|
||||
IP2L_SCORE=$(echo "$JSON_DATA" | jq -r '.Score.IP2LOCATION // "0"' 2>/dev/null)
|
||||
FRAUD_RISK=$(echo "$JSON_DATA" | jq -r '.Score.ipapi // "0%"' 2>/dev/null)
|
||||
|
||||
# [修复] 清洗 API 阻断返回的 null 值,保障面板整洁
|
||||
[ "$SCAM_SCORE" == "null" ] || [ -z "$SCAM_SCORE" ] && SCAM_SCORE="N/A"
|
||||
[ "$ABUSE_SCORE" == "null" ] || [ -z "$ABUSE_SCORE" ] && ABUSE_SCORE="N/A"
|
||||
[ "$IPQS_SCORE" == "null" ] || [ -z "$IPQS_SCORE" ] && IPQS_SCORE="N/A"
|
||||
[ "$IP2L_SCORE" == "null" ] || [ -z "$IP2L_SCORE" ] && IP2L_SCORE="N/A"
|
||||
[ "$FRAUD_RISK" == "null" ] || [ -z "$FRAUD_RISK" ] && FRAUD_RISK="N/A"
|
||||
|
||||
# 代理/VPN 特征探针 (只要有一家认为是代理,就亮黄灯)
|
||||
IS_PROXY="🟢 干净"
|
||||
if echo "$JSON_DATA" | jq -e '.Factor.Proxy | to_entries | any(.value == true)' >/dev/null 2>&1 || \
|
||||
echo "$JSON_DATA" | jq -e '.Factor.VPN | to_entries | any(.value == true)' >/dev/null 2>&1; then
|
||||
IS_PROXY="🟡 疑似代理/VPN"
|
||||
fi
|
||||
|
||||
# 4. 提取流媒体与 AI 解锁指标 (带解锁类型)
|
||||
parse_media() {
|
||||
local status=$(echo "$JSON_DATA" | jq -r ".Media.$1.Status // \"未知\"" 2>/dev/null)
|
||||
local reg=$(echo "$JSON_DATA" | jq -r ".Media.$1.Region // \"\"" 2>/dev/null)
|
||||
local type=$(echo "$JSON_DATA" | jq -r ".Media.$1.Type // \"\"" 2>/dev/null)
|
||||
|
||||
if [[ "$status" == *"解锁"* ]]; then
|
||||
echo "🟢 ${reg} (${type})"
|
||||
elif [[ "$status" == *"仅"* ]] || [[ "$status" == *"机房"* ]] || [[ "$status" == *"待支持"* ]]; then
|
||||
# 捕捉 Netflix "仅自制"、ChatGPT "仅网页"、TikTok "机房" 等半残状态
|
||||
echo "🟡 ${status} ${reg}"
|
||||
elif [[ "$status" == *"屏蔽"* ]] || [[ "$status" == *"失败"* ]] || [[ "$status" == *"中国"* ]] || [[ "$status" == *"禁"* ]]; then
|
||||
# 捕捉 "屏蔽"、"失败"、"禁会员"、"中国"(送中)
|
||||
echo "🔴 ${status}"
|
||||
else
|
||||
echo "⚪ ${status}"
|
||||
fi
|
||||
}
|
||||
|
||||
NF_STAT=$(parse_media "Netflix")
|
||||
YT_STAT=$(parse_media "Youtube")
|
||||
DP_STAT=$(parse_media "DisneyPlus")
|
||||
TK_STAT=$(parse_media "TikTok")
|
||||
GPT_STAT=$(parse_media "ChatGPT")
|
||||
APV_STAT=$(parse_media "AmazonPrimeVideo")
|
||||
|
||||
# 提取原生 JSON 里的原始状态用于底层隐写回传
|
||||
RAW_NF_STAT=$(echo "$JSON_DATA" | jq -r '.Media.Netflix.Status // "Unknown"' 2>/dev/null)
|
||||
RAW_YT_REG=$(echo "$JSON_DATA" | jq -r '.Media.Youtube.Region // ""' 2>/dev/null)
|
||||
RAW_YT_STAT=$(echo "$JSON_DATA" | jq -r '.Media.Youtube.Status // "Unknown"' 2>/dev/null)
|
||||
|
||||
# 5. 邮局连通性与黑名单
|
||||
PORT25=$(echo "$JSON_DATA" | jq -r '.Mail.Port25 // "false"' 2>/dev/null)
|
||||
[ "$PORT25" == "true" ] && P25_TEXT="✅ 畅通" || P25_TEXT="❌ 封堵"
|
||||
DNS_BLACK=$(echo "$JSON_DATA" | jq -r '.Mail.DNSBlacklist.Blacklisted // "0"' 2>/dev/null)
|
||||
DNS_MARK=$(echo "$JSON_DATA" | jq -r '.Mail.DNSBlacklist.Marked // "0"' 2>/dev/null)
|
||||
|
||||
# 6. “送中” 逻辑判定
|
||||
WARNING_MSG=""
|
||||
# [修复] 官方 JSON 已经去除了方括号,直接匹配 CN 或者状态包含中国
|
||||
if [[ "$RAW_YT_REG" == "CN" ]] || [[ "$RAW_YT_STAT" == *"中国"* ]]; then
|
||||
WARNING_MSG="%0A🚨 **[高危] 该节点已被 Google 判定为中国大陆 (送中)!**%0A"
|
||||
fi
|
||||
|
||||
# 7. 组装情报级 Markdown 战报
|
||||
# 提取本地运行态版本与生成时间戳
|
||||
LOCAL_VER="${AGENT_VERSION:-未知}"
|
||||
CURRENT_TIME=$(date "+%Y-%m-%d %H:%M:%S")
|
||||
# [核心修复] 抛弃本地残缺配置,直接提取探针刚刚实测拿到的真实出口 IP 拼接链接!
|
||||
LINK_IP=$(echo "$IP_ADDR" | tr -d '[]')
|
||||
|
||||
REPORT="🎯 *IP-Sentinel 深海声呐报告*
|
||||
📍 节点:\`${NODE_ALIAS}\`
|
||||
🌐 地址:\`${IP_ADDR}\`${WARNING_MSG}
|
||||
|
||||
*🏢 物理身份与网络属性*
|
||||
\`AS${ASN}\` | \`${ORG}\`
|
||||
**定位:** \`${COUNTRY} - ${CITY}\`
|
||||
**属性:** \`${IP_TYPE}\` | \`${USAGE_TYPE}\`
|
||||
**探针:** ${IS_PROXY}
|
||||
|
||||
*🛡️ 欺诈雷达 (0为最优)*
|
||||
• **Scamalytics:** \`${SCAM_SCORE}/100\`
|
||||
• **AbuseIPDB:** \`${ABUSE_SCORE}/100\`
|
||||
• **IPQS:** \`${IPQS_SCORE}/100\`
|
||||
• **IP2Location:** \`${IP2L_SCORE}/100\`
|
||||
• **IPAPI 风险率:** \`${FRAUD_RISK}\`
|
||||
|
||||
*🎬 核心业务解锁*
|
||||
• **YouTube:** ${YT_STAT}
|
||||
• **Netflix:** ${NF_STAT}
|
||||
• **Disney+:** ${DP_STAT}
|
||||
• **PrimeVideo:** ${APV_STAT}
|
||||
• **TikTok:** ${TK_STAT}
|
||||
• **ChatGPT:** ${GPT_STAT}
|
||||
|
||||
*✉️ 邮局与污染度*
|
||||
• **25 端口出站:** ${P25_TEXT}
|
||||
• **DNS 污染库:** 严重 \`${DNS_BLACK}\` | 轻微 \`${DNS_MARK}\`
|
||||
|
||||
_👉 [🔍 详细信用图谱直达 (Scamalytics)](https://scamalytics.com/ip/${LINK_IP})_
|
||||
|
||||
⏱️ \`${CURRENT_TIME}\` | ⚙️ \`v${LOCAL_VER}\`"
|
||||
|
||||
# [修复] 剥离显示层的 N/A,确保传给 Master 趋势数据库的是纯数字 (无效则记为0)
|
||||
SAFE_SCAM_SCORE=$(echo "$SCAM_SCORE" | tr -cd '0-9')
|
||||
[ -z "$SAFE_SCAM_SCORE" ] && SAFE_SCAM_SCORE="0"
|
||||
|
||||
# [v4.0.2 扩容] 提取 Google(基于YouTube) 和 ChatGPT 的原生状态
|
||||
RAW_GOOG_STAT="${RAW_YT_REG:-$RAW_YT_STAT}"
|
||||
[ -z "$RAW_GOOG_STAT" ] && RAW_GOOG_STAT="未知"
|
||||
RAW_GPT_STAT=$(echo "$JSON_DATA" | jq -r '.Media.ChatGPT.Status // "未知"' 2>/dev/null)
|
||||
|
||||
# [修复] 废除会导致中文 UTF-8 字节被劈裂(产生乱码 )的 awk 暴力截断。
|
||||
# 原始状态文本极短(如"解锁"、"屏蔽"、"US"),只需洗掉隐形换行符即可安全传输。
|
||||
S_GOOG=$(echo "$RAW_GOOG_STAT" | tr -d '\n\r ')
|
||||
S_NF=$(echo "$RAW_NF_STAT" | tr -d '\n\r ')
|
||||
S_GPT=$(echo "$RAW_GPT_STAT" | tr -d '\n\r ')
|
||||
CB_DATA="svq|${NODE_NAME}|${SAFE_SCAM_SCORE}|${S_GOOG}|${S_NF}|${S_GPT}"
|
||||
|
||||
# 8. 挂载内联键盘并直送指挥部
|
||||
JSON_PAYLOAD=$(jq -n \
|
||||
--arg cid "$CHAT_ID" \
|
||||
--arg txt "$REPORT" \
|
||||
--arg cb "$CB_DATA" \
|
||||
'{
|
||||
chat_id: $cid,
|
||||
text: $txt,
|
||||
parse_mode: "Markdown",
|
||||
disable_web_page_preview: true,
|
||||
reply_markup: {
|
||||
inline_keyboard: [[{text: "📥 将本次体检录入趋势库", callback_data: $cb}]]
|
||||
}
|
||||
}')
|
||||
|
||||
curl -s -X POST "${TG_API_URL}" -H "Content-Type: application/json" -d "$JSON_PAYLOAD" >/dev/null
|
||||
163
core/mod_trust.sh
Executable file
163
core/mod_trust.sh
Executable file
@@ -0,0 +1,163 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: mod_trust.sh (IP 信用净化模块 - 动态锚点版)
|
||||
# 核心功能: 动态扫描本地 LBS 冷数据,提取权威白名单,执行流量净化
|
||||
# ==========================================================
|
||||
|
||||
INSTALL_DIR="/opt/ip_sentinel"
|
||||
CONFIG_FILE="${INSTALL_DIR}/config.conf"
|
||||
UA_FILE="${INSTALL_DIR}/data/user_agents.txt"
|
||||
# 你的 GitHub 仓库 Raw 数据直链前缀
|
||||
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||
# 临时改为私库地址用于测试
|
||||
# REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/v3.6.2-rc"
|
||||
|
||||
# 1. 基础环境校验
|
||||
[ ! -f "$CONFIG_FILE" ] && exit 1
|
||||
source "$CONFIG_FILE"
|
||||
|
||||
REGION=${REGION_CODE:-"US"}
|
||||
LOG_FILE="${INSTALL_DIR}/logs/sentinel.log"
|
||||
|
||||
# 2. 动态获取配置 (V3 拓扑自适应与兜底)
|
||||
# 利用 find 穿透多级子目录,自动抓取安装时落地的那份专属 json 文件
|
||||
REGION_JSON_FILE=$(find "${INSTALL_DIR}/data/regions" -name "*.json" 2>/dev/null | head -n 1)
|
||||
|
||||
# 兼容旧节点兜底:如果本地真没找到 json,回退到拉取云端通用大区配置
|
||||
if [ -z "$REGION_JSON_FILE" ] || [ ! -f "$REGION_JSON_FILE" ]; then
|
||||
REGION_JSON_FILE="${INSTALL_DIR}/data/regions/${REGION}.json"
|
||||
mkdir -p "${INSTALL_DIR}/data/regions"
|
||||
curl -${IP_PREF:-4} -sL "${REPO_RAW_URL}/data/regions/${REGION}.json" -o "$REGION_JSON_FILE"
|
||||
fi
|
||||
|
||||
# 使用 jq 将 json 中的网址数组安全地读入 Bash 数组
|
||||
if [ -f "$REGION_JSON_FILE" ]; then
|
||||
mapfile -t TRUST_URLS < <(jq -r '.trust_module.white_urls[]' "$REGION_JSON_FILE" 2>/dev/null)
|
||||
fi
|
||||
|
||||
# 兜底:如果仓库挂了或者解析失败,提供国际通用白名单
|
||||
if [ ${#TRUST_URLS[@]} -eq 0 ]; then
|
||||
TRUST_URLS=("https://en.wikipedia.org/wiki/Special:Random" "https://www.apple.com/" "https://www.microsoft.com/")
|
||||
fi
|
||||
|
||||
# 3. 日志规范化 (v3.4.0 引入版本探针)
|
||||
log_msg() {
|
||||
local TYPE=$1
|
||||
local MSG=$2
|
||||
local TIME=$(date "+%Y-%m-%d %H:%M:%S")
|
||||
# [v3.4.0 核心] 提取当前配置中的版本锚点
|
||||
local local_ver="${AGENT_VERSION:-未知}"
|
||||
|
||||
# 日志格式注入 [版本号] 追踪标识,保持对齐
|
||||
echo "[$TIME] [v%-5s] [%-5s] [Trust ] [$REGION] $MSG" | sed "s/%-5s/$local_ver/;s/%-5s/$TYPE/" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
# 4. 锁定单次会话指纹
|
||||
# -----------------------------------------------------------
|
||||
# [V3.1.5] 哈希锚定法 (Hash-Seeded Persona)
|
||||
# 利用 IP 算力固定 3 个永久化专属指纹,破除僵尸网络同质化特征
|
||||
# -----------------------------------------------------------
|
||||
if [ -f "$UA_FILE" ]; then
|
||||
mapfile -t UA_POOL < <(grep -v '^$' "$UA_FILE")
|
||||
TOTAL_UA=${#UA_POOL[@]}
|
||||
|
||||
if [ "$TOTAL_UA" -gt 0 ]; then
|
||||
# [v3.3.1修改] 优先使用固化的公网 IP 作为哈希种子,防止 NAT 节点指纹同质化
|
||||
SEED=$(echo -n "${PUBLIC_IP:-${BIND_IP:-127.0.0.1}}" | cksum | awk '{print $1}')
|
||||
|
||||
# 利用确定的种子,在全球 4000 的库中,计算出本机的 3 个绝对专属坐标
|
||||
IDX1=$(( SEED % TOTAL_UA ))
|
||||
IDX2=$(( (SEED * 17) % TOTAL_UA ))
|
||||
IDX3=$(( (SEED * 31) % TOTAL_UA ))
|
||||
|
||||
# 将专属坐标映射为专属设备库
|
||||
MY_UA_POOL=("${UA_POOL[$IDX1]}" "${UA_POOL[$IDX2]}" "${UA_POOL[$IDX3]}")
|
||||
|
||||
# 本次会话从这 3 台专属设备中随机挑选 1 台 (模拟真实的家庭多设备环境)
|
||||
CURRENT_UA=${MY_UA_POOL[$RANDOM % 3]}
|
||||
else
|
||||
# 兜底容错
|
||||
CURRENT_UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
|
||||
fi
|
||||
else
|
||||
CURRENT_UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
|
||||
fi
|
||||
|
||||
# ==========================================
|
||||
# 🚀 净化行动开始
|
||||
# ==========================================
|
||||
log_msg "START" "========== 启动区域 IP 信用净化会话 =========="
|
||||
log_msg "INFO " "已载入 [${REGION}] 区域白名单,配置库条目: ${#TRUST_URLS[@]} 个"
|
||||
log_msg "INFO " "已锁定本地伪装指纹: $(echo $CURRENT_UA | cut -d' ' -f1-2)..."
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# [V3.2.1 热修复] 网络锚定与协议自适应构建
|
||||
# 强制 curl 绑定网卡,并自动匹配 IPv4/v6 协议,杜绝 curl 冲突报错
|
||||
# -----------------------------------------------------------
|
||||
CURL_BIND_OPT=""
|
||||
DYNAMIC_IP_PREF="-${IP_PREF:-4}" # 默认提取用户配置
|
||||
|
||||
if [[ -n "$BIND_IP" && "$BIND_IP" =~ ^[0-9a-fA-F:\.]+$ ]]; then
|
||||
# [v3.6.3 容错层补丁] 探测物理网卡/虚拟 IP 存活状态
|
||||
RAW_BIND_IP=$(echo "$BIND_IP" | tr -d '[]')
|
||||
if ! ip addr show 2>/dev/null | grep -qw "$RAW_BIND_IP"; then
|
||||
log_msg "WARN " "检测到配置的出口 IP ($RAW_BIND_IP) 已丢失,自动降级为系统默认路由出网!"
|
||||
CURL_BIND_OPT=""
|
||||
else
|
||||
CURL_BIND_OPT="--interface $BIND_IP"
|
||||
# 智能探测:带冒号为 V6,带点号为 V4
|
||||
if [[ "$BIND_IP" == *":"* ]]; then
|
||||
DYNAMIC_IP_PREF="-6"
|
||||
log_msg "INFO " "底层路由锁定: 绑定 IPv6 出口及协议 ($BIND_IP)"
|
||||
elif [[ "$BIND_IP" == *"."* ]]; then
|
||||
DYNAMIC_IP_PREF="-4"
|
||||
log_msg "INFO " "底层路由锁定: 绑定 IPv4 出口及协议 ($BIND_IP)"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
STEP_COUNT=$((RANDOM % 4 + 3))
|
||||
SUCCESS_INJECT=0
|
||||
|
||||
for ((i=1; i<=STEP_COUNT; i++)); do
|
||||
# 随机抽取本地区域权威网址
|
||||
TARGET_URL=${TRUST_URLS[$RANDOM % ${#TRUST_URLS[@]}]}
|
||||
|
||||
# [v3.0.1修复] 注入高权重流量时,强制从绑定的 IPv4 或 IPv6 隧道出网
|
||||
# [V3.2.1 热修复] 注入 $CURL_BIND_OPT 与 $DYNAMIC_IP_PREF 协议自适应
|
||||
HTTP_CODE=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -A "$CURRENT_UA" \
|
||||
-H "Accept: text/html,application/xhtml+xml;q=0.9,image/avif,image/webp,*/*;q=0.8" \
|
||||
-H "Accept-Language: en-US,en;q=0.9" \
|
||||
-H "Sec-Fetch-Dest: document" \
|
||||
-H "Sec-Fetch-Mode: navigate" \
|
||||
-H "Upgrade-Insecure-Requests: 1" \
|
||||
--compressed \
|
||||
-s -o /dev/null -w "%{http_code}" -m 15 "$TARGET_URL")
|
||||
|
||||
# 扩大 HTTP 状态码容错区间:包含所有 20x (如亚马逊的 202) 和 30x 重定向
|
||||
if [[ "$HTTP_CODE" =~ ^(20[0-9]|30[1-8])$ ]]; then
|
||||
log_msg "EXEC " "动作[$i/$STEP_COUNT]完成 | 状态: $HTTP_CODE | 注入: $TARGET_URL"
|
||||
((SUCCESS_INJECT++))
|
||||
else
|
||||
log_msg "EXEC " "动作[$i/$STEP_COUNT]异常 | 状态: $HTTP_CODE | 阻拦: $TARGET_URL"
|
||||
fi
|
||||
|
||||
if [ $i -lt $STEP_COUNT ]; then
|
||||
SLEEP_TIME=$((RANDOM % 76 + 45))
|
||||
log_msg "WAIT " "正在浏览本地高权重页面,模拟停留 $SLEEP_TIME 秒..."
|
||||
sleep $SLEEP_TIME
|
||||
fi
|
||||
done
|
||||
|
||||
# ==========================================
|
||||
# 📊 结论判定与输出
|
||||
# ==========================================
|
||||
if [ "$SUCCESS_INJECT" -ge $((STEP_COUNT / 2)) ]; then
|
||||
log_msg "SCORE" "自检结论: ✅ 信用净化完成 (已成功注入 $SUCCESS_INJECT 条无害流量)"
|
||||
else
|
||||
log_msg "SCORE" "自检结论: ❌ 净化受阻 (部分站点拦截或网络超时)"
|
||||
fi
|
||||
|
||||
log_msg "END " "========== 会话结束,释放进程 =========="
|
||||
log_msg "INFO " "系统级调度完毕,信任因子持续积累中..."
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: runner.sh (IP-Sentinel 主控调度引擎)
|
||||
# 核心功能: 防并发随机延迟启动、加载本地固化配置、调度业务模块
|
||||
# 脚本名称: runner.sh (IP-Sentinel 主控调度引擎 - 动态锚点版)
|
||||
# 核心功能: 防并发延迟启动、功能开关(Feature Flag)自适应、多模块概率轮盘调度
|
||||
# ==========================================================
|
||||
|
||||
INSTALL_DIR="/opt/ip_sentinel"
|
||||
@@ -15,34 +15,86 @@ if [ ! -f "$CONFIG_FILE" ]; then
|
||||
fi
|
||||
source "$CONFIG_FILE"
|
||||
|
||||
# 2. 全局日志写入函数 (导出给子进程共享使用)
|
||||
# ================== [新增: 文件排他锁,防止并发重入引发内存雪崩] ==================
|
||||
exec 200>"/tmp/ip_sentinel_runner.lock"
|
||||
if ! flock -n 200; then
|
||||
echo "[$(date)] ⚠️ 上一轮巡逻任务尚未结束,本次触发自动取消。" >> "$LOG_FILE"
|
||||
exit 0
|
||||
fi
|
||||
# ==================================================================================
|
||||
|
||||
# 2. 全局日志写入函数 (导出给子进程共享使用,v3.4.0 引入版本探针)
|
||||
log() {
|
||||
local module=$1
|
||||
local level=$2
|
||||
local msg=$3
|
||||
# [v3.4.0 核心] 提取当前配置中的版本锚点
|
||||
local local_ver="${AGENT_VERSION:-未知}"
|
||||
|
||||
# 保证日志目录存在
|
||||
mkdir -p "${INSTALL_DIR}/logs"
|
||||
printf "[$(date '+%Y-%m-%d %H:%M:%S')] [%-5s] [%-7s] [%s] %s\n" "$level" "$module" "$REGION_CODE" "$msg" >> "$LOG_FILE"
|
||||
|
||||
# 日志格式注入 [版本号] 追踪标识
|
||||
local core_msg=$(printf "[v%-5s] [%-5s] [%-7s] [%s] %s" "$local_ver" "$level" "$module" "$REGION_CODE" "$msg")
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $core_msg" >> "$LOG_FILE"
|
||||
|
||||
# 强制推送到 Systemd Journal (如果系统支持)
|
||||
if command -v logger >/dev/null 2>&1; then
|
||||
logger -t ip-sentinel "$core_msg"
|
||||
else
|
||||
# 降级输出到 stdout,让 Systemd 捕获
|
||||
echo "$core_msg"
|
||||
fi
|
||||
}
|
||||
export -f log
|
||||
export CONFIG_FILE INSTALL_DIR
|
||||
|
||||
# 3. 防僵尸网络特征 (Cron Jitter) - 核心隐蔽逻辑
|
||||
# 【核心升级】配合每 30 分钟的调度周期,将随机休眠控制在 0 到 180 秒 (3分钟) 内,彻底打散全球并发请求
|
||||
JITTER_TIME=$((RANDOM % 180))
|
||||
log "SYSTEM" "INFO" "主控引擎被 Cron 唤醒,进入防并发随机休眠状态: ${JITTER_TIME} 秒..."
|
||||
sleep $JITTER_TIME
|
||||
|
||||
# 4. 唤醒并调度业务模块
|
||||
log "SYSTEM" "INFO" "休眠结束,开始执行养护任务..."
|
||||
|
||||
# 调度 Google 模块
|
||||
if [ -x "${INSTALL_DIR}/core/mod_google.sh" ]; then
|
||||
log "SYSTEM" "INFO" "加载子模块: Google 业务模拟"
|
||||
# 核心降耗逻辑:使用 nice -n 19 赋予进程最低 CPU 优先级,绝不抢占 VPS 正常业务的资源
|
||||
nice -n 19 bash "${INSTALL_DIR}/core/mod_google.sh"
|
||||
# 配合每 30 分钟的调度周期,将随机休眠控制在 0 到 180 秒内,彻底打散全球并发请求
|
||||
if [ -t 1 ]; then
|
||||
log "SYSTEM" "INFO " "💻 检测到人工终端干预,跳过静默休眠,立即执行任务!"
|
||||
else
|
||||
log "SYSTEM" "ERROR" "未找到可执行的 Google 模块"
|
||||
JITTER_TIME=$((RANDOM % 180))
|
||||
log "SYSTEM" "INFO " "⏱️ 主控引擎由后台唤醒,进入防并发随机休眠状态: ${JITTER_TIME} 秒..."
|
||||
sleep $JITTER_TIME
|
||||
fi
|
||||
|
||||
# 4. 唤醒并读取功能开关,执行智能调度 (Feature Flag)
|
||||
log "SYSTEM" "INFO" "休眠结束,开始计算本轮任务轮盘..."
|
||||
|
||||
TARGET_MOD=""
|
||||
MOD_NAME=""
|
||||
|
||||
# 智能轮盘赌算法
|
||||
if [ "$ENABLE_GOOGLE" == "true" ] && [ "$ENABLE_TRUST" == "true" ]; then
|
||||
# 双管齐下: 70% 概率跑 Google 稳固定位,30% 概率跑 Trust 洗刷风控分
|
||||
ROLL=$((RANDOM % 100 + 1))
|
||||
if [ $ROLL -le 70 ]; then
|
||||
TARGET_MOD="mod_google.sh"
|
||||
MOD_NAME="Google 区域纠偏"
|
||||
else
|
||||
TARGET_MOD="mod_trust.sh"
|
||||
MOD_NAME="IP 信用净化"
|
||||
fi
|
||||
elif [ "$ENABLE_GOOGLE" == "true" ]; then
|
||||
TARGET_MOD="mod_google.sh"
|
||||
MOD_NAME="Google 区域纠偏"
|
||||
elif [ "$ENABLE_TRUST" == "true" ]; then
|
||||
TARGET_MOD="mod_trust.sh"
|
||||
MOD_NAME="IP 信用净化"
|
||||
else
|
||||
log "SYSTEM" "WARN" "节点未开启任何养护模块,跳过本轮执行。"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 5. 拉起选定的业务模块
|
||||
if [ -n "$TARGET_MOD" ] && [ -x "${INSTALL_DIR}/core/${TARGET_MOD}" ]; then
|
||||
log "SYSTEM" "INFO" "命中触发条件,加载并执行子模块: ${MOD_NAME}"
|
||||
# 核心降耗逻辑:使用 nice -n 19 赋予进程最低 CPU 优先级,绝不抢占 VPS 正常业务的资源
|
||||
# [安全修复] 注入 200>&-,强行关闭子进程对排他锁的继承权!防止子进程假死导致全局死锁
|
||||
nice -n 19 bash "${INSTALL_DIR}/core/${TARGET_MOD}" 200>&-
|
||||
else
|
||||
log "SYSTEM" "ERROR" "配置了模块 ${MOD_NAME},但未找到对应的可执行脚本: ${TARGET_MOD}"
|
||||
fi
|
||||
|
||||
log "SYSTEM" "INFO" "本轮所有模块调度完毕,哨兵继续隐蔽待命。"
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: tg_daemon.sh (Telegram 互动监听守护进程)
|
||||
# 核心功能: 极低功耗长轮询监听 TG 指令,实现远程控制
|
||||
# 脚本名称: tg_daemon.sh (Telegram 互动监听守护进程 - 动态锚点版)
|
||||
# 核心功能: 极低功耗长轮询监听、节点溯源、版本继承
|
||||
# ==========================================================
|
||||
|
||||
INSTALL_DIR="/opt/ip_sentinel"
|
||||
@@ -16,6 +16,11 @@ source "$CONFIG_FILE"
|
||||
# 如果没有配置 TG 机器人,则安静退出守护进程
|
||||
[ -z "$TG_TOKEN" ] || [ -z "$CHAT_ID" ] && exit 0
|
||||
|
||||
# [核心: 动态版本锚点与防撞甲身份载入]
|
||||
LOCAL_VER="${AGENT_VERSION:-未知}"
|
||||
IP_HASH=$(echo "${PUBLIC_IP:-127.0.0.1}" | md5sum | cut -c 1-4 | tr 'a-z' 'A-Z')
|
||||
NODE_NAME="$(hostname | cut -c 1-10)-${IP_HASH}"
|
||||
|
||||
# 2. 初始化消息偏移量 (Offset) 记录文件,防止重启后重复处理老消息
|
||||
OFFSET=0
|
||||
[ -f "$OFFSET_FILE" ] && OFFSET=$(cat "$OFFSET_FILE")
|
||||
@@ -44,20 +49,20 @@ while true; do
|
||||
if [ "$MSG_CHAT_ID" == "$CHAT_ID" ]; then
|
||||
case "$MSG_TEXT" in
|
||||
"/run")
|
||||
send_msg "🚀 **[指令下达]** 正在后台立即触发 IP 养护任务..."
|
||||
send_msg "🚀 **[${NODE_NAME}]** 正在后台触发 IP 养护任务 (v${LOCAL_VER})..."
|
||||
# 使用 nohup 另起后台独立进程运行,防止阻塞当前监听器的循环
|
||||
nohup bash "${INSTALL_DIR}/core/mod_google.sh" >/dev/null 2>&1 &
|
||||
;;
|
||||
"/log")
|
||||
LOG_DATA=$(tail -n 15 "${INSTALL_DIR}/logs/sentinel.log")
|
||||
send_msg "📄 **[最近 15 行系统日志]**%0A\`\`\`log%0A${LOG_DATA}%0A\`\`\`"
|
||||
send_msg "📄 **[${NODE_NAME}] 实时日志 (v${LOCAL_VER}):**%0A\`\`\`log%0A${LOG_DATA}%0A\`\`\`"
|
||||
;;
|
||||
"/report")
|
||||
# 触发生成一次战报
|
||||
bash "${INSTALL_DIR}/core/tg_report.sh"
|
||||
;;
|
||||
"/help"|"/start")
|
||||
HELP_MSG="🛡️ **IP-Sentinel 控制台**%0A/run - 立刻执行一次养护%0A/log - 抓取最新运行日志%0A/report - 手动生成统计简报"
|
||||
HELP_MSG="🛡️ **IP-Sentinel 边缘控制台**%0A📍 节点: \`${NODE_NAME}\`%0A🔖 版本: \`v${LOCAL_VER}\`%0A%0A/run - 立刻执行一次养护%0A/log - 抓取最新运行日志%0A/report - 手动生成统计简报"
|
||||
send_msg "$HELP_MSG"
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: tg_report.sh (Telegram 每日战报模块 V5.3 缝合加强版)
|
||||
# 核心功能: 分析日志并推送 24 小时统计数据到 TG (修复 Markdown 断联Bug)
|
||||
# 脚本名称: tg_report.sh (Telegram 每日战报模块 - 动态锚点版)
|
||||
# 核心功能: 适配 Feature Flag 架构,按需展示独立统计数据,OTA 更新预警
|
||||
# ==========================================================
|
||||
|
||||
INSTALL_DIR="/opt/ip_sentinel"
|
||||
@@ -18,96 +18,206 @@ if [ -z "$TG_TOKEN" ] || [ -z "$CHAT_ID" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 2. 节点元数据抓取
|
||||
NODE_NAME=$(hostname | cut -c 1-15)
|
||||
CURRENT_IP=$(curl -4 -s -m 5 api.ip.sb/ip || echo "Unknown")
|
||||
# 2. 节点元数据抓取 (v3.2.2 协议自适应与多级容灾版)
|
||||
# [v3.5.2 核心: 引入双轨身份架构]
|
||||
if [ -z "$NODE_NAME" ]; then
|
||||
IP_HASH=$(echo "${PUBLIC_IP:-127.0.0.1}" | md5sum | cut -c 1-4 | tr 'a-z' 'A-Z')
|
||||
NODE_NAME="$(hostname | cut -c 1-10)-${IP_HASH}"
|
||||
fi
|
||||
NODE_ALIAS="${NODE_ALIAS:-$NODE_NAME}"
|
||||
|
||||
# --- [防线 1: 底层路由锁定与协议自适应] ---
|
||||
CURL_BIND_OPT=""
|
||||
DYNAMIC_IP_PREF="-${IP_PREF:-4}"
|
||||
|
||||
if [[ -n "$BIND_IP" && "$BIND_IP" =~ ^[0-9a-fA-F:\.]+$ ]]; then
|
||||
# [v3.6.3 容错层补丁] 探测物理网卡/虚拟 IP 存活状态
|
||||
RAW_BIND_IP=$(echo "$BIND_IP" | tr -d '[]')
|
||||
if ! ip addr show 2>/dev/null | grep -qw "$RAW_BIND_IP"; then
|
||||
CURL_BIND_OPT=""
|
||||
else
|
||||
CURL_BIND_OPT="--interface $BIND_IP"
|
||||
if [[ "$BIND_IP" == *":"* ]]; then
|
||||
DYNAMIC_IP_PREF="-6"
|
||||
elif [[ "$BIND_IP" == *"."* ]]; then
|
||||
DYNAMIC_IP_PREF="-4"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# 多节点容灾探测出口 IP (注入协议自适应)
|
||||
CURRENT_IP=$( (curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -s -m 5 api.ip.sb/ip || curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -s -m 5 ifconfig.me) 2>/dev/null | tr -d '[:space:]' )
|
||||
# [v3.3.1 修改] 强制兜底:如果外部 API 挂了,优先使用固化的对外公网面孔 (兼容 NAT 机的空 BIND_IP)
|
||||
[ -z "$CURRENT_IP" ] && CURRENT_IP="${PUBLIC_IP:-$BIND_IP}"
|
||||
|
||||
# 为可能获取到的 IPv6 自动添加方括号护甲
|
||||
[[ "$CURRENT_IP" == *":"* ]] && [[ "$CURRENT_IP" != *"["* ]] && CURRENT_IP="[${CURRENT_IP}]"
|
||||
|
||||
# --- [防线 2: 多级 ISP 容灾探针链路] ---
|
||||
ISP_INFO=""
|
||||
|
||||
# 探针 A: 纯文本 API (免 jq,极速稳定)
|
||||
ISP_INFO=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -s -m 5 ipinfo.io/org 2>/dev/null)
|
||||
|
||||
# 探针 B: 备用纯文本 API
|
||||
if [ -z "$ISP_INFO" ] || [[ "$ISP_INFO" == *"error"* ]]; then
|
||||
ISP_INFO=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -s -m 5 ip-api.com/line/?fields=isp 2>/dev/null)
|
||||
fi
|
||||
|
||||
# 探针 C: 原版的 JSON API (需要 jq 兜底)
|
||||
if [ -z "$ISP_INFO" ] || [[ "$ISP_INFO" == *"error"* ]]; then
|
||||
if command -v jq &> /dev/null; then
|
||||
ISP_INFO=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -s -m 5 api.ip.sb/geoip | jq -r '.organization' 2>/dev/null)
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- [防线 3: 数据清洗 (遵循底层共识原则)] ---
|
||||
# 剔除 ipinfo 返回的开头 AS 号 (例如 "AS137535 JT TELECOM" -> "JT TELECOM")
|
||||
ISP_INFO=$(echo "$ISP_INFO" | sed -E 's/^AS[0-9]+ //')
|
||||
|
||||
# 最终兜底判断
|
||||
[ -z "$ISP_INFO" ] || [ "$ISP_INFO" == "null" ] && ISP_INFO="未知 ISP"
|
||||
|
||||
# 智能判断 IP 属性 (通过检查 ISP 标识)
|
||||
ISP_INFO=$(curl -4 -s -m 5 api.ip.sb/geoip | jq -r '.organization' 2>/dev/null)
|
||||
if [[ "$ISP_INFO" == *"Cloudflare"* ]]; then
|
||||
IP_TYPE="Cloudflare Warp 🛰️"
|
||||
else
|
||||
IP_TYPE="Native 原生网卡 🏠"
|
||||
IP_TYPE="$ISP_INFO 🏠"
|
||||
fi
|
||||
|
||||
# 3. 截取过去 24 小时的日志
|
||||
LOG_CONTENT=$(find "$LOG_FILE" -mtime -1 -exec cat {} \; 2>/dev/null)
|
||||
# 动态国旗
|
||||
case "$REGION_CODE" in
|
||||
"JP") FLAG="🇯🇵" ;;
|
||||
"US") FLAG="🇺🇸" ;;
|
||||
"DE") FLAG="🇩🇪" ;;
|
||||
"SG") FLAG="🇸🇬" ;;
|
||||
"HK") FLAG="🇭🇰" ;;
|
||||
"GB"|"UK") FLAG="🇬🇧" ;;
|
||||
"AU") FLAG="🇦🇺" ;;
|
||||
*) FLAG="🌐" ;;
|
||||
esac
|
||||
|
||||
# 3. 截取过去 24 小时的日志 (每天48次轮询,保留最新 1000 行足以覆盖单日战报)
|
||||
LOG_CONTENT=$(tail -n 1000 "$LOG_FILE" 2>/dev/null)
|
||||
|
||||
if [ -z "$LOG_CONTENT" ]; then
|
||||
# 修复了换行问题,统一用 EOF 块构造
|
||||
read -r -d '' MSG <<EOT
|
||||
🛑 **[IP-Sentinel] 告警:节点异常**
|
||||
----------------------------
|
||||
📍 **节点名称**: \`${NODE_NAME}\`
|
||||
📍 **节点名称**: \`${NODE_ALIAS}\`
|
||||
⚠️ **警告**: 过去 24 小时无运行日志!
|
||||
🛠️ **建议**: 节点可能刚部署完毕,请手动执行一次 [执行深度伪装]。
|
||||
🛠️ **建议**: 节点可能刚部署完毕,请在面板手动执行一次养护动作。
|
||||
EOT
|
||||
else
|
||||
# 4. 数据精准分析
|
||||
TOTAL_SESSIONS=$(echo "$LOG_CONTENT" | grep "\[START\]" -c)
|
||||
SUCCESS_COUNT=$(echo "$LOG_CONTENT" | grep "✅" -c)
|
||||
FAILED_COUNT=$(echo "$LOG_CONTENT" | grep "❌" -c)
|
||||
UNKNOWN_COUNT=$(echo "$LOG_CONTENT" | grep "⚠️" -c)
|
||||
# ==========================================
|
||||
# 4. 动态模块数据分析 (核心升级)
|
||||
# ==========================================
|
||||
|
||||
# 提取最近一次运行的快照 (智能识别所属模块)
|
||||
LAST_LOG_LINE=$(echo "$LOG_CONTENT" | grep "\[SCORE\]" | tail -n 1)
|
||||
LAST_TIME=$(echo "$LAST_LOG_LINE" | awk '{print $1,$2}' | tr -d '[]')
|
||||
LAST_MOD=$(echo "$LAST_LOG_LINE" | awk '{print $4}' | tr -d '[]')
|
||||
LAST_SCORE=$(echo "$LAST_LOG_LINE" | awk -F'自检结论: ' '{print $2}')
|
||||
|
||||
# 提取最近一次运行的时间和结论文本
|
||||
# (⚠️ 核心 Bug 修复: 用 awk 切割并过滤掉中括号 [],防止触发 TG Markdown 语法错误导致消息丢弃)
|
||||
LAST_TIME=$(echo "$LOG_CONTENT" | grep "\[END" | tail -n 1 | awk '{print $1,$2}' | tr -d '[]')
|
||||
LAST_SCORE=$(echo "$LOG_CONTENT" | grep "\[SCORE\]" | tail -n 1 | awk -F'自检结论: ' '{print $2}' | tr -d '[]')
|
||||
|
||||
# 计算成功率
|
||||
if [ "$TOTAL_SESSIONS" -gt 0 ]; then
|
||||
RATE=$(awk "BEGIN {printf \"%.1f\", ($SUCCESS_COUNT/$TOTAL_SESSIONS)*100}")
|
||||
else
|
||||
RATE=0
|
||||
fi
|
||||
|
||||
# 状态表情逻辑:成功率 100% 显绿色,0% 显红色,中间显黄色
|
||||
if [ "$SUCCESS_COUNT" -eq "$TOTAL_SESSIONS" ] && [ "$TOTAL_SESSIONS" -gt 0 ]; then
|
||||
STATUS_EMOJI="🟢 隐匿完美"
|
||||
elif [ "$SUCCESS_COUNT" -gt 0 ]; then
|
||||
STATUS_EMOJI="🟡 伪装拉锯中"
|
||||
else
|
||||
STATUS_EMOJI="🔴 目标已暴露"
|
||||
fi
|
||||
|
||||
# 动态国旗
|
||||
case "$REGION_CODE" in
|
||||
"JP") FLAG="🇯🇵" ;;
|
||||
"US") FLAG="🇺🇸" ;;
|
||||
"DE") FLAG="🇩🇪" ;;
|
||||
"SG") FLAG="🇸🇬" ;;
|
||||
*) FLAG="🌐" ;;
|
||||
esac
|
||||
|
||||
# 5. 组装 Markdown 消息体 (吸收了老版本的优点)
|
||||
read -r -d '' MSG <<EOT
|
||||
📊 **IP-Sentinel 每日简报 (${FLAG} ${REGION_NAME})**
|
||||
# 开始组装战报头部
|
||||
MSG="📊 **IP-Sentinel 每日简报 (${FLAG} ${REGION_NAME})**
|
||||
----------------------------
|
||||
📍 **节点名称**: \`${NODE_NAME}\`
|
||||
📍 **节点名称**: \`${NODE_ALIAS}\`
|
||||
📡 **出口 IP**: \`${CURRENT_IP}\`
|
||||
🛡️ **IP 属性**: ${IP_TYPE}
|
||||
🔰 **当前状态**: ${STATUS_EMOJI}
|
||||
🛡️ **IP 属性**: ${IP_TYPE}"
|
||||
|
||||
📅 **24H 统计数据**:
|
||||
🚀 执行总数: ${TOTAL_SESSIONS} 次
|
||||
✅ 成功伪装: ${SUCCESS_COUNT} 次
|
||||
❌ 判定送中: ${FAILED_COUNT} 次
|
||||
⚠️ 未知跳转: ${UNKNOWN_COUNT} 次
|
||||
📈 综合胜率: **${RATE}%**
|
||||
# --- [分析块 1: Google 纠偏模块] ---
|
||||
if [ "$ENABLE_GOOGLE" == "true" ]; then
|
||||
GOOGLE_LOGS=$(echo "$LOG_CONTENT" | grep "\[Google")
|
||||
G_TOTAL=$(echo "$GOOGLE_LOGS" | grep "\[START\]" -c)
|
||||
G_SUCCESS=$(echo "$GOOGLE_LOGS" | grep "✅" -c)
|
||||
G_FAILED=$(echo "$GOOGLE_LOGS" | grep "❌" -c)
|
||||
G_WARN=$(echo "$GOOGLE_LOGS" | grep "⚠️" -c)
|
||||
|
||||
G_RATE="0.0"
|
||||
[ "$G_TOTAL" -gt 0 ] && G_RATE=$(awk "BEGIN {printf \"%.1f\", ($G_SUCCESS/$G_TOTAL)*100}")
|
||||
|
||||
🕒 **最近执行快照**:
|
||||
MSG="$MSG
|
||||
|
||||
🎯 **[Google 区域纠偏]**
|
||||
🚀 执行总数: ${G_TOTAL} 次 (胜率: **${G_RATE}%**)
|
||||
✅ 成功: ${G_SUCCESS} | ❌ 送中: ${G_FAILED} | ⚠️ 警告: ${G_WARN}"
|
||||
fi
|
||||
|
||||
# --- [分析块 2: IP 信用净化模块] ---
|
||||
if [ "$ENABLE_TRUST" == "true" ]; then
|
||||
TRUST_LOGS=$(echo "$LOG_CONTENT" | grep "\[Trust")
|
||||
T_TOTAL=$(echo "$TRUST_LOGS" | grep "\[START\]" -c)
|
||||
T_SUCCESS=$(echo "$TRUST_LOGS" | grep "✅" -c)
|
||||
T_FAILED=$(echo "$TRUST_LOGS" | grep "❌" -c)
|
||||
|
||||
T_RATE="0.0"
|
||||
[ "$T_TOTAL" -gt 0 ] && T_RATE=$(awk "BEGIN {printf \"%.1f\", ($T_SUCCESS/$T_TOTAL)*100}")
|
||||
|
||||
MSG="$MSG
|
||||
|
||||
🔰 **[IP 信用净化]**
|
||||
🚀 净化总数: ${T_TOTAL} 轮 (成功率: **${T_RATE}%**)
|
||||
✅ 成功注入: ${T_SUCCESS} | ❌ 访问受阻: ${T_FAILED}"
|
||||
fi
|
||||
|
||||
# 组装战报尾部 (最近快照)
|
||||
MSG="$MSG
|
||||
|
||||
🕒 **最近执行快照 [${LAST_MOD:-"System"}]:**
|
||||
时间: ${LAST_TIME:-"暂无数据"}
|
||||
结论: ${LAST_SCORE:-"暂无数据"}
|
||||
----------------------------
|
||||
💡 哨兵正在后台默默守护您的资产。
|
||||
EOT
|
||||
结论: ${LAST_SCORE:-"暂无数据"}"
|
||||
|
||||
fi
|
||||
|
||||
# 6. 调用 API 推送 (增加返回值校验输出,方便查错)
|
||||
RESPONSE=$(curl -s -m 10 -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
|
||||
-d "chat_id=${CHAT_ID}" \
|
||||
-d "text=${MSG}" \
|
||||
-d "parse_mode=Markdown")
|
||||
# ==========================================
|
||||
# 5. [核心: OTA 云端版本探针与告警模块]
|
||||
# ==========================================
|
||||
# 从配置文件提取当前本地版本,若无则默认为未知
|
||||
LOCAL_VER="${AGENT_VERSION:-未知}"
|
||||
|
||||
# 极轻量级探针: 抓取 GitHub 云端的 version.txt (超时 3 秒,KV解析法)
|
||||
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||
REMOTE_VER=$(curl -s -m 3 "${REPO_RAW_URL}/version.txt" | grep "^AGENT_VERSION=" | cut -d'=' -f2 | tr -d '[:space:]')
|
||||
|
||||
# 构建底部引擎状态块
|
||||
MSG="$MSG
|
||||
----------------------------
|
||||
🛡️ **系统引擎状态**
|
||||
当前运行版本: \`v${LOCAL_VER}\`"
|
||||
|
||||
# 比对逻辑:如果成功抓到了远端版本,且和本地不一样
|
||||
if [ -n "$REMOTE_VER" ] && [ "$REMOTE_VER" != "$LOCAL_VER" ]; then
|
||||
MSG="$MSG
|
||||
最新官方版本: \`v${REMOTE_VER}\` (✨有新版)
|
||||
💡 *司令部提示:检测到新版装甲,请长官登录节点执行平滑热更新!*"
|
||||
elif [ -n "$REMOTE_VER" ] && [ "$REMOTE_VER" == "$LOCAL_VER" ]; then
|
||||
MSG="$MSG
|
||||
最新官方版本: \`v${REMOTE_VER}\` (✅已是最新)
|
||||
💡 *哨兵正在后台默默守护您的资产。*"
|
||||
else
|
||||
# 抓取失败兜底
|
||||
MSG="$MSG
|
||||
💡 *哨兵正在后台默默守护您的资产。*"
|
||||
fi
|
||||
|
||||
# 5. 调用 API 推送 (接入安全网关,挂载交互式控制台按钮)
|
||||
JSON_PAYLOAD=$(jq -n \
|
||||
--arg cid "$CHAT_ID" \
|
||||
--arg txt "$MSG" \
|
||||
--arg cb "manage:${NODE_NAME}" \
|
||||
'{
|
||||
chat_id: $cid,
|
||||
text: $txt,
|
||||
parse_mode: "Markdown",
|
||||
disable_web_page_preview: true,
|
||||
reply_markup: {
|
||||
inline_keyboard: [[{text: "⚙️ 调出该节点控制台", callback_data: $cb}]]
|
||||
}
|
||||
}')
|
||||
|
||||
RESPONSE=$(curl -s -m 10 -X POST "${TG_API_URL}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$JSON_PAYLOAD")
|
||||
|
||||
if [[ "$RESPONSE" != *"\"ok\":true"* ]]; then
|
||||
echo "❌ 战报发送失败!API 响应: $RESPONSE" >> "${INSTALL_DIR}/logs/error.log"
|
||||
|
||||
@@ -1,35 +1,84 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 脚本名称: uninstall.sh (IP-Sentinel 一键卸载脚本 - 动态锚点版)
|
||||
# 核心功能: 无痕清理守护进程、定时任务、运行目录及临时缓存
|
||||
# ==========================================================
|
||||
# 脚本名称: uninstall.sh (IP-Sentinel 一键卸载脚本)
|
||||
# 核心功能: 清除守护进程、清理系统定时任务、删除所有程序文件
|
||||
|
||||
# ==========================================================
|
||||
# 🛑 核心权限防线: 检查是否以 root 权限运行
|
||||
# ==========================================================
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo -e "\033[31m❌ 权限被拒绝: 卸载 IP-Sentinel 需要最高系统权限。\033[0m"
|
||||
echo -e "💡 请切换到 root 用户 (执行 su root 或 sudo -i) 后重新运行指令。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
INSTALL_DIR="/opt/ip_sentinel"
|
||||
|
||||
echo "========================================================"
|
||||
echo " 🗑️ 准备卸载 IP-Sentinel (VPS IP 自动养护哨兵)"
|
||||
echo " 🗑️ 准备卸载 IP-Sentinel (边缘节点 Edge Agent)"
|
||||
|
||||
# [核心: 动态读取并播报即将销毁的本地版本号]
|
||||
CONFIG_FILE="${INSTALL_DIR}/config.conf"
|
||||
if [ -f "$CONFIG_FILE" ]; then
|
||||
CURRENT_VER=$(grep "^AGENT_VERSION=" "$CONFIG_FILE" | cut -d'"' -f2)
|
||||
[ -n "$CURRENT_VER" ] && echo " 📍 目标版本: v${CURRENT_VER}"
|
||||
fi
|
||||
echo "========================================================"
|
||||
|
||||
# 1. 停止运行中的守护进程与主控模块
|
||||
echo "[1/3] 正在终止后台 Telegram 守护进程与养护任务..."
|
||||
pgrep -f tg_daemon.sh | xargs -r kill -9 >/dev/null 2>&1
|
||||
pgrep -f runner.sh | xargs -r kill -9 >/dev/null 2>&1
|
||||
pgrep -f mod_google.sh | xargs -r kill -9 >/dev/null 2>&1
|
||||
# 1. 停止并删除 Systemd 服务 (适配新架构)
|
||||
echo "[1/4] 正在停止并删除 Systemd 服务..."
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
echo "💡 检测到 Systemd 环境,正在抹除 Systemd 服务单元..."
|
||||
systemctl disable --now ip-sentinel-runner.service ip-sentinel-runner.timer \
|
||||
ip-sentinel-updater.service ip-sentinel-updater.timer \
|
||||
ip-sentinel-report.service ip-sentinel-report.timer \
|
||||
ip-sentinel-agent-daemon.service >/dev/null 2>&1
|
||||
rm -f /etc/systemd/system/ip-sentinel-runner.service
|
||||
rm -f /etc/systemd/system/ip-sentinel-runner.timer
|
||||
rm -f /etc/systemd/system/ip-sentinel-updater.service
|
||||
rm -f /etc/systemd/system/ip-sentinel-updater.timer
|
||||
rm -f /etc/systemd/system/ip-sentinel-report.service
|
||||
rm -f /etc/systemd/system/ip-sentinel-report.timer
|
||||
rm -f /etc/systemd/system/ip-sentinel-agent-daemon.service
|
||||
systemctl daemon-reload
|
||||
systemctl reset-failed
|
||||
else
|
||||
echo "💡 未检测到 Systemd,跳过此步骤..."
|
||||
fi
|
||||
|
||||
# 2. 清除系统定时任务 (Cron)
|
||||
echo "[2/3] 正在清理系统定时任务 (Cron)..."
|
||||
crontab -l 2>/dev/null | grep -v "ip_sentinel" > /tmp/cron_backup
|
||||
crontab /tmp/cron_backup
|
||||
rm -f /tmp/cron_backup
|
||||
# 2. 停止运行中的守护进程与主控模块 (兜底清理老版进程)
|
||||
echo "[2/4] 正在终止后台守护进程与所有养护任务..."
|
||||
pkill -9 -f "tg_daemon.sh" >/dev/null 2>&1
|
||||
pkill -9 -f "agent_daemon.sh" >/dev/null 2>&1
|
||||
pkill -9 -f "python3.*webhook.py" >/dev/null 2>&1
|
||||
pkill -9 -f "webhook.py" >/dev/null 2>&1
|
||||
pkill -9 -f "runner.sh" >/dev/null 2>&1
|
||||
pkill -9 -f "updater.sh" >/dev/null 2>&1
|
||||
pkill -9 -f "tg_report.sh" >/dev/null 2>&1
|
||||
pkill -9 -f "mod_google.sh" >/dev/null 2>&1
|
||||
pkill -9 -f "mod_trust.sh" >/dev/null 2>&1
|
||||
|
||||
# 3. 删除所有文件与日志
|
||||
echo "[3/3] 正在抹除核心程序、配置文件与系统日志..."
|
||||
# 3. 清除系统定时任务 (Cron)
|
||||
echo "[3/4] 正在清理系统定时任务 (Cron)..."
|
||||
if crontab -l >/dev/null 2>&1; then
|
||||
crontab -l | grep -v "ip_sentinel" > /tmp/cron_backup
|
||||
crontab /tmp/cron_backup
|
||||
rm -f /tmp/cron_backup
|
||||
fi
|
||||
|
||||
# 4. 删除所有文件、日志与临时缓存
|
||||
echo "[4/4] 正在抹除核心程序、配置文件与系统痕迹..."
|
||||
if [ -d "$INSTALL_DIR" ]; then
|
||||
rm -rf "$INSTALL_DIR"
|
||||
fi
|
||||
|
||||
# 拔除 /tmp 目录下的所有更新下载临时文件和 V1/V2 遗留的偏移量记录
|
||||
rm -f /tmp/ip_sentinel_*.txt
|
||||
rm -f /tmp/ip_sentinel_*.json
|
||||
|
||||
echo "========================================================"
|
||||
echo "✅ 卸载彻底完成!IP-Sentinel 已从您的系统中无痕移除。"
|
||||
echo "👋 感谢您的使用,期待未来再次为您守护 IP!"
|
||||
echo "💡 提示:如果安装时在防火墙放行了 Webhook 随机端口,请您按需手动关闭。"
|
||||
echo "👋 感谢您的使用,期待未来再次为您守护资产!"
|
||||
echo "========================================================"
|
||||
138
core/updater.sh
138
core/updater.sh
@@ -1,14 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: updater.sh (IP-Sentinel 养料注入与系统维护模块)
|
||||
# 核心功能: 定期静默更新热数据、清理瘦身日志文件
|
||||
# 脚本名称: updater.sh (IP-Sentinel 养料注入与分频调度中枢 - 动态锚点版)
|
||||
# 核心功能: 静默更新热数据/LBS、指纹库错峰调度、强制出站死锁、版本无缝继承
|
||||
# ==========================================================
|
||||
|
||||
INSTALL_DIR="/opt/ip_sentinel"
|
||||
CONFIG_FILE="${INSTALL_DIR}/config.conf"
|
||||
# 你的专属 Forgejo 仓库 Raw 数据直链前缀
|
||||
REPO_RAW_URL="https://git.94211762.xyz/hotyue/IP-Sentinel/raw/branch/main"
|
||||
UA_TIME_FILE="${INSTALL_DIR}/core/.ua_last_update"
|
||||
|
||||
# GitHub 仓库 Raw 数据直链前缀
|
||||
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||
# 临时改为开发地址用于测试
|
||||
# REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/v3.6.2-rc"
|
||||
|
||||
# 1. 加载本地冷数据配置
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
@@ -16,37 +20,133 @@ if [ ! -f "$CONFIG_FILE" ]; then
|
||||
fi
|
||||
source "$CONFIG_FILE"
|
||||
|
||||
# 2. 全局日志写入函数
|
||||
# 2. 全局日志写入函数 (v3.4.0 引入版本探针)
|
||||
log() {
|
||||
# [v3.4.0 核心] 提取当前配置中的版本锚点
|
||||
local local_ver="${AGENT_VERSION:-未知}"
|
||||
|
||||
# 保证日志目录存在
|
||||
mkdir -p "${INSTALL_DIR}/logs"
|
||||
printf "[$(date '+%Y-%m-%d %H:%M:%S')] [%-5s] [%-7s] [%s] %s\n" "$2" "$1" "$REGION_CODE" "$3" >> "$LOG_FILE"
|
||||
|
||||
# 日志格式注入 [版本号] 追踪标识
|
||||
local core_msg=$(printf "[v%-5s] [%-5s] [%-7s] [%s] %s" "$local_ver" "$2" "$1" "$REGION_CODE" "$3")
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $core_msg" >> "$LOG_FILE"
|
||||
|
||||
# 强制推送到 Systemd Journal (如果系统支持)
|
||||
if command -v logger >/dev/null 2>&1; then
|
||||
logger -t ip-sentinel "$core_msg"
|
||||
else
|
||||
# 降级输出到 stdout,让 Systemd 捕获
|
||||
echo "$core_msg"
|
||||
fi
|
||||
}
|
||||
|
||||
log "Updater" "INFO " "========== 触发后台静默 OTA 热数据更新 =========="
|
||||
|
||||
# 3. 容灾机制拉取 UA 池
|
||||
TMP_UA="/tmp/ip_sentinel_ua.txt"
|
||||
curl -sL "${REPO_RAW_URL}/data/user_agents.txt" -o "$TMP_UA"
|
||||
if [ -s "$TMP_UA" ]; then
|
||||
mv "$TMP_UA" "${INSTALL_DIR}/data/user_agents.txt"
|
||||
log "Updater" "INFO " "✅ 设备指纹池 (User-Agents) 更新成功"
|
||||
else
|
||||
log "Updater" "WARN " "❌ UA 池拉取失败或为空,保留本地旧数据防崩溃"
|
||||
rm -f "$TMP_UA"
|
||||
# ==========================================================
|
||||
# 🛡️ 终极护城河:构建强锚定出站的 curl 请求引擎
|
||||
# ==========================================================
|
||||
# 基础参数:跟随 install.sh 锁定的协议偏好 (4 或 6)
|
||||
CURL_CMD="curl -${IP_PREF:-4} -sL"
|
||||
|
||||
# 【防坑核心】如果用户配置了死锁锚点,必须强制绑定网卡,杜绝流量溢出!
|
||||
if [ -n "$BIND_IP" ]; then
|
||||
# curl 的 --interface 参数不支持带方括号的 IPv6 地址,必须强行脱壳
|
||||
RAW_BIND_IP=$(echo "$BIND_IP" | tr -d '[]')
|
||||
# [v3.6.3 容错层补丁] 探测网卡存活状态,防止 IP 漂移导致永久断网
|
||||
if ! ip addr show 2>/dev/null | grep -qw "$RAW_BIND_IP"; then
|
||||
log "Updater" "WARN " "检测到绑定的出口 IP ($RAW_BIND_IP) 已丢失,自动退回默认路由!"
|
||||
else
|
||||
CURL_CMD="$CURL_CMD --interface $RAW_BIND_IP"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 4. 容灾机制拉取当地最新搜索词库
|
||||
# ==========================================================
|
||||
# 3. 容灾机制拉取 UA 指纹池 (V3.3.0 引入 30 天错峰防惊群逻辑)
|
||||
# ==========================================================
|
||||
NOW=$(date +%s)
|
||||
LAST_UPDATE=0
|
||||
|
||||
# 读取上一次更新的时间戳
|
||||
if [ -f "$UA_TIME_FILE" ]; then
|
||||
# tr -d 清除可能存在的换行或回车符,防止算术崩溃
|
||||
LAST_UPDATE=$(cat "$UA_TIME_FILE" | tr -d '\r\n')
|
||||
fi
|
||||
|
||||
# 校验数据合法性,防崩溃
|
||||
if ! [[ "$LAST_UPDATE" =~ ^[0-9]+$ ]]; then
|
||||
LAST_UPDATE=0
|
||||
fi
|
||||
|
||||
DIFF=$((NOW - LAST_UPDATE))
|
||||
|
||||
# 距离上次拉取超过 30 天 (2592000 秒),才执行下载
|
||||
if [ "$DIFF" -ge 2592000 ] || [ "$LAST_UPDATE" -eq 0 ]; then
|
||||
TMP_UA="/tmp/ip_sentinel_ua.txt"
|
||||
# 使用重装升级后的 CURL_CMD
|
||||
$CURL_CMD "${REPO_RAW_URL}/data/user_agents.txt" -o "$TMP_UA"
|
||||
|
||||
if [ -s "$TMP_UA" ]; then
|
||||
mv "$TMP_UA" "${INSTALL_DIR}/data/user_agents.txt"
|
||||
echo "$NOW" > "$UA_TIME_FILE"
|
||||
log "Updater" "INFO " "✅ 设备指纹池 (User-Agents) 30天错峰滚动更新成功"
|
||||
else
|
||||
log "Updater" "WARN " "❌ UA 池拉取失败,保留本地旧数据防崩溃"
|
||||
rm -f "$TMP_UA"
|
||||
fi
|
||||
else
|
||||
DAYS_LEFT=$(((2592000 - DIFF) / 86400))
|
||||
log "Updater" "INFO " "⏳ 设备指纹池处于 30 天静默期 (剩余约 ${DAYS_LEFT} 天),跳过拉取"
|
||||
fi
|
||||
|
||||
# ==========================================================
|
||||
# 4. 容灾机制拉取当地最新搜索词库 (每日高频拉取,保证活体新鲜度)
|
||||
# ==========================================================
|
||||
TMP_KW="/tmp/ip_sentinel_kw.txt"
|
||||
curl -sL "${REPO_RAW_URL}/data/keywords/kw_${REGION_CODE}.txt" -o "$TMP_KW"
|
||||
$CURL_CMD "${REPO_RAW_URL}/data/keywords/kw_${REGION_CODE}.txt" -o "$TMP_KW"
|
||||
|
||||
if [ -s "$TMP_KW" ]; then
|
||||
mv "$TMP_KW" "${INSTALL_DIR}/data/keywords/kw_${REGION_CODE}.txt"
|
||||
log "Updater" "INFO " "✅ 区域搜索词库 (kw_${REGION_CODE}) 更新成功"
|
||||
log "Updater" "INFO " "✅ 区域搜索词库 (kw_${REGION_CODE}) 每日同步成功"
|
||||
else
|
||||
log "Updater" "WARN " "❌ 搜索词库拉取失败,保留本地旧数据防崩溃"
|
||||
rm -f "$TMP_KW"
|
||||
fi
|
||||
|
||||
# 5. 【升级点】日志防满瘦身机制 (保留最近 2000 行)
|
||||
# ==========================================================
|
||||
# 5. 自适应拉取本地 LBS 专属 JSON 规则库 (每日同步)
|
||||
# ==========================================================
|
||||
REGION_JSON_FILE=$(find "${INSTALL_DIR}/data/regions" -name "*.json" 2>/dev/null | head -n 1)
|
||||
|
||||
if [ -n "$REGION_JSON_FILE" ] && [ -f "$REGION_JSON_FILE" ]; then
|
||||
REL_PATH=${REGION_JSON_FILE#*${INSTALL_DIR}/}
|
||||
TMP_JSON="/tmp/ip_sentinel_region.json"
|
||||
|
||||
$CURL_CMD "${REPO_RAW_URL}/${REL_PATH}" -o "$TMP_JSON"
|
||||
|
||||
if [ -s "$TMP_JSON" ]; then
|
||||
mv "$TMP_JSON" "$REGION_JSON_FILE"
|
||||
log "Updater" "INFO " "✅ 核心战区规则库 ($REL_PATH) 每日同步成功"
|
||||
else
|
||||
log "Updater" "WARN " "❌ 战区规则库拉取失败,保留本地旧数据"
|
||||
rm -f "$TMP_JSON"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ==========================================================
|
||||
# 5.5. 容灾更新深海声呐底层探针 (彻底消除第三方 RCE 依赖)
|
||||
# ==========================================================
|
||||
TMP_PROBE="/tmp/ip_sentinel_probe.sh"
|
||||
$CURL_CMD "https://raw.githubusercontent.com/xykt/IPQuality/main/ip.sh" -o "$TMP_PROBE"
|
||||
if [ -s "$TMP_PROBE" ]; then
|
||||
mv "$TMP_PROBE" "${INSTALL_DIR}/core/ip_probe.sh"
|
||||
chmod +x "${INSTALL_DIR}/core/ip_probe.sh"
|
||||
log "Updater" "INFO " "✅ 深海声呐底层探针 (ip_probe.sh) 源文件安全对齐"
|
||||
fi
|
||||
|
||||
# ==========================================================
|
||||
# 6. 日志防满瘦身机制 (保留最近 2000 行)
|
||||
# ==========================================================
|
||||
if [ -f "$LOG_FILE" ]; then
|
||||
tail -n 2000 "$LOG_FILE" > "${LOG_FILE}.tmp"
|
||||
mv "${LOG_FILE}.tmp" "$LOG_FILE"
|
||||
|
||||
200
data/keywords/kw_AU.txt
Normal file
200
data/keywords/kw_AU.txt
Normal file
@@ -0,0 +1,200 @@
|
||||
alex de minaur
|
||||
sunderland vs nottm forest
|
||||
real betis vs real madrid
|
||||
sai sudharsan
|
||||
jannik sinner
|
||||
gt vs rcb
|
||||
amd stock
|
||||
carey mulligan
|
||||
jermaine jackson
|
||||
hulk hogan
|
||||
coles
|
||||
elon musk
|
||||
ben roberts-smith
|
||||
ig
|
||||
danish malewar
|
||||
assassin's creed black flag resynced
|
||||
sarfaraz khan
|
||||
kartik sharma
|
||||
instagram stories not working
|
||||
meningococcal b
|
||||
sundaresh menon
|
||||
thunder vs suns
|
||||
cade cunningham
|
||||
real salt lake vs inter miami
|
||||
pope francis
|
||||
camera
|
||||
anzac
|
||||
teams
|
||||
kids news
|
||||
bonds
|
||||
hung cao
|
||||
classroom
|
||||
michael jackson biopic movie
|
||||
india women vs south africa women
|
||||
nato
|
||||
bayern munich
|
||||
barcelona vs celta vigo
|
||||
bournemouth vs leeds
|
||||
leverkusen vs bayern
|
||||
psl
|
||||
burnley vs man city
|
||||
elche vs atlético madrid
|
||||
lsg vs rr
|
||||
pakistan super league
|
||||
kyle sandilands alleged rant jackie o
|
||||
carrie bickmore
|
||||
adam levine
|
||||
alexandra eala
|
||||
tubi
|
||||
charles melton
|
||||
nuclear weapon
|
||||
janet jackson
|
||||
justin bieber australia tour
|
||||
fleche wallonne 2026
|
||||
ange postecoglou
|
||||
john hattie
|
||||
airbus a380
|
||||
real madrid vs alavés
|
||||
brighton vs chelsea
|
||||
woolworth
|
||||
nitish rana
|
||||
lyrid meteor shower
|
||||
世界地球日
|
||||
youth gang
|
||||
earth day
|
||||
crystal palace vs west ham
|
||||
d4vd
|
||||
mi vs gt
|
||||
perth weather
|
||||
elijah hollands carlton football club
|
||||
fair work commission fuel
|
||||
vanguard
|
||||
sydney sweeney
|
||||
cailee spaeny
|
||||
psg vs lyon
|
||||
sporting vs benfica
|
||||
celtics vs 76ers
|
||||
bundesliga
|
||||
pl
|
||||
afc cup
|
||||
mars rover
|
||||
celtic fc
|
||||
bayern vs vfb stuttgart
|
||||
bom radar
|
||||
sydney weather
|
||||
melbourne weather
|
||||
brisbane weather
|
||||
adelaide weather
|
||||
myGov login
|
||||
news.com.au
|
||||
abc news
|
||||
nrl ladder
|
||||
afl scores
|
||||
afl fixture
|
||||
matildas
|
||||
sam kerr
|
||||
bunnings opening hours
|
||||
coles catalogue
|
||||
woolies specials
|
||||
qantas
|
||||
virgin australia
|
||||
jetstar flights
|
||||
commbank
|
||||
asx 200
|
||||
sydney morning herald
|
||||
the age
|
||||
ticketek
|
||||
ticketmaster
|
||||
opal card top up
|
||||
myki top up
|
||||
translink timetable
|
||||
transperth journey planner
|
||||
adelaide metro
|
||||
adelaide 500
|
||||
adelaide crows
|
||||
port adelaide
|
||||
penrith panthers
|
||||
brisbane broncos
|
||||
collingwood fc
|
||||
sydney swans
|
||||
west coast eagles
|
||||
fremantle dockers
|
||||
canberra raiders
|
||||
mcg events
|
||||
optus stadium
|
||||
the gabba
|
||||
state of origin
|
||||
australian open
|
||||
melbourne cup
|
||||
masterchef australia
|
||||
mafs australia
|
||||
petrol prices near me
|
||||
australia post tracking
|
||||
service nsw login
|
||||
vicroads
|
||||
queensland health
|
||||
medicare
|
||||
ato
|
||||
jb hi-fi
|
||||
kmart
|
||||
amazon.com.au
|
||||
vivid sydney
|
||||
mona hobart
|
||||
dark mofo
|
||||
floriade canberra
|
||||
rottnest island ferry
|
||||
sydney airport arrivals
|
||||
rba interest rate
|
||||
nsw school holidays
|
||||
qld school holidays
|
||||
wa school holidays
|
||||
m4 traffic updates
|
||||
west gate tunnel updates
|
||||
bruce highway traffic
|
||||
taylor swift
|
||||
netflix
|
||||
gemini
|
||||
chatgpt
|
||||
margot robbie
|
||||
oscar piastri
|
||||
formula 1
|
||||
supercars championship
|
||||
ufc
|
||||
optus sport
|
||||
telstra outage
|
||||
kayo sports
|
||||
domain real estate
|
||||
realestate.com.au
|
||||
seek jobs
|
||||
gumtree
|
||||
westpac login
|
||||
anz internet banking
|
||||
nab login
|
||||
aldi catalogue
|
||||
big w
|
||||
target australia
|
||||
the good guys
|
||||
officeworks
|
||||
dan murphys
|
||||
bws
|
||||
chemist warehouse
|
||||
priceline
|
||||
uber eats
|
||||
menulog
|
||||
dominos
|
||||
centrelink login
|
||||
medicare online
|
||||
passport renewal
|
||||
aec enrolment
|
||||
agl energy
|
||||
origin energy
|
||||
stan
|
||||
binge
|
||||
9now
|
||||
7plus
|
||||
abc iview
|
||||
hoyts cinemas
|
||||
event cinemas
|
||||
big bash league
|
||||
the ashes
|
||||
150
data/keywords/kw_CA.txt
Normal file
150
data/keywords/kw_CA.txt
Normal file
@@ -0,0 +1,150 @@
|
||||
lpga leaderboard
|
||||
randy travis
|
||||
execution by firing squad
|
||||
nelly korda
|
||||
leicester city vs millwall
|
||||
asteroid
|
||||
sunderland vs nottm forest
|
||||
real betis vs real madrid
|
||||
betis – real madrid
|
||||
napoli vs cremonese
|
||||
gta 6
|
||||
japon
|
||||
padres vs rockies
|
||||
guide de la révolution de l'iran
|
||||
casa pia vs braga
|
||||
denaturalization
|
||||
ocean
|
||||
alberta referendum
|
||||
vfb stuttgart vs sc freiburg
|
||||
gpt 5.5
|
||||
noah cates
|
||||
pwhl scores
|
||||
hank idsinga
|
||||
karoline leavitt
|
||||
ottawa charge
|
||||
million dollar secret
|
||||
white sox vs diamondbacks
|
||||
suns vs thunder
|
||||
stars vs wild
|
||||
jaafar jackson
|
||||
airport
|
||||
grand theft auto vi
|
||||
pentagone
|
||||
etats unis
|
||||
monette farms news
|
||||
jose soriano
|
||||
jon cooper
|
||||
barcelona vs celta vigo
|
||||
764
|
||||
david scott
|
||||
rodrigo duterte
|
||||
u18 world championship
|
||||
zara tindall
|
||||
graham platner
|
||||
lsg vs rr
|
||||
nikki glaser
|
||||
global news edmonton
|
||||
policier
|
||||
nuclear weapon
|
||||
motorcycle
|
||||
msc francesca
|
||||
poet stock
|
||||
fifa tickets
|
||||
7 jours
|
||||
alexandra eala
|
||||
earth
|
||||
zara larsson
|
||||
girona vs real betis
|
||||
kevyn adams
|
||||
ali khamenei
|
||||
inter – côme
|
||||
leicester city vs hull
|
||||
fenerbahçe
|
||||
françois bonnardel
|
||||
missile
|
||||
real madrid vs alavés
|
||||
vrabel and russini photos
|
||||
will trent
|
||||
ibm
|
||||
lecce vs fiorentina
|
||||
celeste rivas hernandez
|
||||
abhishek bachchan
|
||||
national guard of the united states
|
||||
triathlon
|
||||
vincent trocheck
|
||||
benyamin netanyahou
|
||||
kings vs avalanche
|
||||
crème solaire
|
||||
trabzonspor vs istanbul başakşehir
|
||||
indigenous rights
|
||||
meghan, duchess of sussex
|
||||
gwyneth paltrow
|
||||
toronto gas prices
|
||||
président
|
||||
mets vs cubs
|
||||
matt fitzpatrick
|
||||
sénateurs – hurricanes
|
||||
senators vs hurricanes
|
||||
man utd
|
||||
spurs
|
||||
evan mobley
|
||||
chelsea vs man united
|
||||
atlético madrid vs real sociedad
|
||||
roma vs atalanta
|
||||
cf montreal
|
||||
jakob poeltl
|
||||
ukraine
|
||||
contrôle routier québec
|
||||
bachelorette 2026
|
||||
lens – toulouse
|
||||
arber xhekaj
|
||||
leylah fernandez
|
||||
anthropic
|
||||
paige wwe
|
||||
inter – cagliari
|
||||
carrie ann inaba
|
||||
syria
|
||||
highway hotline sk
|
||||
real betis vs braga
|
||||
vincent bolloré
|
||||
europa conference league
|
||||
resident alien
|
||||
battlefield 6
|
||||
aston villa vs bologna
|
||||
nottm forest vs porto
|
||||
soccer
|
||||
lamour est dans le pré
|
||||
luis suárez
|
||||
listeria
|
||||
strc
|
||||
bayern
|
||||
arda güler
|
||||
aleksandar pavlović
|
||||
kooora
|
||||
yalla kora
|
||||
ina garten
|
||||
jordan goodwin
|
||||
jerami grant
|
||||
jimmy snuggerud
|
||||
clav
|
||||
bts
|
||||
zach galifianakis
|
||||
billy crystal
|
||||
club américa vs nashville
|
||||
allison williams
|
||||
CBC News
|
||||
Canada.ca
|
||||
Toronto Weather
|
||||
NHL Scores
|
||||
Amazon.ca
|
||||
Air Canada
|
||||
Service Canada
|
||||
CRA login
|
||||
Rogers
|
||||
Bell Internet
|
||||
Tim Hortons
|
||||
Indigo
|
||||
Canadian Tire
|
||||
Walmart Canada
|
||||
Toronto Raptors
|
||||
173
data/keywords/kw_DE.txt
Normal file
173
data/keywords/kw_DE.txt
Normal file
@@ -0,0 +1,173 @@
|
||||
christian lindner
|
||||
kevin costner
|
||||
samu haber
|
||||
gillian anderson
|
||||
peggy jerofke
|
||||
rafael jódar
|
||||
ernie dosio
|
||||
bibi heinicke
|
||||
denia
|
||||
der alte blaue stunde
|
||||
matthias ginter
|
||||
ralf schmitz
|
||||
maximilian eggestein
|
||||
hull city
|
||||
demirovic
|
||||
elton john
|
||||
jeff chabot
|
||||
lindt schokolade
|
||||
manzambi
|
||||
angelo stiller
|
||||
lamine yamal verletzung
|
||||
peer steinbrück
|
||||
h&m
|
||||
lotto 6aus49
|
||||
pistons – magic
|
||||
chemnitzer fc erzgebirge aue
|
||||
asiago
|
||||
23 nisan
|
||||
sternschnuppen heute lyriden
|
||||
aryna sabalenka
|
||||
michael douglas
|
||||
herman van veen
|
||||
georgina fleur
|
||||
tui
|
||||
trainer bayern münchen
|
||||
katie holmes
|
||||
prinz frederic
|
||||
bayern vs
|
||||
flekken
|
||||
бернли – манчестер сити
|
||||
ard
|
||||
queen camilla
|
||||
clankriminalität
|
||||
politik
|
||||
gladbach-news
|
||||
sara gündogan
|
||||
krankenversicherung
|
||||
jan-lennard struff
|
||||
bryan lasme
|
||||
republikanische partei
|
||||
thailand
|
||||
robin gosens
|
||||
maremma
|
||||
tennis madrid
|
||||
osterburg
|
||||
برايتون ضد تشيلسي
|
||||
sebastian hoeneß
|
||||
ncis
|
||||
menowin fröhlich
|
||||
maren gilzer
|
||||
cineplex
|
||||
gina schumacher
|
||||
alexander räuscher
|
||||
riza kayaalp
|
||||
heppenheim
|
||||
ministerpräsident
|
||||
robbie williams
|
||||
brian littrell
|
||||
saarbrücken hauptbahnhof
|
||||
frauen-bundesliga
|
||||
lemgo
|
||||
kommissar rex
|
||||
hemsbach
|
||||
benjamin weber
|
||||
sydney sweeney
|
||||
martin schindler
|
||||
robert kennedy
|
||||
antoni kowalski
|
||||
luna
|
||||
paris-sg – lyon
|
||||
arte live
|
||||
dominik kohr
|
||||
dumbledores geheimnisse
|
||||
kampf der realitystars
|
||||
kerner
|
||||
tennessee
|
||||
billy idol
|
||||
kfc uerdingen
|
||||
bastian pastewka
|
||||
gnabry
|
||||
lok leipzig heute live ticker
|
||||
true lies wahre lügen
|
||||
челси – манчестер юнайтед
|
||||
nurburgring
|
||||
fortnite server status
|
||||
süperlig
|
||||
ronaldinho
|
||||
esther schweins let's dance
|
||||
michael jackson film 2026
|
||||
oliver pocher
|
||||
die queen film
|
||||
milano
|
||||
straße von hormus
|
||||
motsi mabuse
|
||||
rayo vallecano
|
||||
harry meghan
|
||||
irland
|
||||
betis sevilla
|
||||
vermisste person
|
||||
konferenz league
|
||||
garda
|
||||
judith hoersch
|
||||
jörg pilawa
|
||||
strasbourg
|
||||
real madrid vs bayern
|
||||
kicker
|
||||
mbappe
|
||||
flashscore
|
||||
sport1
|
||||
vini jr
|
||||
bet365
|
||||
kompany
|
||||
jude bellingham
|
||||
upamecano
|
||||
gute zeiten, schlechte zeiten
|
||||
inflation
|
||||
fog warning
|
||||
wetter bochum
|
||||
wetter aachen
|
||||
wetter bonn
|
||||
onet
|
||||
protest
|
||||
jamie dornan
|
||||
heizöl
|
||||
champions league
|
||||
uefa champions league
|
||||
keytruda
|
||||
péter magyar
|
||||
psg
|
||||
hechingen
|
||||
şampiyonlar ligi
|
||||
tschernobyl 1986
|
||||
amazon video
|
||||
paris saint-germain
|
||||
dietrich grönemeyer
|
||||
fränkische schweiz
|
||||
scarlett johansson
|
||||
jeff bezos
|
||||
dan brown
|
||||
паспорт громадянина україни для виїзду за кордон
|
||||
serena williams
|
||||
манчестер юнайтед – лидс
|
||||
catherine deneuve
|
||||
bobzin
|
||||
sprit
|
||||
kev
|
||||
abschiebung
|
||||
steuer
|
||||
masters rory mcilroy
|
||||
großglockner
|
||||
news38
|
||||
jessie cave
|
||||
michael schulte
|
||||
wetter frankfurt heute
|
||||
bundesliga ergebnisse
|
||||
aktuelle nachrichten deutschland
|
||||
restaurant in der nähe
|
||||
deutsche bahn fahrplan
|
||||
urlaub buchen
|
||||
rezept für kartoffelsalat
|
||||
dax aktueller stand
|
||||
apotheke notdienst frankfurt
|
||||
günstige flüge
|
||||
149
data/keywords/kw_ES.txt
Normal file
149
data/keywords/kw_ES.txt
Normal file
@@ -0,0 +1,149 @@
|
||||
rafa jodar
|
||||
samantha vallejo-nágera
|
||||
ريال بتيس ضد الريال
|
||||
nvidia
|
||||
rafael jódar
|
||||
jodar tenista
|
||||
sunderland afc - nottingham forest
|
||||
rb leipzig - fc union berlin
|
||||
julián alvarez
|
||||
stade brestois - lens
|
||||
clasificación de primera división
|
||||
javier hidalgo
|
||||
cayetano martínez de irujo
|
||||
llanes
|
||||
vfb stuttgart – freiburg
|
||||
roca rey
|
||||
psv - zwolle
|
||||
daredevil
|
||||
a que estas esperando
|
||||
real oviedo - villarreal
|
||||
real salt lake - inter miami
|
||||
pistons - magic
|
||||
barcelona sc - mushuc runa
|
||||
kick
|
||||
raphinha
|
||||
lluvia estrellas liridas
|
||||
frenkie de jong
|
||||
santos - coritiba
|
||||
del cerro grande
|
||||
phillip cocu
|
||||
cospedal
|
||||
david wilcock
|
||||
barcelona - celta de vigo
|
||||
barcelone – celta vigo
|
||||
macarena gómez
|
||||
luis merlo
|
||||
bournemouth vs leeds
|
||||
racing de estrasburgo - niza
|
||||
burnley vs man city
|
||||
alaska cantante
|
||||
raúl gonzález blanco
|
||||
marta sanchez
|
||||
roberto bautista
|
||||
cifras y letras
|
||||
xbox game pass ultimate
|
||||
horse powertrain
|
||||
las hurdes
|
||||
herbicida cancer colon
|
||||
lluvia de barro
|
||||
ripoll
|
||||
manilva
|
||||
steve jobs
|
||||
berrettini
|
||||
prizmic
|
||||
laura moure
|
||||
lens - toulouse
|
||||
girona vs real betis
|
||||
yuri berchiche
|
||||
clasificacion liga
|
||||
posiciones de la liga
|
||||
resultados liga
|
||||
lfp
|
||||
umar sadiq
|
||||
реал мадрид – алавес
|
||||
jan virgili
|
||||
moreirense - estoril
|
||||
downton abbey
|
||||
koldo garcía izaguirre
|
||||
jorge martín
|
||||
mike james
|
||||
carla leite
|
||||
eclipse solar del 12 de agosto de 2026
|
||||
crystal palace - west ham
|
||||
luz valdenebro
|
||||
porto - tondela
|
||||
santos - fluminense
|
||||
juventus
|
||||
almería - málaga
|
||||
punjab kings vs lucknow super giants standings
|
||||
mönchengladbach – mainz
|
||||
victor eloy
|
||||
paris-sg – lyon
|
||||
psg vs lyon
|
||||
zamora - osasuna b
|
||||
estrecho
|
||||
nurburgring
|
||||
la 1 directo
|
||||
oyarzabal
|
||||
enrique cerezo
|
||||
fraude
|
||||
tasa
|
||||
la 1
|
||||
tve directo
|
||||
cuántas copas del rey tiene la real sociedad
|
||||
eugenia martínez de irujo
|
||||
ccoo
|
||||
racing de santander
|
||||
racing
|
||||
baliza v16
|
||||
st. pauli – köln
|
||||
iphone 18
|
||||
st. pauli - colonia
|
||||
nico paz
|
||||
lionel messi
|
||||
armengol
|
||||
pau víctor
|
||||
braga fc
|
||||
fiorentina vs crystal palace
|
||||
morante hoy
|
||||
iago aspas
|
||||
aston villa
|
||||
real betis vs braga
|
||||
ministerio de sanidad
|
||||
az - shakhtar
|
||||
arsenal
|
||||
tiktok
|
||||
harry kane
|
||||
sudan
|
||||
lunin
|
||||
airef
|
||||
tiempo de juego
|
||||
fc bayern
|
||||
militao
|
||||
bellingham
|
||||
supervivientes
|
||||
jalen green
|
||||
rockstar games
|
||||
bam adebayo
|
||||
china
|
||||
suns - trail blazers
|
||||
euromillones
|
||||
ldu quito - mirassol
|
||||
davion mitchell
|
||||
universitario - coquimbo unido
|
||||
El País
|
||||
Marca
|
||||
RTVE Noticias
|
||||
La Liga
|
||||
Real Madrid
|
||||
Barcelona FC
|
||||
Tiempo Madrid
|
||||
Renfe
|
||||
Iberia
|
||||
Amazon España
|
||||
El Corte Inglés
|
||||
Hacienda
|
||||
Mercadona
|
||||
YouTube Música
|
||||
Entradas Cine
|
||||
167
data/keywords/kw_FR.txt
Normal file
167
data/keywords/kw_FR.txt
Normal file
@@ -0,0 +1,167 @@
|
||||
lena situation
|
||||
rafael jódar
|
||||
mask singer
|
||||
alex de minaur
|
||||
oyonnax – agen
|
||||
lucie castets
|
||||
napoli - cremonese
|
||||
ligue1+
|
||||
agnes lassalle
|
||||
jean luc reichmann
|
||||
m6
|
||||
caitlyn jenner
|
||||
star academy
|
||||
bfm marseille provence
|
||||
hunter schafer
|
||||
unchosen netflix
|
||||
john travolta
|
||||
valence-romans – brive
|
||||
reem kherici
|
||||
will smith
|
||||
real salt lake – inter miami
|
||||
neymar
|
||||
les traîtres
|
||||
jacob elordi
|
||||
pistons – magic
|
||||
invincible
|
||||
santos – coritiba
|
||||
ios 18
|
||||
la liga
|
||||
royaume uni interdiction tabac
|
||||
stephane tapie
|
||||
tondelier
|
||||
benjamin duhamel
|
||||
loto 22 avril 2026
|
||||
interdiction tabac royaume uni
|
||||
lol qui rit sort
|
||||
eva longoria
|
||||
alain bauer
|
||||
achraf hakimi
|
||||
ibrahim mbaye
|
||||
mma
|
||||
roberto calvet
|
||||
budget
|
||||
coupe du monde de football 2006
|
||||
plus belle la vie en avance
|
||||
mateus fernandes
|
||||
tour des alpes 2026
|
||||
lorenzo finn
|
||||
maine-et-loire
|
||||
mathieu flamini
|
||||
kamel daoud
|
||||
vandalisme
|
||||
sydney sweeney
|
||||
julien odoul
|
||||
france2
|
||||
xavier dupont de ligonnès
|
||||
elisabeth 2
|
||||
antibes
|
||||
girona – betis
|
||||
christine bravo
|
||||
الريال ضد ألافيس
|
||||
madonna age
|
||||
margot haddad
|
||||
ana riera
|
||||
hinaupoko devèze
|
||||
bruce toussaint
|
||||
cheque energie
|
||||
reid wiseman
|
||||
loto 20 avril 2026
|
||||
from serie
|
||||
toulouse
|
||||
racing 92 – stade français
|
||||
juventus - bologna
|
||||
film une annee difficile
|
||||
échouement
|
||||
programme tv ce soir
|
||||
porto – tondela
|
||||
matthieu pigasse
|
||||
santos – fluminense
|
||||
gta 6
|
||||
laetitia milot
|
||||
loto 18 avril 2026
|
||||
bercy
|
||||
pierre lellouche
|
||||
adele
|
||||
adil rami
|
||||
castres – toulouse
|
||||
angel
|
||||
stéphane bern
|
||||
anne claire coudray
|
||||
pmu résultat
|
||||
laury thilleman et paul mirabel
|
||||
quinté du jour
|
||||
euromillions 17 avril 2026
|
||||
uson
|
||||
guillaume meurice
|
||||
pmu
|
||||
grenoble – oyonnax
|
||||
bagarre
|
||||
programme télé
|
||||
tchernobyl
|
||||
géraldine maillet
|
||||
biot
|
||||
racing
|
||||
liga europa
|
||||
tv ce soir
|
||||
programme tv de ce soir
|
||||
brad pitt
|
||||
aston villa
|
||||
michael olise
|
||||
robert ménard
|
||||
match ce soir
|
||||
sporting
|
||||
ester exposito
|
||||
bellingham
|
||||
iptv
|
||||
militao
|
||||
jeff goldblum
|
||||
lunin
|
||||
kiev
|
||||
julien royal
|
||||
viktor orbán
|
||||
aqababe
|
||||
nhl
|
||||
suns – trail blazers
|
||||
bam adebayo
|
||||
davion mitchell
|
||||
l
|
||||
santos – recoleta football club
|
||||
atletico madrid
|
||||
tf1
|
||||
uefa champions league
|
||||
camille cerf
|
||||
giorgi mamardashvili
|
||||
streaming football
|
||||
atlético madryt – fc barcelona
|
||||
miss france
|
||||
rts
|
||||
leonardo balerdi
|
||||
yann barthes
|
||||
alain delon
|
||||
loto du 13 avril 2026
|
||||
juan arbeláez
|
||||
hbo
|
||||
katy perry justin trudeau
|
||||
tondela – gil vicente
|
||||
le rugbynistère
|
||||
epstein
|
||||
kino
|
||||
horoscope du 13 avril 2026
|
||||
golf masters augusta 2026
|
||||
boursorama bourse
|
||||
cac 40
|
||||
sept à huit
|
||||
ligne 12 métro
|
||||
alice taglioni
|
||||
pedro sánchez
|
||||
meteo paris
|
||||
actualités en direct
|
||||
résultats ligue 1
|
||||
pharmacie de garde
|
||||
horaires sncf
|
||||
recette crêpes
|
||||
cac 40 en direct
|
||||
acheter billet louvre
|
||||
boulangerie autour de moi
|
||||
carte vitale ameli
|
||||
162
data/keywords/kw_HK.txt
Normal file
162
data/keywords/kw_HK.txt
Normal file
@@ -0,0 +1,162 @@
|
||||
米高積遜
|
||||
jaafar jackson
|
||||
nvda
|
||||
吳業坤
|
||||
麻疹
|
||||
林盛斌
|
||||
cuaca besok
|
||||
明天的天氣
|
||||
weather tomorrow
|
||||
rthk
|
||||
weather hong kong
|
||||
天文台
|
||||
hk observatory
|
||||
observatory
|
||||
csk vs mi
|
||||
長沙灣
|
||||
魔音女團
|
||||
球迷世界
|
||||
天星銀行
|
||||
game
|
||||
林嘉華
|
||||
套餐
|
||||
賈曉晨
|
||||
hk weather
|
||||
綠色債券
|
||||
barcelona vs celta vigo
|
||||
weather
|
||||
7-eleven
|
||||
天气
|
||||
班來 對 曼城
|
||||
埃爾切 對 馬德里競技
|
||||
rosenior
|
||||
艾納斯
|
||||
liam rosenior
|
||||
巴黎聖日耳曼 對 南特
|
||||
chatgpt image 2
|
||||
啟德醫院
|
||||
李泳漢老婆
|
||||
破產
|
||||
繼承人
|
||||
英皇群星演唱會
|
||||
布浩榮
|
||||
新聞
|
||||
貨幣貶值
|
||||
居 屋 2025
|
||||
3988
|
||||
李泳豪老婆
|
||||
皇家馬德里 對 艾拉維斯
|
||||
李泳漢
|
||||
鄭欣宜
|
||||
srh vs dc
|
||||
鍾嘉欣
|
||||
張柏芝
|
||||
江美儀
|
||||
全港戲院日 2026
|
||||
江旻憓
|
||||
陶傑
|
||||
水晶宮 對 西漢姆聯
|
||||
吉達艾阿里
|
||||
山口智子
|
||||
百佳超級市場
|
||||
利息
|
||||
戴祖儀
|
||||
陳若思
|
||||
麥當勞
|
||||
首岸
|
||||
中国商飞c919
|
||||
pl
|
||||
bundesliga
|
||||
hailey bieber
|
||||
德甲
|
||||
cherki
|
||||
now
|
||||
曼城
|
||||
now tv
|
||||
al nassr
|
||||
洪金寶
|
||||
曼聯
|
||||
車路士
|
||||
切爾西 對 曼聯
|
||||
英超
|
||||
何沛珈
|
||||
熱刺
|
||||
tottenham vs brighton
|
||||
熱刺 對 布萊頓
|
||||
epl
|
||||
司機
|
||||
补贴
|
||||
華富邨
|
||||
零售
|
||||
藍莓
|
||||
商湯科技
|
||||
周國豐
|
||||
啟點
|
||||
歐聯
|
||||
神戶勝利船
|
||||
潘宏彬
|
||||
姚正菁
|
||||
木乃伊
|
||||
ios 26
|
||||
李克寧木乃伊
|
||||
田啟文
|
||||
曼寧加
|
||||
arsenal
|
||||
ucl
|
||||
歐洲聯賽冠軍盃
|
||||
arsenal vs sporting
|
||||
bayern vs real madrid
|
||||
real madrid
|
||||
皇馬
|
||||
拜仁慕尼黑 對 皇家馬德里
|
||||
claude
|
||||
補貼
|
||||
nba 直播
|
||||
航空公司
|
||||
向華強
|
||||
李嘉欣
|
||||
typhoon
|
||||
nba
|
||||
nba線上看
|
||||
nba直播
|
||||
全民國家安全教育日
|
||||
運輸署
|
||||
liverpool vs psg
|
||||
利物浦
|
||||
barcelona
|
||||
歐冠
|
||||
馬德里競技 對 巴塞隆納
|
||||
利物浦 對 巴黎聖日耳曼
|
||||
hkjc
|
||||
馬會
|
||||
航空
|
||||
2035
|
||||
man united vs leeds
|
||||
曼聯 對 里茲聯
|
||||
prediction market
|
||||
預測市場
|
||||
polymarket
|
||||
巴基斯坦
|
||||
sndk
|
||||
楊何蓓茵
|
||||
樂珈嘉
|
||||
姜濤
|
||||
日經平均指數
|
||||
飲茶
|
||||
上市公司
|
||||
daniel caesar
|
||||
中年好聲音4
|
||||
香港天文台
|
||||
煤氣
|
||||
livenation
|
||||
政府
|
||||
香港天文台天氣預報
|
||||
MTR 港鐵路線圖
|
||||
OpenRice 附近美食
|
||||
LIHKG 討論區
|
||||
恆生指數今日行情
|
||||
SCMP breaking news
|
||||
HKEX 港交所股價
|
||||
國泰航空航班狀態
|
||||
香港迪士尼樂園門票
|
||||
百佳超級市場網購
|
||||
@@ -1,7 +1,163 @@
|
||||
ベティス 対 rマドリード
|
||||
dior
|
||||
伊藤美来
|
||||
フィギュア
|
||||
玉置浩二
|
||||
生田絵梨花
|
||||
日本高野連会長辞任
|
||||
anaheim
|
||||
sbiグローバルアセットマネジメント
|
||||
ストレンジャーシングス
|
||||
石油
|
||||
ios 26.4 2
|
||||
ポケモンカード
|
||||
シュトゥットガルト 対 フライブルク
|
||||
ドラクエ スマグロ 攻略
|
||||
ipl
|
||||
kick
|
||||
アコム
|
||||
鈴木愛理
|
||||
sox指数
|
||||
村上世彰
|
||||
桐山 照史
|
||||
マラッカ海峡
|
||||
ソニック
|
||||
sbi新生銀行 株価
|
||||
マイクラ
|
||||
川口 春奈
|
||||
サンダー 対 サンズ
|
||||
インフルエンサー
|
||||
サンケイビル
|
||||
岡村隆史
|
||||
上田竜也
|
||||
メタプラネット
|
||||
バルセロナ 対 セルタ
|
||||
三橋くん
|
||||
エルニーニョ
|
||||
バーンリー 対 マンc
|
||||
ボーンマス 対 リーズ u
|
||||
elche vs atlético madrid
|
||||
レバークーゼン 対 バイエルン
|
||||
ソシエダ 対 ヘタフェ
|
||||
サイメモリ
|
||||
b リーグ 順位
|
||||
日ハム
|
||||
ナダル
|
||||
失点
|
||||
岩瀬洋志
|
||||
ファイターズ 試合
|
||||
江村美咲
|
||||
宝島社
|
||||
井ノ原 快彦
|
||||
ファイターズ
|
||||
西郷隆盛
|
||||
ボーダー
|
||||
チェルシー
|
||||
関税
|
||||
ブライトン
|
||||
佳子内親王
|
||||
rマドリード 対 アラベス
|
||||
鳥貴族
|
||||
ブライトン 対 チェルシー
|
||||
アシエンダ乗馬学校
|
||||
ngày trái đất
|
||||
加藤小夏
|
||||
町田ゼルビア
|
||||
モンスターハンターシリーズ
|
||||
クリスタル・パレス 対 ウェストハム
|
||||
吉田正尚
|
||||
旭琉會
|
||||
神戸
|
||||
てんちむ
|
||||
浜辺美波
|
||||
デーブ ロバーツ
|
||||
皇治
|
||||
小雪
|
||||
にじさんじデビュー
|
||||
ちゃんよた
|
||||
坂本 花織
|
||||
松山 千春
|
||||
リーグアン
|
||||
伊勢谷友介
|
||||
bayern vs vfb stuttgart
|
||||
chelsea vs man united
|
||||
唐田えりか
|
||||
102回目のプロポーズ
|
||||
デゼルビ
|
||||
アトレティコ 対 ソシエダ
|
||||
チェルシー 対 マンu
|
||||
スパーズ
|
||||
清春
|
||||
ディエゴ・ゴメス
|
||||
ps5
|
||||
ハイウェイ の 堕 天使 興行 収入
|
||||
カブス 対 メッツ
|
||||
dazn
|
||||
サッスオーロ 対 コモ
|
||||
杉咲花
|
||||
町田 対 アル・イテハド
|
||||
家計
|
||||
週末旅の極意
|
||||
北斗の拳
|
||||
qvc
|
||||
小芝風花
|
||||
中井亜美
|
||||
afc u20女子アジアカップ
|
||||
ネオジオ
|
||||
uefaヨーロッパリーグ
|
||||
加藤史帆
|
||||
志田未来
|
||||
伊藤英明
|
||||
島田麻央
|
||||
al-nassr vs al-ettifaq
|
||||
arsenal
|
||||
レアル・マドリード
|
||||
バイエルン
|
||||
abema
|
||||
real madrid
|
||||
lucknow super giants vs royal challengers bengaluru standings
|
||||
bayern vs real madrid
|
||||
給付
|
||||
wowow
|
||||
小泉進次郎
|
||||
政権
|
||||
ミキティ
|
||||
今日のドジャースの結果
|
||||
新名神高速道路
|
||||
わたせせいぞう
|
||||
ピーチ航空
|
||||
山本由伸
|
||||
アレックス・ベシア
|
||||
リバプール
|
||||
champions league
|
||||
アトレティコ 対 バルセロナ
|
||||
オープンワールド
|
||||
atlético madrid vs barcelona
|
||||
松田好花
|
||||
リコール
|
||||
man united vs leeds
|
||||
白鵬翔
|
||||
日本アカデミー賞 最優秀助演男優賞
|
||||
マンu 対 リーズ u
|
||||
サンディスク 株価
|
||||
らじるらじる
|
||||
マクドナルド
|
||||
ロシア
|
||||
広島市
|
||||
ゲイブル・スティーブソン
|
||||
日本維新の会
|
||||
新 日本 繊維
|
||||
高見沢 俊彦
|
||||
不登校
|
||||
後期高齢者医療制度
|
||||
バーミヤン
|
||||
宮澤エマ
|
||||
チケプラ
|
||||
横綱
|
||||
宮里美香
|
||||
東京 天気 明日
|
||||
新宿 おすすめ 居酒屋
|
||||
最新のニュース 速報
|
||||
ゴールド 相場 チャート
|
||||
近くの静かなカフェ
|
||||
地震 速報
|
||||
円安 影響 生活
|
||||
円安 影響 生活
|
||||
|
||||
110
data/keywords/kw_KR.txt
Normal file
110
data/keywords/kw_KR.txt
Normal file
@@ -0,0 +1,110 @@
|
||||
트와이스
|
||||
s
|
||||
엔비디아 주가
|
||||
이경실
|
||||
신발
|
||||
하현상
|
||||
김수희
|
||||
gt vs rcb
|
||||
야닉 시너
|
||||
amd 주가
|
||||
왕다루
|
||||
시그널
|
||||
모하메드 살라
|
||||
공화당
|
||||
윤하정
|
||||
청남대
|
||||
더보이즈
|
||||
노민우
|
||||
서비스나우
|
||||
김민지
|
||||
박해미
|
||||
국가유산청
|
||||
삼성전자 주가
|
||||
두산에너빌리티 주가
|
||||
김성수
|
||||
한국사
|
||||
삼성전기 주가
|
||||
국립중앙박물관
|
||||
대우건설 주가
|
||||
지방 선거
|
||||
서정대학교
|
||||
캔바
|
||||
한국항공우주산업
|
||||
디트로이트 대 올랜도
|
||||
리그 1
|
||||
elche vs atlético madrid
|
||||
알나스르
|
||||
al-nassr vs al ahli
|
||||
10
|
||||
로세니어
|
||||
psg 대 낭트
|
||||
테슬라 실적발표
|
||||
루카스 모우라
|
||||
9950x3d2
|
||||
삼천당 제약 간담회
|
||||
김재윤
|
||||
k리그1 순위
|
||||
유영찬
|
||||
이호연
|
||||
sxmb
|
||||
진
|
||||
문재인
|
||||
생산직
|
||||
강훈식
|
||||
롤
|
||||
cnn
|
||||
리그오브레전드
|
||||
박환희
|
||||
박지현
|
||||
카토
|
||||
와우넷
|
||||
지구의 날
|
||||
컴프야
|
||||
차지연
|
||||
크리스털 팰리스 대 웨스트 햄
|
||||
비상계엄
|
||||
연기금
|
||||
배틀그라운드
|
||||
제이홉
|
||||
두산로보틱스
|
||||
부부
|
||||
미노이 우원재
|
||||
danish malewar
|
||||
mi vs gt
|
||||
양치 승
|
||||
프리미어리그
|
||||
pl
|
||||
bundesliga
|
||||
맨체스터 시티 fc
|
||||
fotmob
|
||||
윤아
|
||||
김영인
|
||||
манчестер сити – арсенал
|
||||
케이뱅크
|
||||
날씨
|
||||
환율
|
||||
손흥민
|
||||
토트넘 경기 일정
|
||||
넷플릭스
|
||||
국민은행
|
||||
네이버웹툰
|
||||
로또 당첨번호
|
||||
쿠팡
|
||||
KBO 리그
|
||||
프로야구 순위
|
||||
아이폰 15
|
||||
챗gpt
|
||||
인스타그램
|
||||
유튜브 밴스드
|
||||
이강인
|
||||
김민재
|
||||
LCK 일정
|
||||
T1
|
||||
페이커
|
||||
무빙
|
||||
카카오톡 PC버전
|
||||
당근마켓
|
||||
배달의민족
|
||||
올리브영
|
||||
메이플스토리
|
||||
44
data/keywords/kw_MY.txt
Normal file
44
data/keywords/kw_MY.txt
Normal file
@@ -0,0 +1,44 @@
|
||||
sunderland vs nottm forest
|
||||
real betis lwn real madrid cf
|
||||
napoli vs cremonese
|
||||
real betis vs real madrid
|
||||
perfect crown ep 5
|
||||
siti mastura mohamad
|
||||
jannik sinner
|
||||
sai sudharsan
|
||||
yaris cross hybrid
|
||||
cretaceous kraken
|
||||
rayo vallecano vs espanyol
|
||||
dji lito x1
|
||||
levante vs sevilla
|
||||
danish malewar
|
||||
mukesh choudhary
|
||||
lyrid meteor showers
|
||||
jamie overton
|
||||
piala uber
|
||||
madrid open
|
||||
dewald brevis
|
||||
counter strike
|
||||
pistons vs magic
|
||||
real salt lake vs inter miami
|
||||
chart gpt
|
||||
asiago
|
||||
liza hanim
|
||||
good morning
|
||||
pandikar amin mulia
|
||||
勒沃库森 - 拜仁
|
||||
santos vs coritiba
|
||||
Malaysia
|
||||
Kuala Lumpur
|
||||
Bursa Malaysia
|
||||
Ringgit
|
||||
Maybank
|
||||
CIMB
|
||||
Shopee MY
|
||||
Lazada Malaysia
|
||||
KWSP i-Akaun
|
||||
LHDN MyTax
|
||||
Anwar Ibrahim
|
||||
Johor Bahru
|
||||
Penang
|
||||
Nasi Lemak
|
||||
146
data/keywords/kw_NL.txt
Normal file
146
data/keywords/kw_NL.txt
Normal file
@@ -0,0 +1,146 @@
|
||||
wesley sonck
|
||||
arnold schwarzenegger
|
||||
devil wears prada
|
||||
jagiellonia białystok – górnik zabrze
|
||||
unchosen
|
||||
max verstappen redbull
|
||||
mvv - top oss
|
||||
energiecontract
|
||||
real betis - real madrid
|
||||
fc den bosch
|
||||
maarten van rossem
|
||||
jake paul
|
||||
financiën
|
||||
zonnepaneel
|
||||
xrp
|
||||
cyprus
|
||||
verdachte
|
||||
112 eerbeek
|
||||
golden earring
|
||||
sergiño dest
|
||||
david wilcock
|
||||
asiago
|
||||
shownieuws
|
||||
nijmegen
|
||||
keira knightley
|
||||
112 meldingen amsterdam
|
||||
dierenmishandeling
|
||||
112 amsterdam
|
||||
freek rikkerink
|
||||
poetin
|
||||
atalanta - lazio
|
||||
mr nobody against putin
|
||||
club brugge - mechelen
|
||||
frank hosmar
|
||||
barcelona - celta
|
||||
kees flodder
|
||||
burnley - manchester city
|
||||
lodewijk asscher
|
||||
vallende sterren
|
||||
guus meeuwis
|
||||
hoofddorp
|
||||
kaja kallas
|
||||
politiek
|
||||
stroomstoring amersfoort
|
||||
solvinity digid
|
||||
wout weghorst
|
||||
jamie kames
|
||||
bitvavo
|
||||
box 3
|
||||
ronde van de alpen
|
||||
matthias uhl
|
||||
taken film
|
||||
robot
|
||||
girona - real betis
|
||||
real madryt – deportivo alaves
|
||||
tbilisi
|
||||
southampton - bristol city
|
||||
vittoria guazzini
|
||||
oppo find x9 ultra
|
||||
real madrid - alavés
|
||||
الريال ضد ألافيس
|
||||
lecce - fiorentina
|
||||
pogoda jutro
|
||||
vierhouten
|
||||
weather tomorrow
|
||||
over mijn lijk 2026 overleden
|
||||
crystal palace - west ham
|
||||
f-16 fighting falcon
|
||||
klagenfurt
|
||||
italië
|
||||
ruud gullit
|
||||
perfil falso
|
||||
psg - lyon
|
||||
juventus - bologna
|
||||
troy parrott
|
||||
az - nec opstellingen
|
||||
caroline tensen
|
||||
asielbeleid
|
||||
philippe sandler
|
||||
keerbergen
|
||||
peer koopmeiners
|
||||
nederlands elftal
|
||||
pogoń szczecin – lech poznań
|
||||
stand premier league
|
||||
bulgarije
|
||||
chelsea - man utd
|
||||
atlético madrid - real sociedad
|
||||
xavi simons
|
||||
ayase ueda
|
||||
roma - atalanta
|
||||
rtv noord
|
||||
paraguay
|
||||
sergio herman
|
||||
fed
|
||||
keuken kampioen
|
||||
legia warszawa – zagłębie lubin
|
||||
ripple
|
||||
voorzitter fed
|
||||
bahamas
|
||||
overtreding
|
||||
almere city
|
||||
real betis
|
||||
sean connery
|
||||
l1 nieuws
|
||||
ronaldinho
|
||||
demi de boer
|
||||
bondgenoten
|
||||
frank masmeijer
|
||||
real betis - braga
|
||||
ethereum
|
||||
aston villa - bologna
|
||||
manuel neuer
|
||||
neuer
|
||||
olise
|
||||
mbappe
|
||||
sporting
|
||||
live tv
|
||||
bayern munchen
|
||||
arda güler
|
||||
ziggo
|
||||
arda guler
|
||||
netflix
|
||||
frenkie de jong
|
||||
kanye west
|
||||
vandaag inside
|
||||
at5
|
||||
veroordeling
|
||||
verenigde staten
|
||||
alec baldwin
|
||||
anna paulowna
|
||||
şampiyonlar ligi
|
||||
NOS Nieuws
|
||||
Buienradar
|
||||
Rijksoverheid
|
||||
Albert Heijn
|
||||
Funda
|
||||
Marktplaats
|
||||
KLM
|
||||
Ziggo
|
||||
ING Bank
|
||||
Eredivisie
|
||||
Amsterdam Weer
|
||||
Bol.com
|
||||
Treinkaartjes NS
|
||||
PostNL
|
||||
Pathé
|
||||
168
data/keywords/kw_SG.txt
Normal file
168
data/keywords/kw_SG.txt
Normal file
@@ -0,0 +1,168 @@
|
||||
sunderland vs nottm forest
|
||||
real betis vs real madrid
|
||||
anne hathaway
|
||||
michael jackson
|
||||
sai sudharsan
|
||||
madrid open
|
||||
amd
|
||||
gt vs rcb
|
||||
ijooz
|
||||
raghav chadha
|
||||
darren wang
|
||||
mukesh choudhary
|
||||
danish malewar
|
||||
tamil nadu election results
|
||||
allah ghazanfar
|
||||
peregrine falcon
|
||||
airasia flight
|
||||
arrest
|
||||
hao mart singapore
|
||||
kartik sharma
|
||||
real salt lake vs inter miami
|
||||
pistons vs magic
|
||||
krisflyer
|
||||
michael jackson biopic
|
||||
新加坡
|
||||
atalanta vs lazio
|
||||
伯恩利 - 曼城
|
||||
porto vs sporting
|
||||
cdl directors resign millennium copthorne
|
||||
tsla
|
||||
leverkusen vs bayern
|
||||
burnley vs man city
|
||||
elche vs atlético madrid
|
||||
psg vs nantes
|
||||
al-nassr vs al ahli
|
||||
mohsin khan
|
||||
changi
|
||||
rr vs lsg
|
||||
primary school
|
||||
alexandra eala
|
||||
tan su shan
|
||||
sundaresh menon
|
||||
moulin rouge musical singapore
|
||||
south korea
|
||||
step
|
||||
gamba osaka vs avispa fukuoka
|
||||
honor 600 pro
|
||||
china shipyards oil tanker orders
|
||||
coe
|
||||
lebron james
|
||||
real madrid vs alavés
|
||||
brighton vs chelsea
|
||||
nitish rana
|
||||
earth day
|
||||
michael movie
|
||||
kevin warsh
|
||||
srh vs dc
|
||||
chinese aircraft carrier liaoning
|
||||
andie chen
|
||||
jesseca liu wins best actress
|
||||
crystal palace vs west ham
|
||||
grok
|
||||
gemini
|
||||
claude ai
|
||||
gpt
|
||||
ai
|
||||
is chatgpt down
|
||||
strait of hormuz news
|
||||
mi vs gt
|
||||
psg vs lyon
|
||||
sporting vs benfica
|
||||
bayern munich
|
||||
pl
|
||||
bangkok weather
|
||||
starhub
|
||||
廖子妤
|
||||
曼城 - 阿森纳
|
||||
bayern vs vfb stuttgart
|
||||
英超
|
||||
chelsea vs man united
|
||||
perfect crown
|
||||
ayush mhatre
|
||||
sarfaraz khan
|
||||
napoli vs lazio
|
||||
tottenham vs brighton
|
||||
abhishek sharma
|
||||
tinie tempah
|
||||
wrexham vs stoke city
|
||||
sassuolo vs como
|
||||
the straits times
|
||||
india women vs south africa women
|
||||
beef season 2
|
||||
loyang valley
|
||||
world cup 2026
|
||||
afc champions league
|
||||
hormuz
|
||||
malacca strait
|
||||
kkr vs gt
|
||||
aston villa vs bologna
|
||||
mumbai indians vs punjab kings standings
|
||||
al sadd vs vissel kobe
|
||||
amd share price
|
||||
opus 4.7
|
||||
pete hegseth
|
||||
naman dhir
|
||||
yen singapore dollar
|
||||
mayank rawat
|
||||
dji pocket 4
|
||||
real madrid
|
||||
al-nassr vs al-ettifaq
|
||||
bayern vs real madrid
|
||||
arsenal vs sporting
|
||||
lucknow super giants vs royal challengers bengaluru standings
|
||||
is claude down
|
||||
claude
|
||||
allbirds
|
||||
red sea
|
||||
rcb vs lsg
|
||||
retirement
|
||||
asia flights delays cancellations
|
||||
suns vs trail blazers
|
||||
johnny somali
|
||||
bam adebayo
|
||||
zhang linghe pursuit of jade
|
||||
roman gofman
|
||||
cruz azul vs lafc
|
||||
ocbc
|
||||
santos vs recoleta
|
||||
atlético madrid vs barcelona
|
||||
ipl schedule
|
||||
liverpool vs psg
|
||||
iran blockade strait of hormuz
|
||||
kartik tyagi
|
||||
carlos alcaraz
|
||||
propertylimbrothers
|
||||
byeon woo-seok
|
||||
mahathir mohamad
|
||||
csk vs kkr
|
||||
man united vs leeds
|
||||
cbse class 10 result 2026 date
|
||||
euphoria season 3
|
||||
srh vs rr
|
||||
tamil new year 2026
|
||||
low de wei
|
||||
pope
|
||||
flexar
|
||||
microsoft outlook
|
||||
new rolex 2026
|
||||
medical classification
|
||||
blasphemy law
|
||||
big bang coachella 2026
|
||||
小贩
|
||||
malaysia fuel price crisis
|
||||
sbti personality test
|
||||
cancer survivor
|
||||
tim cook
|
||||
spurs vs nuggets
|
||||
asia flights cancelled delayed
|
||||
singapore weather forecast
|
||||
mrt map singapore
|
||||
straitstimes breaking news
|
||||
cpf board login
|
||||
hdb bto launch updates
|
||||
best chicken rice near me
|
||||
public holidays sg
|
||||
singpass login portal
|
||||
changi airport flight status
|
||||
iras tax filing
|
||||
122
data/keywords/kw_TW.txt
Normal file
122
data/keywords/kw_TW.txt
Normal file
@@ -0,0 +1,122 @@
|
||||
cpo概念股
|
||||
anaheim
|
||||
iu
|
||||
山口智子
|
||||
蔡依珊
|
||||
林岱安
|
||||
michael jackson 電影
|
||||
行動電源
|
||||
地震
|
||||
地震速報
|
||||
宋祖兒
|
||||
porter
|
||||
謝京穎
|
||||
德州儀器
|
||||
台新綜合證券
|
||||
希臘
|
||||
費城半導體
|
||||
mrvl
|
||||
狄鶯
|
||||
三星罷工
|
||||
00981a
|
||||
馬斯克
|
||||
辛耘
|
||||
宏碁股價
|
||||
0056
|
||||
2330
|
||||
雷霆 對 太陽
|
||||
0050股價
|
||||
006208
|
||||
景碩
|
||||
女警陳芊雯
|
||||
廢死聯盟
|
||||
西甲
|
||||
台灣積體電路製造
|
||||
高頻寬 記憶 體
|
||||
戴奧辛
|
||||
3665
|
||||
宋晟睿
|
||||
黃 甘霖
|
||||
劉家翔
|
||||
王耿豪
|
||||
王勝偉
|
||||
009816
|
||||
thời tiết ngày mai
|
||||
明天的天氣
|
||||
總部
|
||||
美光科技
|
||||
cpbl
|
||||
中華職棒
|
||||
中職
|
||||
黃甘霖
|
||||
軍 公教
|
||||
6147
|
||||
earth day
|
||||
陳哲遠
|
||||
台鐵
|
||||
斯普拉遁 塗擊隊
|
||||
麥可傑克森
|
||||
神將彩券行
|
||||
米可白
|
||||
洪敬堯
|
||||
華邦電子
|
||||
柔美的細胞小將 3
|
||||
底特律老虎
|
||||
三 商 美邦
|
||||
川 普
|
||||
英雄聯盟
|
||||
聯電adr
|
||||
rklb
|
||||
amd stock
|
||||
宋仲基
|
||||
兵工廠
|
||||
德甲
|
||||
塞爾提克 對 76人
|
||||
justin bieber 演唱會
|
||||
拜仁慕尼黑
|
||||
何潤東
|
||||
廖子妤
|
||||
英超
|
||||
許凱
|
||||
tottenham vs brighton
|
||||
長野縣
|
||||
交通安全
|
||||
朋友收集夢想生活
|
||||
麥克傑克森
|
||||
王濛
|
||||
騎士 對 暴龍
|
||||
熱刺 對 布萊頓
|
||||
iem rio 2026
|
||||
garret anderson
|
||||
墓乃伊
|
||||
曹格
|
||||
claude design
|
||||
柯文哲
|
||||
金剛
|
||||
荷 姆 茲 海峽
|
||||
東北 季風
|
||||
斯圖加特公開賽
|
||||
歐聯
|
||||
菡生婦幼診所
|
||||
台鐵訂票
|
||||
飛機
|
||||
東光路
|
||||
货币
|
||||
amd
|
||||
航空母艦
|
||||
axti
|
||||
Yahoo奇摩
|
||||
天氣
|
||||
蝦皮購物
|
||||
PChome
|
||||
Momo購物網
|
||||
Mobile01
|
||||
Dcard
|
||||
巴哈姆特
|
||||
中時電子報
|
||||
聯合新聞網
|
||||
台灣高鐵
|
||||
台鐵時刻表
|
||||
中華電信
|
||||
統一發票
|
||||
勞動部
|
||||
171
data/keywords/kw_UK.txt
Normal file
171
data/keywords/kw_UK.txt
Normal file
@@ -0,0 +1,171 @@
|
||||
napoli
|
||||
alex de minaur
|
||||
tigers vs hull fc
|
||||
edinburgh vs sharks
|
||||
mark williams snooker
|
||||
criminal record
|
||||
alex sinclair israeli police detention
|
||||
st. johnstone vs raith rovers
|
||||
mo farah
|
||||
newcastle vs bristol
|
||||
tbilisi
|
||||
connor storrie
|
||||
georgia
|
||||
stephen bunting
|
||||
great british menu 2026
|
||||
ravi eastenders
|
||||
salford city fc
|
||||
national grid
|
||||
mass effect
|
||||
salford city vs bromley
|
||||
real salt lake vs inter miami
|
||||
provinces of the pantheon
|
||||
mls
|
||||
russell brand
|
||||
john phelan
|
||||
hung cao
|
||||
pistons vs magic
|
||||
jerry bruckheimer
|
||||
st. george's day
|
||||
asiago
|
||||
framework laptop 13 pro
|
||||
boro
|
||||
interactive investor
|
||||
mint
|
||||
believe me itv
|
||||
amanda bynes
|
||||
strasbourg vs nice
|
||||
chase bank
|
||||
jak jones
|
||||
chase
|
||||
rodri
|
||||
christopher trybus
|
||||
lsg vs rr
|
||||
pension
|
||||
hebden bridge
|
||||
sandra bullock practical magic
|
||||
rolls-royce share price drop
|
||||
invincible season 5
|
||||
josé mourinho
|
||||
chris wakelin
|
||||
jimmy bullard adam thomas
|
||||
aer lingus flight cancellations
|
||||
sam west
|
||||
oxford united
|
||||
liam delap
|
||||
wba
|
||||
реал мадрид – алавес
|
||||
coppa italia
|
||||
girona vs real betis
|
||||
marcus rashford
|
||||
الريال ضد ألافيس
|
||||
supreme leader of iran
|
||||
oscar isaac
|
||||
xrp ledger
|
||||
pablo
|
||||
is tane leaving home and away
|
||||
julie andrews
|
||||
danny boyle
|
||||
nina eastenders
|
||||
john stones
|
||||
amazon vega os fire tv
|
||||
porto vs tondela
|
||||
santos vs fluminense
|
||||
martin brundle lost f1 seat
|
||||
keegan bradley
|
||||
david attenborough
|
||||
antoni kowalski
|
||||
kezia dugdale
|
||||
car
|
||||
beef netflix
|
||||
juventus vs bologna
|
||||
losc vs nice
|
||||
david szalay
|
||||
the killer
|
||||
joe cole
|
||||
lille fc
|
||||
simon cowell
|
||||
pl
|
||||
frank lampard everton
|
||||
nottingham forest fixtures
|
||||
everton manager
|
||||
dragons vs bulls
|
||||
suede
|
||||
lahore
|
||||
wrestlemania 2026
|
||||
giants vs rhinos
|
||||
glenrothan
|
||||
york knights vs leopards
|
||||
tim sherwood
|
||||
redditch
|
||||
ccfc
|
||||
europa conference league
|
||||
bromley fc
|
||||
paul merton
|
||||
chris wood
|
||||
istanbul
|
||||
turkey
|
||||
lucy watson
|
||||
thiago silva
|
||||
bednarek
|
||||
jan bednarek
|
||||
vincent kompany
|
||||
mbappe
|
||||
luis suarez sporting
|
||||
madrid fc
|
||||
andriy lunin
|
||||
what did bec say to rachel mafs
|
||||
yalla kora
|
||||
geovany quenda
|
||||
sporting cp
|
||||
pavlovic
|
||||
talktalk
|
||||
arne slot drops mohamed salah
|
||||
suns vs trail blazers
|
||||
italian
|
||||
used cars
|
||||
mlb
|
||||
roman
|
||||
johnny somali
|
||||
windows update
|
||||
davion mitchell
|
||||
hbo max
|
||||
bolton wanderers
|
||||
barca vs atletico
|
||||
kemi badenoch
|
||||
warren zaïre-emery
|
||||
barca
|
||||
samuel west
|
||||
barcelona fc
|
||||
lamine yamal
|
||||
hbomax
|
||||
noah okafor
|
||||
casemiro
|
||||
talksport
|
||||
lazio
|
||||
leeds united fixtures
|
||||
bruno fernandes
|
||||
afc champions league
|
||||
meteor
|
||||
carlos queiroz
|
||||
travel warning
|
||||
tori amos
|
||||
cloud
|
||||
reading
|
||||
rolls-royce smr
|
||||
istanbul airport
|
||||
a27
|
||||
bridget phillipson
|
||||
tottenham standings
|
||||
may bank holiday 2026
|
||||
toto wolff
|
||||
london weather today
|
||||
bbc news latest
|
||||
premier league fixtures
|
||||
tesco near me
|
||||
tube map london
|
||||
uk bank holidays
|
||||
royal family news
|
||||
how to make english tea
|
||||
nhs symptom checker
|
||||
property for sale in london
|
||||
@@ -1,6 +1,174 @@
|
||||
alex de minaur
|
||||
rafael jodar
|
||||
adam levine face
|
||||
howard frankland bridge
|
||||
vibrio vulnificus new york waters
|
||||
stade brestois - lens
|
||||
moustapha thiam
|
||||
us asylum seeker border reopening
|
||||
sunderland vs nottm forest
|
||||
leicester city vs millwall
|
||||
brandon marsh
|
||||
psv vs pec zwolle
|
||||
daniel merida aguilar
|
||||
william byron
|
||||
eve plumb
|
||||
mall of louisiana
|
||||
real oviedo vs villarreal
|
||||
real oviedo - villarreal
|
||||
call of duty: black ops 7
|
||||
tarik skubal
|
||||
cubs game today
|
||||
mike repole
|
||||
okc thunder
|
||||
atlanta braves
|
||||
mlb scores today
|
||||
braves standings
|
||||
kevin mckidd
|
||||
dan vladar
|
||||
mls standings
|
||||
dodgers - giants
|
||||
jon ossoff
|
||||
ozzy survivor
|
||||
braves score
|
||||
washington nationals
|
||||
alex bregman
|
||||
peter lambert
|
||||
darrell sheets
|
||||
ludwig kaiser
|
||||
edina shooting
|
||||
atalanta - lazio
|
||||
barcelona vs celta vigo
|
||||
rivian r2
|
||||
pick 4
|
||||
aoc
|
||||
cleveland browns
|
||||
nintendo switch gamecube games
|
||||
lsg vs rr
|
||||
spirit
|
||||
atlanta fire
|
||||
deshaun watson
|
||||
good morning america
|
||||
pittsburgh
|
||||
strands answers
|
||||
scientist
|
||||
mark cuban pharmacy
|
||||
inter milan
|
||||
lens vs toulouse
|
||||
david james
|
||||
andy weir
|
||||
noah kahan tiny desk concert
|
||||
jennifer garner 54th birthday photos
|
||||
troy baker
|
||||
real madrid - alavés
|
||||
girona - real betis
|
||||
girona vs real betis
|
||||
arc raiders
|
||||
schd etf dividend yield
|
||||
deportación
|
||||
lecce - fiorentina
|
||||
valley forge high school
|
||||
betty yee
|
||||
seth trimble
|
||||
bill belichick
|
||||
winter storm
|
||||
jim parsons
|
||||
kings vs avalanche
|
||||
suns vs thunder
|
||||
wolf
|
||||
santos - fluminense
|
||||
mets - cubs
|
||||
alexander manninger
|
||||
santos vs fluminense
|
||||
disclosure day
|
||||
tobias myers
|
||||
vladimir putin
|
||||
knicks game
|
||||
ben rice
|
||||
prem
|
||||
timberwolves vs nuggets
|
||||
cody bellinger
|
||||
nik khamenia
|
||||
real sociedad
|
||||
nurburgring crash
|
||||
atlético madrid - real sociedad
|
||||
ruke orhorhoro
|
||||
radar
|
||||
the weather channel
|
||||
kttc
|
||||
luke gulbranson
|
||||
kttc weather
|
||||
comcast data breach settlement
|
||||
tornado watch
|
||||
moisés ballesteros
|
||||
mets game today
|
||||
giancarlo stanton
|
||||
real betis
|
||||
prosecution of daniel duggan
|
||||
liv morgan
|
||||
mikey williams
|
||||
indiana fever sophie cunningham baptism
|
||||
gregory donnell morgan jr
|
||||
why are the sirens going off
|
||||
leylah fernandez
|
||||
strasbourg vs mainz
|
||||
michael olise
|
||||
ريال مدريد
|
||||
dazn
|
||||
paramount
|
||||
univision
|
||||
jude bellingham
|
||||
sam antonacci
|
||||
real madrid
|
||||
bayern
|
||||
arda güler
|
||||
los angeles dodgers
|
||||
vandenberg launch schedule
|
||||
ryan dunn
|
||||
alex vesia
|
||||
ken jennings
|
||||
ucla baseball
|
||||
padres standings
|
||||
mets vs dodgers match player stats
|
||||
bo bichette
|
||||
jorge polanco
|
||||
psg
|
||||
barca
|
||||
vix
|
||||
fcb
|
||||
barcelona schedule
|
||||
tarjeta roja
|
||||
a knight of the seven kingdoms season 2
|
||||
charlotte flair
|
||||
usa network
|
||||
natalie sago
|
||||
carlos queiroz
|
||||
carlos batista
|
||||
katie boulter
|
||||
levante - getafe
|
||||
levante vs getafe
|
||||
mcilroy green jacket presentation
|
||||
man united vs leeds
|
||||
7-eleven closing locations
|
||||
cloud
|
||||
sports
|
||||
sony playstation
|
||||
alaska airline
|
||||
toronto
|
||||
sydney
|
||||
paris
|
||||
tokyo
|
||||
delhi
|
||||
sykkuno drama
|
||||
Los Angeles weather today
|
||||
S&P 500 stock chart
|
||||
local coffee shops near me
|
||||
latest tech news
|
||||
California traffic updates
|
||||
AI startups in Silicon Valley
|
||||
AI startups in Silicon ValleySan Jose weather this weekend
|
||||
Silicon Valley tech news
|
||||
best tacos in San Jose
|
||||
Apple park visitor center hours
|
||||
Seattle Weather
|
||||
Las Vegas strip
|
||||
Charlotte Hornets
|
||||
|
||||
146
data/keywords/kw_VN.txt
Normal file
146
data/keywords/kw_VN.txt
Normal file
@@ -0,0 +1,146 @@
|
||||
napoli
|
||||
sunderland đấu với nottm forest
|
||||
betis đấu với real madrid
|
||||
napoli đấu với cremonese
|
||||
leipzig đấu với union berlin
|
||||
sunderland
|
||||
mẫu iphone 18
|
||||
oppo find x9 ultra
|
||||
xem phim
|
||||
andoni iraola
|
||||
stuttgart đấu với freiburg
|
||||
levante – sevilla
|
||||
giao dịch tài chính
|
||||
sevilla
|
||||
rayo đấu với espanyol
|
||||
levante đấu với sevilla
|
||||
bong ma hanh phuc
|
||||
aryna sabalenka
|
||||
mason nguyễn
|
||||
marcus rashford
|
||||
hà nội
|
||||
salt lake đấu với inter miami
|
||||
hung cao
|
||||
neymar
|
||||
cầu phú mỹ
|
||||
nhac
|
||||
wordle
|
||||
날씨
|
||||
giá cà phê hôm nay
|
||||
premier league standings
|
||||
mls
|
||||
phạm nhật vượng
|
||||
cristiano roland
|
||||
burnley vs man city
|
||||
bayern munich
|
||||
atalanta đấu với lazio
|
||||
barcelona đấu với celta
|
||||
mc vs
|
||||
psg – nantes
|
||||
leverkusen đấu với bayern
|
||||
elche – atlético madrid
|
||||
bryan mbeumo
|
||||
al nasr
|
||||
trực tiếp bóng đá hôm nay
|
||||
vtv3
|
||||
vtv3 trực tiếp
|
||||
tv
|
||||
vtv
|
||||
vtv6
|
||||
360
|
||||
lê khánh
|
||||
ô tô
|
||||
lê phương
|
||||
trực tiếp bóng đá u17 hôm nay
|
||||
inter milan
|
||||
chelsea
|
||||
girona đấu với betis
|
||||
real madrid đấu với alavés
|
||||
brighton vs chelsea
|
||||
brighton đấu với chelsea
|
||||
ath. bilbao đấu với osasuna
|
||||
pep guardiola
|
||||
zelvia đấu với shabab al-ahli
|
||||
cầu thủ
|
||||
lecce vs fiorentina
|
||||
fiorentina
|
||||
crystal palace đấu với west ham
|
||||
afc champions league
|
||||
eduardo camavinga
|
||||
vissel kobe
|
||||
gemi
|
||||
idp
|
||||
bảo hiểm y tế
|
||||
crystal palace vs west ham
|
||||
sporting lisbon
|
||||
psg vs lyon
|
||||
ca sĩ
|
||||
juventus đấu với bologna
|
||||
gladbach đấu với mainz
|
||||
twitch
|
||||
psg đấu với lyon
|
||||
juventus
|
||||
ligue 1
|
||||
trận đấu ngoại hạng anh
|
||||
chelsea đấu với man utd
|
||||
atlético madrid đấu với real sociedad
|
||||
roma đấu với atalanta
|
||||
epl
|
||||
iem rio 2026
|
||||
tot
|
||||
tập đoàn gelex
|
||||
đường ray
|
||||
inter
|
||||
inter đấu với cagliari
|
||||
sassuolo vs como
|
||||
david alaba
|
||||
claude design
|
||||
fenerbahçe đấu với rizespor
|
||||
como
|
||||
como vs
|
||||
thẻ đỏ
|
||||
al ahli
|
||||
porto vs
|
||||
crystal palace
|
||||
porto
|
||||
uefa europa conference
|
||||
betis đấu với braga
|
||||
real betis vs braga
|
||||
aston villa đấu với bologna
|
||||
fiorentina đấu với crystal palace
|
||||
c2
|
||||
cup c2
|
||||
thể thao
|
||||
arda güler
|
||||
aleksandar pavlović
|
||||
ars
|
||||
90
|
||||
real
|
||||
real madrid
|
||||
xoi
|
||||
luong sơn
|
||||
fpt
|
||||
phan văn giang
|
||||
nhà ở xã hội
|
||||
club america
|
||||
giàu
|
||||
đỗ mỹ linh
|
||||
sun group
|
||||
hưng yên
|
||||
nvl
|
||||
américa đấu với nashville
|
||||
VnExpress
|
||||
Zing News
|
||||
Thời tiết Hà Nội
|
||||
Giá vàng hôm nay
|
||||
Shopee VN
|
||||
Tiki
|
||||
Vietjet Air
|
||||
Vietnam Airlines
|
||||
Bóng đá trực tuyến
|
||||
Lịch thi đấu Euro
|
||||
Xổ số miền Bắc
|
||||
Grab Vietnam
|
||||
VTV Go
|
||||
Học tiếng Anh
|
||||
Du lịch Đà Lạt
|
||||
407
data/map.json
Normal file
407
data/map.json
Normal file
@@ -0,0 +1,407 @@
|
||||
{
|
||||
"version": "3.5.2",
|
||||
"updated_at": "2026-04-19",
|
||||
"continents": [
|
||||
{
|
||||
"id": "ASIA",
|
||||
"name": "亚太战区 (Asia-Pacific)",
|
||||
"countries": [
|
||||
{
|
||||
"id": "JP",
|
||||
"name": "Japan (日本)",
|
||||
"keyword_file": "kw_JP.txt",
|
||||
"states": [
|
||||
{
|
||||
"id": "Default",
|
||||
"name": "Default State",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Tokyo",
|
||||
"name": "Tokyo (东京)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "SG",
|
||||
"name": "Singapore (新加坡)",
|
||||
"keyword_file": "kw_SG.txt",
|
||||
"states": [
|
||||
{
|
||||
"id": "Default",
|
||||
"name": "Default State",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Singapore",
|
||||
"name": "Singapore (新加坡)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "KR",
|
||||
"name": "South Korea (韩国)",
|
||||
"keyword_file": "kw_KR.txt",
|
||||
"states": [
|
||||
{
|
||||
"id": "Default",
|
||||
"name": "Default State",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Seoul",
|
||||
"name": "Seoul (首尔)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "HK",
|
||||
"name": "Hong Kong (香港)",
|
||||
"keyword_file": "kw_HK.txt",
|
||||
"states": [
|
||||
{
|
||||
"id": "Default",
|
||||
"name": "Default State",
|
||||
"cities": [
|
||||
{
|
||||
"id": "HongKong",
|
||||
"name": "Hong Kong (香港)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "VN",
|
||||
"name": "Vietnam (越南)",
|
||||
"keyword_file": "kw_VN.txt",
|
||||
"states": [
|
||||
{
|
||||
"id": "Default",
|
||||
"name": "Default State",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Hanoi",
|
||||
"name": "Hanoi (河内)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "TW",
|
||||
"name": "Taiwan (台湾)",
|
||||
"keyword_file": "kw_TW.txt",
|
||||
"states": [
|
||||
{
|
||||
"id": "Default",
|
||||
"name": "Default State",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Taipei",
|
||||
"name": "Taipei (台北)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "MY",
|
||||
"name": "Malaysia (马来西亚)",
|
||||
"keyword_file": "kw_MY.txt",
|
||||
"states": [
|
||||
{
|
||||
"id": "Default",
|
||||
"name": "Default State",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Kuala_Lumpur",
|
||||
"name": "Kuala Lumpur (吉隆坡)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "EUROPE",
|
||||
"name": "欧洲战区 (Europe)",
|
||||
"countries": [
|
||||
{
|
||||
"id": "UK",
|
||||
"name": "United Kingdom (英国)",
|
||||
"keyword_file": "kw_UK.txt",
|
||||
"states": [
|
||||
{
|
||||
"id": "Default",
|
||||
"name": "Default State",
|
||||
"cities": [
|
||||
{
|
||||
"id": "London",
|
||||
"name": "London (伦敦)"
|
||||
},
|
||||
{
|
||||
"id": "Coventry",
|
||||
"name": "Coventry (考文垂)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "DE",
|
||||
"name": "Germany (德国)",
|
||||
"keyword_file": "kw_DE.txt",
|
||||
"states": [
|
||||
{
|
||||
"id": "Default",
|
||||
"name": "Default State",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Frankfurt",
|
||||
"name": "Frankfurt (法兰克福)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "FR",
|
||||
"name": "France (法国)",
|
||||
"keyword_file": "kw_FR.txt",
|
||||
"states": [
|
||||
{
|
||||
"id": "Default",
|
||||
"name": "Default State",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Paris",
|
||||
"name": "Paris (巴黎)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "NL",
|
||||
"name": "Netherlands (荷兰)",
|
||||
"keyword_file": "kw_NL.txt",
|
||||
"states": [
|
||||
{
|
||||
"id": "Default",
|
||||
"name": "Default State",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Amsterdam",
|
||||
"name": "Amsterdam (阿姆斯特丹)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ES",
|
||||
"name": "Spain (西班牙)",
|
||||
"keyword_file": "kw_ES.txt",
|
||||
"states": [
|
||||
{
|
||||
"id": "Default",
|
||||
"name": "Default State",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Madrid",
|
||||
"name": "Madrid (马德里)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "AMERICAS",
|
||||
"name": "美洲战区 (Americas)",
|
||||
"countries": [
|
||||
{
|
||||
"id": "US",
|
||||
"name": "United States (美国)",
|
||||
"keyword_file": "kw_US.txt",
|
||||
"states": [
|
||||
{
|
||||
"id": "CA",
|
||||
"name": "California (加州)",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Los_Angeles",
|
||||
"name": "Los Angeles (洛杉矶)"
|
||||
},
|
||||
{
|
||||
"id": "San_Jose",
|
||||
"name": "San Jose (圣何塞)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "IL",
|
||||
"name": "Illinois (伊利诺伊州)",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Warrenville",
|
||||
"name": "Warrenville (沃伦维尔)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "NC",
|
||||
"name": "North Carolina (北卡罗来纳州)",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Charlotte",
|
||||
"name": "Charlotte (夏洛特)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "NV",
|
||||
"name": "Nevada (内华达州)",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Las_Vegas",
|
||||
"name": "Las Vegas (拉斯维加斯)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "OR",
|
||||
"name": "Oregon (俄勒冈州)",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Bend",
|
||||
"name": "Bend (本德)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "UT",
|
||||
"name": "Utah (犹他州)",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Salt_Lake_City",
|
||||
"name": "Salt Lake City (盐湖城)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "WA",
|
||||
"name": "Washington (华盛顿州)",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Seattle",
|
||||
"name": "Seattle (西雅图)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "TX",
|
||||
"name": "Texas (得克萨斯州)",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Dallas",
|
||||
"name": "Dallas (达拉斯)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "CA",
|
||||
"name": "Canada (加拿大)",
|
||||
"keyword_file": "kw_CA.txt",
|
||||
"states": [
|
||||
{
|
||||
"id": "Default",
|
||||
"name": "Default State",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Toronto",
|
||||
"name": "Toronto (多伦多)"
|
||||
},
|
||||
{
|
||||
"id": "Montreal",
|
||||
"name": "Montreal (蒙特利尔)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "OCEANIA",
|
||||
"name": "大洋洲战区 (Oceania)",
|
||||
"countries": [
|
||||
{
|
||||
"id": "AU",
|
||||
"name": "Australia (澳大利亚)",
|
||||
"keyword_file": "kw_AU.txt",
|
||||
"states": [
|
||||
{
|
||||
"id": "NSW",
|
||||
"name": "New South Wales (新南威尔士州)",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Sydney",
|
||||
"name": "Sydney (悉尼)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "VIC",
|
||||
"name": "Victoria (维多利亚州)",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Melbourne",
|
||||
"name": "Melbourne (墨尔本)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "QLD",
|
||||
"name": "Queensland (昆士兰州)",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Brisbane",
|
||||
"name": "Brisbane (布里斯班)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "WA",
|
||||
"name": "Western Australia (西澳大利亚州)",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Perth",
|
||||
"name": "Perth (珀斯)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "SA",
|
||||
"name": "South Australia (南澳大利亚州)",
|
||||
"cities": [
|
||||
{
|
||||
"id": "Adelaide",
|
||||
"name": "Adelaide (阿德莱德)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
50
data/regions/AU/NSW/Sydney.json
Normal file
50
data/regions/AU/NSW/Sydney.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"region_name": "Australia - Sydney",
|
||||
"google_module": {
|
||||
"base_lat": -33.8697,
|
||||
"base_lon": 151.2085,
|
||||
"lang_params": "hl=en-AU&gl=AU",
|
||||
"valid_url_suffix": "com.au"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.abc.net.au/news/2026-04-24/india-delimitation-redraw-electoral-map-southern-states/106591486",
|
||||
"https://www.seek.com.au/",
|
||||
"https://www.abc.net.au/news/2026-04-24/kezie-apps-says-womens-state-of-origin-timing-not-ideal/106604306",
|
||||
"https://www.abc.net.au/news/2026-04-25/outcome-farrer-byelection-one-nation-watershed-moment/106485956",
|
||||
"https://www.woolworths.com.au/",
|
||||
"https://www.abc.net.au/news/2026-04-25/how-iran-regime-changed-after-us-israel-middle-east-war/106595956",
|
||||
"https://my.gov.au/",
|
||||
"https://www.ato.gov.au/",
|
||||
"https://www.abc.net.au/news/2026-04-25/sa-wwii-veterans-meet-after-more-than-80-years/106590394",
|
||||
"https://www.abc.net.au/news/2026-04-25/nato-falklands-spain/106604984",
|
||||
"https://www.abc.net.au/news/2026-04-24/act-government-will-not-cancel-big-splash-operators-lease/106603388",
|
||||
"https://www.abc.net.au/news/2026-04-24/norway-plans-to-introduce-under-16s-social-media-ban/106604296",
|
||||
"https://www.abc.net.au/news/2026-04-24/oyster-growing-closed-again-toxic-phytoplankton-yorke-peninsula/106601670",
|
||||
"https://www.abc.net.au/news/2026-04-24/woman-chooses-to-go-blind-after-delays-at-adelaide-clinic/106602574",
|
||||
"https://www.amazon.com.au/",
|
||||
"https://www.commbank.com.au/",
|
||||
"https://www.abc.net.au/news/2026-04-25/how-the-cost-of-the-ndis-blew-out/106604488",
|
||||
"https://www.bom.gov.au/",
|
||||
"https://www.bunnings.com.au/",
|
||||
"https://www.abc.net.au/news/2026-04-24/nt-doctor-gross-indecency-rape-trial-court-suppression-order/106603984",
|
||||
"https://www.realestate.com.au/",
|
||||
"https://www.coles.com.au/",
|
||||
"https://www.abc.net.au/news/2026-04-24/nrl-live-updates-cowboys-sharks-broncos-bulldogs/106591508",
|
||||
"https://www.abc.net.au/news/2026-04-25/netanyahu-treated-for-prostate-cancer/106605028",
|
||||
"https://www.abc.net.au/news/2026-04-25/april-25-5050-quiz/106601084"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://my.gov.au/",
|
||||
"https://www.ato.gov.au/",
|
||||
"https://www.bom.gov.au/",
|
||||
"https://www.commbank.com.au/",
|
||||
"https://www.seek.com.au/",
|
||||
"https://www.realestate.com.au/",
|
||||
"https://www.woolworths.com.au/",
|
||||
"https://www.coles.com.au/",
|
||||
"https://www.amazon.com.au/",
|
||||
"https://www.bunnings.com.au/"
|
||||
]
|
||||
}
|
||||
}
|
||||
50
data/regions/AU/QLD/Brisbane.json
Normal file
50
data/regions/AU/QLD/Brisbane.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"region_name": "Australia - Brisbane",
|
||||
"google_module": {
|
||||
"base_lat": -27.4697,
|
||||
"base_lon": 153.0252,
|
||||
"lang_params": "hl=en-AU&gl=AU",
|
||||
"valid_url_suffix": "com.au"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.abc.net.au/news/2026-04-24/act-government-will-not-cancel-big-splash-operators-lease/106603388",
|
||||
"https://www.ato.gov.au/",
|
||||
"https://www.abc.net.au/news/2026-04-25/netanyahu-treated-for-prostate-cancer/106605028",
|
||||
"https://www.coles.com.au/",
|
||||
"https://www.seek.com.au/",
|
||||
"https://www.abc.net.au/news/2026-04-24/india-delimitation-redraw-electoral-map-southern-states/106591486",
|
||||
"https://www.abc.net.au/news/2026-04-25/how-the-cost-of-the-ndis-blew-out/106604488",
|
||||
"https://www.abc.net.au/news/2026-04-24/norway-plans-to-introduce-under-16s-social-media-ban/106604296",
|
||||
"https://www.abc.net.au/news/2026-04-24/oyster-growing-closed-again-toxic-phytoplankton-yorke-peninsula/106601670",
|
||||
"https://www.abc.net.au/news/2026-04-25/sa-wwii-veterans-meet-after-more-than-80-years/106590394",
|
||||
"https://www.abc.net.au/news/2026-04-25/nato-falklands-spain/106604984",
|
||||
"https://www.abc.net.au/news/2026-04-25/april-25-5050-quiz/106601084",
|
||||
"https://www.abc.net.au/news/2026-04-24/nt-doctor-gross-indecency-rape-trial-court-suppression-order/106603984",
|
||||
"https://www.abc.net.au/news/2026-04-24/nrl-live-updates-cowboys-sharks-broncos-bulldogs/106591508",
|
||||
"https://www.woolworths.com.au/",
|
||||
"https://www.commbank.com.au/",
|
||||
"https://www.bom.gov.au/",
|
||||
"https://www.abc.net.au/news/2026-04-24/woman-chooses-to-go-blind-after-delays-at-adelaide-clinic/106602574",
|
||||
"https://www.amazon.com.au/",
|
||||
"https://www.abc.net.au/news/2026-04-24/kezie-apps-says-womens-state-of-origin-timing-not-ideal/106604306",
|
||||
"https://www.realestate.com.au/",
|
||||
"https://my.gov.au/",
|
||||
"https://www.abc.net.au/news/2026-04-25/how-iran-regime-changed-after-us-israel-middle-east-war/106595956",
|
||||
"https://www.abc.net.au/news/2026-04-25/outcome-farrer-byelection-one-nation-watershed-moment/106485956",
|
||||
"https://www.bunnings.com.au/"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://my.gov.au/",
|
||||
"https://www.ato.gov.au/",
|
||||
"https://www.bom.gov.au/",
|
||||
"https://www.commbank.com.au/",
|
||||
"https://www.seek.com.au/",
|
||||
"https://www.realestate.com.au/",
|
||||
"https://www.woolworths.com.au/",
|
||||
"https://www.coles.com.au/",
|
||||
"https://www.amazon.com.au/",
|
||||
"https://www.bunnings.com.au/"
|
||||
]
|
||||
}
|
||||
}
|
||||
50
data/regions/AU/SA/Adelaide.json
Normal file
50
data/regions/AU/SA/Adelaide.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"region_name": "Australia - Adelaide",
|
||||
"google_module": {
|
||||
"base_lat": -34.9227,
|
||||
"base_lon": 138.6016,
|
||||
"lang_params": "hl=en-AU&gl=AU",
|
||||
"valid_url_suffix": "com.au"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.bom.gov.au/",
|
||||
"https://www.amazon.com.au/",
|
||||
"https://www.abc.net.au/news/2026-04-24/norway-plans-to-introduce-under-16s-social-media-ban/106604296",
|
||||
"https://www.abc.net.au/news/2026-04-25/outcome-farrer-byelection-one-nation-watershed-moment/106485956",
|
||||
"https://www.abc.net.au/news/2026-04-25/april-25-5050-quiz/106601084",
|
||||
"https://www.abc.net.au/news/2026-04-24/woman-chooses-to-go-blind-after-delays-at-adelaide-clinic/106602574",
|
||||
"https://www.abc.net.au/news/2026-04-24/nrl-live-updates-cowboys-sharks-broncos-bulldogs/106591508",
|
||||
"https://www.bunnings.com.au/",
|
||||
"https://www.realestate.com.au/",
|
||||
"https://www.commbank.com.au/",
|
||||
"https://www.ato.gov.au/",
|
||||
"https://www.abc.net.au/news/2026-04-25/netanyahu-treated-for-prostate-cancer/106605028",
|
||||
"https://www.abc.net.au/news/2026-04-24/kezie-apps-says-womens-state-of-origin-timing-not-ideal/106604306",
|
||||
"https://www.coles.com.au/",
|
||||
"https://www.abc.net.au/news/2026-04-24/act-government-will-not-cancel-big-splash-operators-lease/106603388",
|
||||
"https://www.seek.com.au/",
|
||||
"https://www.abc.net.au/news/2026-04-25/how-the-cost-of-the-ndis-blew-out/106604488",
|
||||
"https://www.woolworths.com.au/",
|
||||
"https://www.abc.net.au/news/2026-04-25/sa-wwii-veterans-meet-after-more-than-80-years/106590394",
|
||||
"https://my.gov.au/",
|
||||
"https://www.abc.net.au/news/2026-04-24/india-delimitation-redraw-electoral-map-southern-states/106591486",
|
||||
"https://www.abc.net.au/news/2026-04-24/oyster-growing-closed-again-toxic-phytoplankton-yorke-peninsula/106601670",
|
||||
"https://www.abc.net.au/news/2026-04-24/nt-doctor-gross-indecency-rape-trial-court-suppression-order/106603984",
|
||||
"https://www.abc.net.au/news/2026-04-25/how-iran-regime-changed-after-us-israel-middle-east-war/106595956",
|
||||
"https://www.abc.net.au/news/2026-04-25/nato-falklands-spain/106604984"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://my.gov.au/",
|
||||
"https://www.ato.gov.au/",
|
||||
"https://www.bom.gov.au/",
|
||||
"https://www.commbank.com.au/",
|
||||
"https://www.seek.com.au/",
|
||||
"https://www.realestate.com.au/",
|
||||
"https://www.woolworths.com.au/",
|
||||
"https://www.coles.com.au/",
|
||||
"https://www.amazon.com.au/",
|
||||
"https://www.bunnings.com.au/"
|
||||
]
|
||||
}
|
||||
}
|
||||
50
data/regions/AU/VIC/Melbourne.json
Normal file
50
data/regions/AU/VIC/Melbourne.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"region_name": "Australia - Melbourne",
|
||||
"google_module": {
|
||||
"base_lat": -37.8106,
|
||||
"base_lon": 144.9624,
|
||||
"lang_params": "hl=en-AU&gl=AU",
|
||||
"valid_url_suffix": "com.au"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.abc.net.au/news/2026-04-24/nt-doctor-gross-indecency-rape-trial-court-suppression-order/106603984",
|
||||
"https://www.ato.gov.au/",
|
||||
"https://www.abc.net.au/news/2026-04-24/kezie-apps-says-womens-state-of-origin-timing-not-ideal/106604306",
|
||||
"https://www.abc.net.au/news/2026-04-24/woman-chooses-to-go-blind-after-delays-at-adelaide-clinic/106602574",
|
||||
"https://www.bunnings.com.au/",
|
||||
"https://www.coles.com.au/",
|
||||
"https://www.abc.net.au/news/2026-04-25/netanyahu-treated-for-prostate-cancer/106605028",
|
||||
"https://www.abc.net.au/news/2026-04-25/sa-wwii-veterans-meet-after-more-than-80-years/106590394",
|
||||
"https://www.realestate.com.au/",
|
||||
"https://www.abc.net.au/news/2026-04-24/oyster-growing-closed-again-toxic-phytoplankton-yorke-peninsula/106601670",
|
||||
"https://www.abc.net.au/news/2026-04-24/india-delimitation-redraw-electoral-map-southern-states/106591486",
|
||||
"https://www.abc.net.au/news/2026-04-24/norway-plans-to-introduce-under-16s-social-media-ban/106604296",
|
||||
"https://www.amazon.com.au/",
|
||||
"https://www.abc.net.au/news/2026-04-25/april-25-5050-quiz/106601084",
|
||||
"https://www.abc.net.au/news/2026-04-25/nato-falklands-spain/106604984",
|
||||
"https://www.abc.net.au/news/2026-04-25/how-the-cost-of-the-ndis-blew-out/106604488",
|
||||
"https://www.seek.com.au/",
|
||||
"https://www.commbank.com.au/",
|
||||
"https://www.bom.gov.au/",
|
||||
"https://www.abc.net.au/news/2026-04-25/how-iran-regime-changed-after-us-israel-middle-east-war/106595956",
|
||||
"https://my.gov.au/",
|
||||
"https://www.woolworths.com.au/",
|
||||
"https://www.abc.net.au/news/2026-04-24/nrl-live-updates-cowboys-sharks-broncos-bulldogs/106591508",
|
||||
"https://www.abc.net.au/news/2026-04-24/act-government-will-not-cancel-big-splash-operators-lease/106603388",
|
||||
"https://www.abc.net.au/news/2026-04-25/outcome-farrer-byelection-one-nation-watershed-moment/106485956"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://my.gov.au/",
|
||||
"https://www.ato.gov.au/",
|
||||
"https://www.bom.gov.au/",
|
||||
"https://www.commbank.com.au/",
|
||||
"https://www.seek.com.au/",
|
||||
"https://www.realestate.com.au/",
|
||||
"https://www.woolworths.com.au/",
|
||||
"https://www.coles.com.au/",
|
||||
"https://www.amazon.com.au/",
|
||||
"https://www.bunnings.com.au/"
|
||||
]
|
||||
}
|
||||
}
|
||||
50
data/regions/AU/WA/Perth.json
Normal file
50
data/regions/AU/WA/Perth.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"region_name": "Australia - Perth",
|
||||
"google_module": {
|
||||
"base_lat": -31.9545,
|
||||
"base_lon": 115.8582,
|
||||
"lang_params": "hl=en-AU&gl=AU",
|
||||
"valid_url_suffix": "com.au"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.abc.net.au/news/2026-04-24/oyster-growing-closed-again-toxic-phytoplankton-yorke-peninsula/106601670",
|
||||
"https://www.abc.net.au/news/2026-04-24/india-delimitation-redraw-electoral-map-southern-states/106591486",
|
||||
"https://www.abc.net.au/news/2026-04-25/outcome-farrer-byelection-one-nation-watershed-moment/106485956",
|
||||
"https://www.abc.net.au/news/2026-04-25/netanyahu-treated-for-prostate-cancer/106605028",
|
||||
"https://my.gov.au/",
|
||||
"https://www.abc.net.au/news/2026-04-25/april-25-5050-quiz/106601084",
|
||||
"https://www.abc.net.au/news/2026-04-24/woman-chooses-to-go-blind-after-delays-at-adelaide-clinic/106602574",
|
||||
"https://www.seek.com.au/",
|
||||
"https://www.ato.gov.au/",
|
||||
"https://www.abc.net.au/news/2026-04-24/nt-doctor-gross-indecency-rape-trial-court-suppression-order/106603984",
|
||||
"https://www.abc.net.au/news/2026-04-24/norway-plans-to-introduce-under-16s-social-media-ban/106604296",
|
||||
"https://www.commbank.com.au/",
|
||||
"https://www.abc.net.au/news/2026-04-25/how-the-cost-of-the-ndis-blew-out/106604488",
|
||||
"https://www.bunnings.com.au/",
|
||||
"https://www.abc.net.au/news/2026-04-25/sa-wwii-veterans-meet-after-more-than-80-years/106590394",
|
||||
"https://www.abc.net.au/news/2026-04-24/nrl-live-updates-cowboys-sharks-broncos-bulldogs/106591508",
|
||||
"https://www.amazon.com.au/",
|
||||
"https://www.coles.com.au/",
|
||||
"https://www.woolworths.com.au/",
|
||||
"https://www.abc.net.au/news/2026-04-24/act-government-will-not-cancel-big-splash-operators-lease/106603388",
|
||||
"https://www.abc.net.au/news/2026-04-25/nato-falklands-spain/106604984",
|
||||
"https://www.abc.net.au/news/2026-04-25/how-iran-regime-changed-after-us-israel-middle-east-war/106595956",
|
||||
"https://www.abc.net.au/news/2026-04-24/kezie-apps-says-womens-state-of-origin-timing-not-ideal/106604306",
|
||||
"https://www.realestate.com.au/",
|
||||
"https://www.bom.gov.au/"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://my.gov.au/",
|
||||
"https://www.ato.gov.au/",
|
||||
"https://www.bom.gov.au/",
|
||||
"https://www.commbank.com.au/",
|
||||
"https://www.seek.com.au/",
|
||||
"https://www.realestate.com.au/",
|
||||
"https://www.woolworths.com.au/",
|
||||
"https://www.coles.com.au/",
|
||||
"https://www.amazon.com.au/",
|
||||
"https://www.bunnings.com.au/"
|
||||
]
|
||||
}
|
||||
}
|
||||
46
data/regions/CA/Default/Montreal.json
Normal file
46
data/regions/CA/Default/Montreal.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"region_name": "Canada - Montreal",
|
||||
"google_module": {
|
||||
"base_lat": 45.5017,
|
||||
"base_lon": -73.5673,
|
||||
"lang_params": "hl=en&gl=CA",
|
||||
"valid_url_suffix": "ca"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.cbc.ca/music/taylor-kirk-timber-timbre-obit-9.7176057?cmp=rss",
|
||||
"https://www.cbc.ca/sports/hockey/pwhl/pwhl-super-saturday-regular-season-finale-april-2026-9.7174583?cmp=rss",
|
||||
"https://www.cbc.ca/news/canada/hamilton/cooney-hamber-trial-decision-date-9.7174773?cmp=rss",
|
||||
"https://www.cbc.ca/news/canada/kitchener-waterloo/livestory/jeffrey-sloka-sexual-assault-trial-ruling-decision-9.7175767?cmp=rss",
|
||||
"https://www.cbc.ca/news/politics/ukraine-war-nato-carignan-trump-9.7175303?cmp=rss",
|
||||
"https://www.cbc.ca/books/canadian-poet-karen-solie-wins-239k-windham-campbell-prize-9.7173005?cmp=rss",
|
||||
"https://www.cbc.ca/news/canada/toronto/ticketmaster-delist-resale-tickets-ontario-9.7175775?cmp=rss",
|
||||
"https://www.cra-arc.gc.ca/",
|
||||
"https://www.amazon.ca/",
|
||||
"https://www.cbc.ca/news/canada/nova-scotia/tiffany-cameron-halifax-tides-northern-super-league-women-s-soccer-anthem-victory-9.7174310?cmp=rss",
|
||||
"https://www.cbc.ca/news/politics/trudeau-china-economic-coercion-9.7175918?cmp=rss",
|
||||
"https://www.td.com/",
|
||||
"https://www.canada.ca/en.html",
|
||||
"https://www.cbc.ca/news/canada/calgary/powerful-winds-calgary-damage-9.7176144?cmp=rss",
|
||||
"https://www.cbc.ca/news/canada/calgary/ottawa-approves-enbridge-s-4b-sunrise-natural-gas-pipeline-expansion-project-9.7175787?cmp=rss",
|
||||
"https://www.rbcroyalbank.com/",
|
||||
"https://www.canadapost-postescanada.ca/",
|
||||
"https://www.cbc.ca/news/investigates/ancestral-remains-wolfe-island-ontario-9.7174129?cmp=rss",
|
||||
"https://www.cbc.ca/news/science/climate-summit-transition-fossil-fuels-9.7175158?cmp=rss",
|
||||
"https://www.cbc.ca/news/canada/calgary/analysis-alberta-electoral-boundary-new-maps-danielle-smith-naheed-nenshi-9.7175296?cmp=rss",
|
||||
"https://www.theweathernetwork.com/ca",
|
||||
"https://www.cbc.ca/news/canada/manitoba/death-threats-genocide-winnipegger-charged-9.7175921?cmp=rss",
|
||||
"https://www.utoronto.ca/"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.canada.ca/en.html",
|
||||
"https://www.cra-arc.gc.ca/",
|
||||
"https://www.canadapost-postescanada.ca/",
|
||||
"https://www.utoronto.ca/",
|
||||
"https://www.td.com/",
|
||||
"https://www.rbcroyalbank.com/",
|
||||
"https://www.amazon.ca/",
|
||||
"https://www.theweathernetwork.com/ca"
|
||||
]
|
||||
}
|
||||
}
|
||||
46
data/regions/CA/Default/Toronto.json
Normal file
46
data/regions/CA/Default/Toronto.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"region_name": "Canada - Toronto",
|
||||
"google_module": {
|
||||
"base_lat": 43.6532,
|
||||
"base_lon": -79.3832,
|
||||
"lang_params": "hl=en&gl=CA",
|
||||
"valid_url_suffix": "ca"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.canada.ca/en.html",
|
||||
"https://www.cbc.ca/sports/hockey/pwhl/pwhl-super-saturday-regular-season-finale-april-2026-9.7174583?cmp=rss",
|
||||
"https://www.cbc.ca/news/politics/ukraine-war-nato-carignan-trump-9.7175303?cmp=rss",
|
||||
"https://www.cbc.ca/news/canada/calgary/ottawa-approves-enbridge-s-4b-sunrise-natural-gas-pipeline-expansion-project-9.7175787?cmp=rss",
|
||||
"https://www.cbc.ca/news/science/climate-summit-transition-fossil-fuels-9.7175158?cmp=rss",
|
||||
"https://www.cbc.ca/news/canada/manitoba/death-threats-genocide-winnipegger-charged-9.7175921?cmp=rss",
|
||||
"https://www.canadapost-postescanada.ca/",
|
||||
"https://www.cbc.ca/news/investigates/ancestral-remains-wolfe-island-ontario-9.7174129?cmp=rss",
|
||||
"https://www.cbc.ca/news/canada/nova-scotia/tiffany-cameron-halifax-tides-northern-super-league-women-s-soccer-anthem-victory-9.7174310?cmp=rss",
|
||||
"https://www.theweathernetwork.com/ca",
|
||||
"https://www.cbc.ca/news/canada/calgary/analysis-alberta-electoral-boundary-new-maps-danielle-smith-naheed-nenshi-9.7175296?cmp=rss",
|
||||
"https://www.cbc.ca/music/taylor-kirk-timber-timbre-obit-9.7176057?cmp=rss",
|
||||
"https://www.td.com/",
|
||||
"https://www.cbc.ca/news/canada/hamilton/cooney-hamber-trial-decision-date-9.7174773?cmp=rss",
|
||||
"https://www.cbc.ca/news/canada/kitchener-waterloo/livestory/jeffrey-sloka-sexual-assault-trial-ruling-decision-9.7175767?cmp=rss",
|
||||
"https://www.cra-arc.gc.ca/",
|
||||
"https://www.cbc.ca/books/canadian-poet-karen-solie-wins-239k-windham-campbell-prize-9.7173005?cmp=rss",
|
||||
"https://www.amazon.ca/",
|
||||
"https://www.rbcroyalbank.com/",
|
||||
"https://www.cbc.ca/news/canada/calgary/powerful-winds-calgary-damage-9.7176144?cmp=rss",
|
||||
"https://www.utoronto.ca/",
|
||||
"https://www.cbc.ca/news/canada/toronto/ticketmaster-delist-resale-tickets-ontario-9.7175775?cmp=rss",
|
||||
"https://www.cbc.ca/news/politics/trudeau-china-economic-coercion-9.7175918?cmp=rss"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.canada.ca/en.html",
|
||||
"https://www.cra-arc.gc.ca/",
|
||||
"https://www.canadapost-postescanada.ca/",
|
||||
"https://www.utoronto.ca/",
|
||||
"https://www.td.com/",
|
||||
"https://www.rbcroyalbank.com/",
|
||||
"https://www.amazon.ca/",
|
||||
"https://www.theweathernetwork.com/ca"
|
||||
]
|
||||
}
|
||||
}
|
||||
46
data/regions/DE/Default/Frankfurt.json
Normal file
46
data/regions/DE/Default/Frankfurt.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"region_name": "Germany - Frankfurt",
|
||||
"google_module": {
|
||||
"base_lat": 50.1109,
|
||||
"base_lon": 8.6821,
|
||||
"lang_params": "hl=de&gl=DE",
|
||||
"valid_url_suffix": "de"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.amazon.de/",
|
||||
"https://www.tagesschau.de/ausland/europa/impfstoffallianz-malaria-who-100.html",
|
||||
"https://www.tagesschau.de/ausland/europa/eu-gipfel-zypern-iran-krieg-ukraine-100.html",
|
||||
"https://www.tagesschau.de/ausland/europa/norwegen-social-media-verbot-100.html",
|
||||
"https://www.tagesschau.de/ausland/europa/eu-gipfel-zypern-106.html",
|
||||
"https://www.tagesschau.de/ausland/europa/eu-gipfel-zypern-102.html",
|
||||
"https://www.tagesschau.de/wirtschaft/konjunktur/ifo-index-stimmung-wirtschaft-100.html",
|
||||
"https://www.arbeitsagentur.de/",
|
||||
"https://www.bahn.de/",
|
||||
"https://www.tum.de/",
|
||||
"https://www.bundesregierung.de/",
|
||||
"https://www.sparkasse.de/",
|
||||
"https://www.tagesschau.de/ausland/amerika/ermittlungen-powell-eingestellt-100.html",
|
||||
"https://www.tagesschau.de/ausland/europa/ukraine-odessa-tote-100.html",
|
||||
"https://www.tagesschau.de/inland/innenpolitik/rente-debatte-100.html",
|
||||
"https://www.tagesschau.de/inland/regional/berlin/berliner-kultursenatorin-ruecktritt-100.html",
|
||||
"https://www.tagesschau.de/wirtschaft/verbraucher/spritpreise-tanken-102.html",
|
||||
"https://www.spiegel.de/",
|
||||
"https://www.tagesschau.de/ausland/asien/faq-israel-libanon-usa-waffenruhe-100.html",
|
||||
"https://www.tagesschau.de/inland/bundestag-tankrabatt-praemie-100.html",
|
||||
"https://www.ebay.de/",
|
||||
"https://www.tagesschau.de/wirtschaft/finanzen/marktberichte/marktbericht-130.html",
|
||||
"https://www.tagesschau.de/inland/regional/brandenburg/flughafen-ryanair-rueckzug-berlin-100.html"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.bundesregierung.de/",
|
||||
"https://www.arbeitsagentur.de/",
|
||||
"https://www.tum.de/",
|
||||
"https://www.sparkasse.de/",
|
||||
"https://www.bahn.de/",
|
||||
"https://www.amazon.de/",
|
||||
"https://www.ebay.de/",
|
||||
"https://www.spiegel.de/"
|
||||
]
|
||||
}
|
||||
}
|
||||
44
data/regions/ES/Default/Madrid.json
Normal file
44
data/regions/ES/Default/Madrid.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"region_name": "Spain - Madrid",
|
||||
"google_module": {
|
||||
"base_lat": 40.4168,
|
||||
"base_lon": -3.7038,
|
||||
"lang_params": "hl=es&gl=ES",
|
||||
"valid_url_suffix": "es"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://elpais.com/internacional/2026-04-24/trump-envia-a-pakistan-a-sus-dos-principales-negociadores-para-retomar-el-dialogo-con-teheran.html",
|
||||
"https://elpais.com/cultura/2026-04-24/xavier-lozano-y-la-historia-detras-de-la-flauta-silla-de-amaia-vinieron-a-verme-y-lo-hizo-bastante-bien.html",
|
||||
"https://elpais.com/economia/negocios/2026-04-19/joseph-stiglitz-premio-nobel-de-economia-la-ideologia-de-los-millonarios-tiene-actualmente-un-grado-de-egoismo-alucinante.html",
|
||||
"https://elpais.com/icon/2026-04-23/una-experiencia-inmersiva-para-redefinir-el-automovil-como-espacio-vital.html",
|
||||
"https://elpais.com/salud-y-bienestar/2026-04-24/el-14-de-los-adultos-espanoles-padece-insomnio.html",
|
||||
"https://www.amazon.es/",
|
||||
"https://elpais.com/espana/catalunya/2026-04-24/illa-reivindica-su-fomento-del-catalan-en-sant-jordi-en-medio-de-las-criticas-de-la-oposicion.html",
|
||||
"https://elpais.com/economia/2026-04-24/bayer-espera-cerrar-pronto-las-demandas-por-el-glifosato-con-un-desembolso-legal-que-ronda-los-24000-millones.html",
|
||||
"https://elpais.com/gente/2026-04-24/donald-trump-responde-a-las-palabras-de-enrique-de-inglaterra-en-ucrania-yo-represento-mas-al-reino-unido-que-el.html",
|
||||
"https://www.zara.com/es/",
|
||||
"https://elpais.com/gente/2026-04-24/al-actor-billy-porter-no-le-importa-que-no-le-inviten-a-la-gala-del-met-desde-2019-ya-no-necesito-la-validacion-de-los-demas.html",
|
||||
"https://www.elcorteingles.es/",
|
||||
"https://elpais.com/videos/2026-04-24/tras-los-noes-de-rajoy-y-cospedal-ahora-que.html",
|
||||
"https://administracion.gob.es/",
|
||||
"https://elpais.com/eps/2026-04-24/lilia-mendez-una-mujer-contra-el-olvido-de-la-artesania-textil-gallega.html",
|
||||
"https://www.santander.es/",
|
||||
"https://elpais.com/economia/2026-04-24/espana-se-suma-a-italia-y-pide-a-bruselas-suspender-las-reglas-fiscales-por-la-crisis-energetica.html",
|
||||
"https://elpais.com/eps/2026-04-21/roberto-paparcone-el-arquitecto-que-dejo-su-trabajo-para-moldear-barro.html",
|
||||
"https://elpais.com/economia/vivienda/2026-04-24/consumo-intensifica-la-presion-y-envia-mas-de-500-cartas-a-grandes-tenedores-para-que-acepten-la-prorroga-de-los-alquileres.html",
|
||||
"https://elpais.com/cultura/2026-04-24/critica-del-nuevo-disco-de-foo-fighters-solo-con-la-rabia-no-es-suficiente.html",
|
||||
"https://www.renfe.com/",
|
||||
"https://www.agenciatributaria.es/"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://administracion.gob.es/",
|
||||
"https://www.agenciatributaria.es/",
|
||||
"https://www.santander.es/",
|
||||
"https://www.renfe.com/",
|
||||
"https://www.amazon.es/",
|
||||
"https://www.zara.com/es/",
|
||||
"https://www.elcorteingles.es/"
|
||||
]
|
||||
}
|
||||
}
|
||||
44
data/regions/FR/Default/Paris.json
Normal file
44
data/regions/FR/Default/Paris.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"region_name": "France - Paris",
|
||||
"google_module": {
|
||||
"base_lat": 48.8566,
|
||||
"base_lon": 2.3522,
|
||||
"lang_params": "hl=fr&gl=FR",
|
||||
"valid_url_suffix": "fr"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.france24.com/fr/sports/20260424-roland-garros-carlos-alcaraz-champion-titre-participera-raison-blessure-poignet-tennis-jannick-sinner-grand-chelem",
|
||||
"https://www.cdiscount.com/",
|
||||
"https://www.sorbonne-universite.fr/",
|
||||
"https://www.france24.com/fr/vid%C3%A9o/20260424-%C3%A9tats-unis-tornade-d%C3%A9vastatrice-dans-le-nord-de-l-oklahoma",
|
||||
"https://www.amazon.fr/",
|
||||
"https://www.france24.com/fr/%C3%A9missions/aux-4-coins-de-l-europe/20260424-transports-en-europe-embarquement-imm%C3%A9diat-partie-1",
|
||||
"https://www.france24.com/fr/vid%C3%A9o/20260424-eurovision-plus-d-un-millier-d-artistes-appellent-au-boycott-du-concours",
|
||||
"https://www.france24.com/fr/vid%C3%A9o/20260424-au-pakistan-une-reprise-des-n%C3%A9gociations-entre-les-%C3%A9tats-unis-et-l-iran",
|
||||
"https://www.sncf.com/",
|
||||
"https://www.credit-agricole.fr/",
|
||||
"https://www.france24.com/fr/%C3%A9co-tech/20260424-le-manifeste-de-palantir-pour-une-silicon-valley-en-armes",
|
||||
"https://www.france24.com/fr/%C3%A9missions/outre-mer/20260424-des-%C3%A9l%C3%A8ves-priv%C3%A9s-d-%C3%A9cole-depuis-deux-semaines-%C3%A0-mayotte",
|
||||
"https://www.france24.com/fr/vid%C3%A9o/20260424-sommet-europ%C3%A9en-%C3%A0-chypre-apr%C3%A8s-l-ukraine-le-moyen-orient-au-menu-des-discussions",
|
||||
"https://www.impots.gouv.fr/",
|
||||
"https://www.france24.com/fr/vid%C3%A9o/20260424-russie-pluie-noire-sur-une-ville-apr%C3%A8s-une-frappe-de-kiev-contre-une-raffinerie",
|
||||
"https://www.france24.com/fr/moyen-orient/20260424-de-retour-dans-le-sud-des-libanais-d%C3%A9vast%C3%A9s-isra%C3%ABl-d%C3%A9truit-des-maisons-des-routes-m%C3%AAme-les-arbres",
|
||||
"https://www.france24.com/fr/vid%C3%A9o/20260424-%C3%A9tats-unis-risque-de-p%C3%A9nurie-de-missiles-tomahawk-apr%C3%A8s-leur-usage-massif-au-moyen-orient",
|
||||
"https://www.france24.com/fr/%C3%A9missions/aux-4-coins-de-l-europe/20260424-transports-en-europe-embarquement-imm%C3%A9diat-partie-2",
|
||||
"https://www.service-public.fr/",
|
||||
"https://www.france24.com/fr/vid%C3%A9o/20260424-liban-sixi%C3%A8me-casque-bleu-tu%C3%A9-depuis-le-d%C3%A9but-de-la-guerre-france-24-re%C3%A7oit-dany-ghafary-finul",
|
||||
"https://www.france24.com/fr/vid%C3%A9o/20260424-pape-l%C3%A9on-xiv-cl%C3%B4ture-de-sa-tourn%C3%A9e-africaine-de-onze-jours-en-guin%C3%A9e-%C3%A9quatoriale",
|
||||
"https://www.france24.com/fr/afrique/20260424-togo-france-affirme-soutenir-initiatives-r%C3%A9gionales-contenir-risque-terroriste-organisation-%C3%A9tat-islamique-al-qa%C3%AFda"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.service-public.fr/",
|
||||
"https://www.impots.gouv.fr/",
|
||||
"https://www.sorbonne-universite.fr/",
|
||||
"https://www.credit-agricole.fr/",
|
||||
"https://www.sncf.com/",
|
||||
"https://www.amazon.fr/",
|
||||
"https://www.cdiscount.com/"
|
||||
]
|
||||
}
|
||||
}
|
||||
44
data/regions/HK/Default/HongKong.json
Normal file
44
data/regions/HK/Default/HongKong.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"region_name": "Hong Kong",
|
||||
"google_module": {
|
||||
"base_lat": 22.2847,
|
||||
"base_lon": 114.1582,
|
||||
"lang_params": "hl=zh-HK&gl=HK",
|
||||
"valid_url_suffix": "com.hk"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://hk.news.yahoo.com/%E5%8F%88-%E6%9D%91%E8%8A%B1%E5%9C%92%E4%BF%B1%E6%A8%82%E9%83%A8%E6%B4%A99%E5%8D%83%E4%BA%BA%E8%B3%87%E6%96%99-%E7%A7%81%E9%9A%B1%E5%B0%88%E5%93%A1%E8%A3%81%E5%AE%9A%E9%81%95%E7%A7%81%E9%9A%B1%E6%A2%9D%E4%BE%8B-200000176.html",
|
||||
"https://www.hktvmall.com/",
|
||||
"https://hk.news.yahoo.com/%E8%8B%B1%E5%9C%8B%E6%94%BF%E5%BA%9C%E7%B8%AE%E6%B8%9B%E8%B5%A4%E5%AD%97%E6%9C%89%E6%88%90%E6%95%88-%E5%B0%88%E5%AE%B6%E8%AD%A6%E5%91%8A%E4%B8%AD%E6%9D%B1%E6%88%B0%E7%88%AD%E6%81%90%E6%8E%A8%E5%8D%87%E8%88%89%E5%82%B5%E6%88%90%E6%9C%AC-132005020.html",
|
||||
"https://www.gov.hk/",
|
||||
"https://www.hsbc.com.hk/",
|
||||
"https://hk.news.yahoo.com/%E8%87%AA%E5%8B%95%E6%8B%86%E5%8D%B8%E7%B5%84%E8%A3%9D%E5%A4%A7%E6%B8%9B%E4%BA%BA%E6%89%8B%E5%B7%A5%E6%99%82-%E6%B8%AF%E9%90%B5%E9%A6%96%E8%BC%AA%E5%B0%8D%E7%B6%AD%E4%BF%AE%E4%B8%AD%E5%BF%83%E5%B0%87%E9%81%8B%E4%BD%9C-200000982.html",
|
||||
"https://hk.news.yahoo.com/%E7%84%A1%E5%A4%96%E9%81%8A%E5%81%87%E5%8F%97%E5%82%B7-%E9%A8%99%E6%97%85%E9%81%8A%E4%BF%9D%E9%9A%AA%E7%B4%A2%E5%84%9F-9%E4%BA%BA%E8%A2%AB%E6%8D%95%E6%B6%8970%E6%A1%88170%E8%90%AC-200000121.html",
|
||||
"https://hk.news.yahoo.com/%E6%AD%90%E5%85%83%E5%8D%80pmi%E8%B7%8C%E7%A0%B4%E6%A6%AE%E6%9E%AF%E7%B7%9A-%E5%8F%97%E4%B8%AD%E6%9D%B1%E6%88%B0%E4%BA%8B%E6%8B%96%E7%B4%AF-113502050.html",
|
||||
"https://hk.news.yahoo.com/%E5%90%88%E8%B3%87%E5%A5%AA%E4%B8%AD%E7%92%B0%E6%B5%B7%E6%BF%B1%E6%B4%BB%E5%8B%95%E7%A9%BA%E9%96%93%E7%A7%9F%E7%B4%84-%E6%81%92%E5%9F%BA%E8%88%87%E8%80%80%E6%A6%AE-%E5%BC%95%E5%9C%8B%E9%9A%9B%E7%B4%9A%E7%9B%9B%E4%BA%8B-%E5%AE%9A%E6%99%82%E9%96%8B%E6%94%BE%E6%95%B4%E5%80%8B%E7%A9%BA%E9%96%93-200000497.html",
|
||||
"https://www.hko.gov.hk/",
|
||||
"https://www.mtr.com.hk/",
|
||||
"https://hk.news.yahoo.com/%E7%BE%8E%E8%BB%8D%E6%94%94%E6%88%AA%E6%AA%A2%E6%9F%A5%E5%8D%B0%E5%BA%A6%E6%B4%8B-%E8%89%98%E6%B2%B9%E8%BC%AA-%E6%8E%A7%E9%81%8B%E8%BC%89%E4%BC%8A%E6%9C%97%E7%9F%B3%E6%B2%B9-140505715.html",
|
||||
"https://hk.news.yahoo.com/%E7%BE%8E%E4%BC%8A%E8%AB%87%E5%88%A4%E9%80%B2%E5%B1%95%E8%86%A0%E8%91%97-%E4%BB%A5%E9%BB%8E%E5%81%9C%E7%81%AB%E5%BB%B6%E9%95%B73%E5%91%A8-060502014.html",
|
||||
"https://hk.news.yahoo.com/%E7%87%92%E5%82%B77-5-%E5%B1%85%E6%B0%91%E5%81%95%E7%88%B6%E5%9B%9E%E5%AE%B6%E5%9F%B7%E6%8B%BE-%E8%BF%91%E8%B5%B7%E7%81%AB%E9%BB%9E%E4%BD%8F%E6%88%B6%E5%98%86-%E5%AE%9A%E5%94%94%E5%A5%BD%E5%8F%97-200000405.html",
|
||||
"https://hk.news.yahoo.com/%E6%8C%AA%E5%A8%81%E6%93%AC%E7%A6%8116%E6%AD%B2%E4%BB%A5%E4%B8%8B%E5%85%92%E5%B0%91%E4%BD%BF%E7%94%A8%E7%A4%BE%E7%BE%A4%E5%AA%92%E9%AB%94-080501788.html",
|
||||
"https://www.hku.hk/",
|
||||
"https://www.police.gov.hk/",
|
||||
"https://hk.news.yahoo.com/%E5%B7%9D%E6%99%AE%E7%A8%B1%E4%B8%8D%E6%80%A5%E6%96%BC%E7%B5%90%E6%9D%9F%E8%88%87%E4%BC%8A%E6%9C%97%E6%88%B0%E7%88%AD-%E4%B8%8D%E6%9C%83%E5%8B%95%E7%94%A8%E6%A0%B8%E6%AD%A6-222004956.html",
|
||||
"https://hk.news.yahoo.com/%E4%BB%8A%E6%97%A9%E9%99%8D%E6%BA%AB-%E4%BD%8E%E8%A6%8B19%E5%BA%A6-%E4%BB%8A%E6%97%A5%E6%9C%89%E7%8B%82%E9%A2%A8%E9%9B%B7%E6%9A%B4-%E9%9B%A8%E5%8B%A2%E9%A0%97%E5%A4%A7-200000766.html",
|
||||
"https://hk.news.yahoo.com/%E7%BE%8E%E5%9C%8B%E5%8B%99%E9%99%A2%E6%87%B8%E8%B3%9E%E9%80%BE3%E5%84%84-%E7%B7%9D%E6%8D%95%E4%BC%8A%E6%8B%89%E5%85%8B%E6%AD%A6%E8%A3%9D%E7%B5%84%E7%B9%94%E9%A0%98%E8%A2%96-152002118.html",
|
||||
"https://hk.news.yahoo.com/%E7%A0%B4%E5%A4%A9%E8%8D%92-%E7%BE%85%E5%85%B5%E5%92%B8%E9%A0%90%E7%95%9910%E5%84%84%E5%9B%9E%E6%B0%B4%E6%81%92%E5%A4%A7%E8%82%A1%E6%9D%B1-%E6%9C%83%E8%B2%A1%E5%B1%80%E5%8F%A6%E7%BD%B03%E5%84%84-%E8%AD%89%E7%9B%A3-%E5%90%91%E5%AF%A9%E8%A8%88%E7%95%8C%E7%99%BC%E6%98%8E%E7%A2%BA%E8%A8%8A%E6%81%AF-200000502.html",
|
||||
"https://hk.news.yahoo.com/%E6%9C%80%E6%96%B0%E5%A4%B1%E6%A5%AD%E7%8E%873-7-%E5%8B%9D%E9%A0%90%E6%9C%9F-%E6%94%BF%E5%BA%9C-%E8%A7%80%E5%AF%9F%E5%9C%B0%E7%B7%A3%E6%94%BF%E6%B2%BB%E5%B1%80%E5%8B%A2%E5%BD%B1%E9%9F%BF-200000451.html"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.gov.hk/",
|
||||
"https://www.hko.gov.hk/",
|
||||
"https://www.police.gov.hk/",
|
||||
"https://www.hku.hk/",
|
||||
"https://www.hsbc.com.hk/",
|
||||
"https://www.mtr.com.hk/",
|
||||
"https://www.hktvmall.com/"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"region_code": "JP",
|
||||
"region_name": "日本 (东京)",
|
||||
"google_module": {
|
||||
"base_lat": 35.6895,
|
||||
"base_lon": 139.6917,
|
||||
"lang_params": "hl=ja&gl=jp&ceid=JP:ja",
|
||||
"valid_url_suffix": "google.co.jp"
|
||||
}
|
||||
}
|
||||
39
data/regions/JP/Default/Tokyo.json
Normal file
39
data/regions/JP/Default/Tokyo.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"region_name": "日本 (东京)",
|
||||
"google_module": {
|
||||
"base_lat": 35.6812,
|
||||
"base_lon": 139.7671,
|
||||
"lang_params": "hl=ja&gl=JP",
|
||||
"valid_url_suffix": "com"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://news.yahoo.co.jp/pickup/6577513?source=rss",
|
||||
"https://news.yahoo.co.jp/pickup/6577514?source=rss",
|
||||
"https://www.u-tokyo.ac.jp/",
|
||||
"https://news.yahoo.co.jp/pickup/6577515?source=rss",
|
||||
"https://www.mhlw.go.jp/",
|
||||
"https://news.yahoo.co.jp/pickup/6577495?source=rss",
|
||||
"https://www.rakuten.co.jp/",
|
||||
"https://news.yahoo.co.jp/pickup/6577518?source=rss",
|
||||
"https://www.yahoo.co.jp/",
|
||||
"https://www.jreast.co.jp/",
|
||||
"https://www.amazon.co.jp/",
|
||||
"https://www.smbc.co.jp/",
|
||||
"https://news.yahoo.co.jp/pickup/6577511?source=rss",
|
||||
"https://www.japan.go.jp/",
|
||||
"https://news.yahoo.co.jp/pickup/6577506?source=rss",
|
||||
"https://news.yahoo.co.jp/pickup/6577486?source=rss"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.japan.go.jp/",
|
||||
"https://www.mhlw.go.jp/",
|
||||
"https://www.u-tokyo.ac.jp/",
|
||||
"https://www.smbc.co.jp/",
|
||||
"https://www.jreast.co.jp/",
|
||||
"https://www.yahoo.co.jp/",
|
||||
"https://www.amazon.co.jp/",
|
||||
"https://www.rakuten.co.jp/"
|
||||
]
|
||||
}
|
||||
}
|
||||
36
data/regions/KR/Default/Seoul.json
Normal file
36
data/regions/KR/Default/Seoul.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"region_name": "South Korea - Seoul",
|
||||
"google_module": {
|
||||
"base_lat": 37.5665,
|
||||
"base_lon": 126.978,
|
||||
"lang_params": "hl=ko&gl=KR",
|
||||
"valid_url_suffix": "co.kr"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.yonhapnewstv.co.kr/news/MYH202604242115160Bi",
|
||||
"https://www.yonhapnewstv.co.kr/news/MYH20260424232327PlQ",
|
||||
"https://www.naver.com/",
|
||||
"https://www.yonhapnewstv.co.kr/news/AKR20260424211617lP3",
|
||||
"https://www.coupang.com/",
|
||||
"https://www.hometax.go.kr/",
|
||||
"https://www.yonhapnewstv.co.kr/news/MYH20260424224141clh",
|
||||
"https://www.korea.kr/",
|
||||
"https://www.daum.net/",
|
||||
"https://www.kbstar.com/",
|
||||
"https://www.yonhapnewstv.co.kr/news/MYH20260424212713ARq",
|
||||
"https://www.snu.ac.kr/",
|
||||
"https://www.kakao.com/"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.korea.kr/",
|
||||
"https://www.hometax.go.kr/",
|
||||
"https://www.snu.ac.kr/",
|
||||
"https://www.kbstar.com/",
|
||||
"https://www.naver.com/",
|
||||
"https://www.daum.net/",
|
||||
"https://www.coupang.com/",
|
||||
"https://www.kakao.com/"
|
||||
]
|
||||
}
|
||||
}
|
||||
50
data/regions/MY/Default/Kuala_Lumpur.json
Normal file
50
data/regions/MY/Default/Kuala_Lumpur.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"region_name": "Malaysia - Kuala Lumpur",
|
||||
"google_module": {
|
||||
"base_lat": 3.139,
|
||||
"base_lon": 101.6869,
|
||||
"lang_params": "hl=en-MY&gl=MY",
|
||||
"valid_url_suffix": "com.my"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://news.google.com/rss/articles/CBMivAFBVV95cUxPZTJSUTBfRXJ5alNuZUlQMlJYTE4tZUdXOW9vWC1TdFlBRC1fc3dHN19iNm5MTHBBVXdMOEZzZkpoR29CN1hibzI4cHVUNkVwWnc1SmtaaTk4YjBVVGJMUjI1X0lxbW1ILVRqTmhpS3Z6RXozZW9tNkYzVHpBbmNBQ29GZHJyOUpSXzAtNExnT0tGRjZWUHBvaDd2ZXNxbWtMMWMxRTNqWGdEdDZnejZlT0JkM1V1Qkhrd2t1UA?oc=5",
|
||||
"https://news.google.com/rss/articles/CBMiUEFVX3lxTFBIRXB0VVVsbUQyNFlKeU4zR3BZMV95M3pPa2pPY3o4eDFiWmIxdWV6TlBHbk8wZTJQVEtxT1I1TktkUFEzZ0k0b1MtQjNfT012?oc=5",
|
||||
"https://www.kwsp.gov.my/",
|
||||
"https://um.edu.my/",
|
||||
"https://news.google.com/rss/articles/CBMisAFBVV95cUxOZWY5eU9nblU5OVNFZVNNa0hBMFAzbmNjZG5Gelk2WjlYM3duelpuZVR0a0xlT2ZRM09jNjFKUzNWU1cwMlFCYnRCbkFuLUFaMkVnaU9GWW1SRmM5V0FwbDZhRWVCdk9aUzRMWEUtd3NlOWJwQnZXOHJTc3VrMHBDWWlYam5rZC1SNG5BX3hCTU4zN3F0U19URmpuNDkyckV0VGh4ZTVveDJIRnB0U0JrVw?oc=5",
|
||||
"https://news.google.com/rss/articles/CBMi8wFBVV95cUxOWDdveGRmZkI2MDFtMjc0cWJJSnJqWHM3cTJRcU9iVVkwd3VJMC1RR0h1cG9BTWVBSzQ4Q2pFb3VmTWpFc2VucVV1c2dNRGZMaWd6TnJ6UnJlRURCYW1jRHgzMzBPUW01elJrSnF5VEVIT1dmcFYxNk5pZ2ZtWHlNV0VCUV9FSnFzdHdERzFFQng4dHJ2c1BtY3NzdkpCTjFVTG0yc0N4VWY2TDdfNzh2THl4MjRNMlF0S1ljYXlRNWlGbFFIRDFRNTc5d3ZsUDd2Yzc2NXMtRS15eHE3TEFrVHRSaF94N2hUbVU2eHd5SjBmX0nSAfgBQVVfeXFMTmJmZ3hLeGh0bDM5VFA5TjNDUjVmclZRZ29ISllyRmQ4VUdTWE9IbHcyV2JzT2Q2enJLZzdLOEEtYWczY2VsRk9pdTIyaV9UZU5IZ0hIb2p0eGpHMHFhMzhmcmc0cFRIWEVTS0g5b0lHRGs5amotc0F6VEloNmhXWGFWLXluemR2amxqSkRQLWhkM3VibjNHNVZHUERUaEFyVW1idUtGRV9nb2FxYkhnVk90OGpzWE5FY2NxSXRwNGFjVm1KeWhNX0t3WkpVVzNScUlldi1xUFNLb2ppZlFxMFpNQVg3ZURUT1hubm9ibkhicmdhOGRudkQ?oc=5",
|
||||
"https://news.google.com/rss/articles/CBMitAFBVV95cUxPbFU5dUFjT2t5LXVxdFhmTlh5YVh2M01qOGNhTUpTdE9PRVg2TG56VklHV2w5T1ZndERyTW16VlFxQlhOTnE4d2p5b3QxNUdWa2hYTHNOcjViLW5jR0NxQkJFdWUyblZhV2EzUlBmcE5aSy1BOGtoR0xFM0hzdzdZX2lEN1NIV01QODJrRTk3MEJIQWg1dVlaaWcwSjZKNEJGb2dwbFY1bklDWVQ0dTV0cG9kYUk?oc=5",
|
||||
"https://www.maybank2u.com.my/",
|
||||
"https://news.google.com/rss/articles/CBMiXkFVX3lxTFAwTl8tYzE3VWx2bjRfS3NrWllQZF9FRWVyMnJPVGxvTVdJenpqd3VEUmVaTnNxTHZqTXBaZWZKMlhNbkI1MnV6dGZISm5fQmREWl8xUHY5c3czSE5SZWc?oc=5",
|
||||
"https://news.google.com/rss/articles/CBMiwAFBVV95cUxPeDNGSlFPYkdjTDBKdWR2MFVIeHV6VHJWdnUwbzdjeHlRSGFVVUhsZlNQQUZicUVaS2pKRG1DRW9TdHpvNENFV1JXLTdPZUc3WEZYQTFqYThXWmZncjZhU3R4dGdab0FRNUlHbUsyQnhBTjhuLTRjWU5ZR1dzbHlmaHpUUzhzTlhwSnJxWi1Bdjd0WEZlZXo1UkY5LXJNb3BUQjUwRnA0ZXF2bUlBdk44LTNLRlF2Wkd2NFRiX0kxQW8?oc=5",
|
||||
"https://www.malaysia.gov.my/",
|
||||
"https://news.google.com/rss/articles/CBMiuwFBVV95cUxNRVk0VVMtNWxDcUluZkV3NnFlcU5RYXVqdmUydjlyaG1KNlpmbUh3UGt2WlhsTVNLNjVMSGh1SnFGZ0ZIZEllNnUxdjNzenloZEJJcXhWWXdDQm5iU3l0azJiWkRxV3RTb29ZLVpjaFBOanB3RzZqTkMwVWJNNS1mVWNGZFYtMW5sYmZkR09sbnhhSmVYUGdtM2prSmtEWXlmX3ZtTVRIOFI0Z1AybVN2d1Vaa19NU19ZSTVn?oc=5",
|
||||
"https://www.cimbclicks.com.my/",
|
||||
"https://news.google.com/rss/articles/CBMirgFBVV95cUxOM1VobXV0X0kyQkxTNTRrMHBJYmIzZ2lZWU1jV2ZTSW1xMXV0TXZvLUUwbGVkUmZhRE1pNlo2THpaeFBlOGRaN293MW1vclhIdndLMjdwZzlfdFY3MDUtX3FQVmRQcHVZUUR1QWxrNTRNZElfVTl5Ti1sUGRwOWttMFRLVWlkOU1vUTJ1QTZqTWFvVl9Qdjh6UzNYcXBFNnRCcWtTQ09tOHl0aEp1NWc?oc=5",
|
||||
"https://www.pos.com.my/",
|
||||
"https://www.thestar.com.my/",
|
||||
"https://news.google.com/rss/articles/CBMivwFBVV95cUxPYkRPNVBFanVWRnBXb0Y0ZXRlWmtiN2dsT1Bya1RaaGZZalJQUWZJY1lud3IxcVpsZFpNdlRubmFxMlk3dUM3bi1keVJlaS1uMEotbWhBWWFZTTBlUW5hNFpQZEhjMGl0U1NWdUdsVTlVdGMzRFJPcjZXMjlHX3ZOZW5LNTIyRFI1cnEzMUhpNXhnNVFHTlpYSlRXRnFJZmJsSG1fc0FyUXJFUnlPNmtjeDVnd0VLNm52QVgtLUJYb9IBxAFBVV95cUxOV0R5WGowY0dSME1zdVZnbHdDUnBKUmJ0Nk1uZENzNm9NdngzR3k1bXRmY29SWG04MGU2aUhaUVRkeXRQZk9GemxQYzZKbTdkM0RQZjFlR1lzYnhnLUdqbW1uUE01UXVnN1JHd0RCLTRyUHFHUmtMZmoxVkpKdGZBSlFjaDlnbkJIdzZjZ1Vra0dnQzltRXFoWTdzLTZNdXNvS1VfbHFMYnN0WnJlazJsa25HLWdfOUw4Sm9PTmZveVJYZTJs?oc=5",
|
||||
"https://news.google.com/rss/articles/CBMizwFBVV95cUxPZGJadEJvZHA0MVlsTWVWcmhWN2ttOXJrZjY4SXdXMTNuUVFlWjlXY080SzlyMVh5Tl9hZE85aWhGTTVxQi0tY1JUZDBzSDRRQ3Qxc1BzekJ2Nnk4WEt3dXd2NUdQZ0U4SzdaSFE0ZExIVlpQTmpCS2RpM1JBSUNRaVBtR3phOXJCOFdsUWpsTXllVDNHcUp4Y0pSbWF6SFN0bHFHdWVsdy1FY01idE44eDNqVy1zRENjMXZKS3hwYXZYcTdwdW5hYlpJMVpwZkk?oc=5",
|
||||
"https://shopee.com.my/",
|
||||
"https://news.google.com/rss/articles/CBMiwAFBVV95cUxOTnZYOEYtQ0ZlZEtxZUlucnM1ZnJvYkVkdFVCbTZPdG1MNmlzNjcwMTR4SVBvWWdyMkhHQ3BQOFZ4Ty1XQ3dCUmhIV2RDQjJCV3BSTjZyZ2txWGo4Mmpmb3VTZVdlV0JGYk8xVTNBUUdFc21nU19oYlhETmN0NkdtcmRJR0xEV1lSb3E5ekZsYlFsazZCaGVGMjlneFp5ZXN5S0FIZ0YwUmdVcG1CWWdVYjJSLVBWNjRuVnVqUGVGS2U?oc=5",
|
||||
"https://news.google.com/rss/articles/CBMioAFBVV95cUxNeWMweUVJV3ZFYkxxbGljVW05ZHdENWxENFhRWFlNWWhiQ2VxZGo4OVFkVEV5REtRbnB2eGRpY2wwNjJadGtwdUIwamUyRkZWempoMFF2elppczhGUGRNQ2hkaEtrRHBTTVRkaV9GakQwSnhzUnVNQk5CVEhrNWFRNkd3VllJbTk5czJmUVFKMmZWNU01OUdmQXJONlB6TjZN0gGmAUFVX3lxTFB0MVo3dmRaWG90ZXJ1Z0tJSnJrd1EtZTZEVXJRUjRuMWpiR1pQNmZxTGtyWmkyd0llQzhUZ183Sk8tNFpZRW9nS2Mwb2dEUGlkTWNhc1VOWnFDSmRZUDJaNEdXSUVtQzdJVGxzTnlQUDJJNG81c3F1WjgwT1ZGOW41M29vc1RmemlhQjRPMzJWV3p0WU5qZl9qLUxnNngwaDJRX2hIcWc?oc=5",
|
||||
"https://news.google.com/rss/articles/CBMigAFBVV95cUxQdFZFRHZIN09tV1Zzd19JcUVWNFUzc0xnaFN1VkgwVUFyV3ZPWXlURlk1N21VdkxvQ0NhcERmMFBCcy10NGd2ek56Yy1WYzBZSU5aUnAtNXpHUHAwZlRmUWhTNDFUSjJZaVY5QXEyYlhDLWtyVWdCd2hBcmU3dzc0bA?oc=5",
|
||||
"https://news.google.com/rss/articles/CBMi3wFBVV95cUxQQzk2M1FMSmVTRkcxdHhsdW5jc0RCZi1pOGtqMDFjSGt4MHhPU2luYUNDSGJ0OGxzcXJwUnVaaG9sWDUtZ0FNenNfMGtBZ0xjZlNZNUtMX0w3cmtJU2tOOTVLelpQNFFDN2c3elpSZF9qNFJqQmJpUWhZdUpCNXlxMXRFZVFLal95ajhxam9MYWpwTGwwWS1EanpoWnVFd0NubGg4SWxTdlprSEpjeTRmVmd3dnpVSGZFUlgzV0tHM19waDc2LXhxNlJjOWZUOEV6OHJNSDFJaUpBdDgzS0hR0gHkAUFVX3lxTE5NOFNBVlo5b1ZuT2JwbEstVk1vbEN4NFM0ejdXbkhhSTJfLURHQXZCTDlpdFRMZlRNV1g0LWY0cEdvdjRFa1d2dmMtVWdPN3I2NDZhdExsSkpGSmM2NDZwT3NyR3NjbGdXODlDSWhvYUtGcGZLd0lZVHZ5SUVBSlJCdGxDQkNjNjltVkg3VW5PeXVyeWRWbnZ1ZDJ0cDJhaThPQ3gxZW1RTUN6ZXd3VW9fUFUxMFBZblczMFd5dy1Nd01MZ3JLNVg2S0dFTWVEdzNKUWZwNm5WcG00cnJiZUtuQXRfcg?oc=5",
|
||||
"https://www.hasil.gov.my/",
|
||||
"https://www.lazada.com.my/"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.malaysia.gov.my/",
|
||||
"https://www.hasil.gov.my/",
|
||||
"https://www.kwsp.gov.my/",
|
||||
"https://um.edu.my/",
|
||||
"https://www.maybank2u.com.my/",
|
||||
"https://www.cimbclicks.com.my/",
|
||||
"https://www.thestar.com.my/",
|
||||
"https://shopee.com.my/",
|
||||
"https://www.lazada.com.my/",
|
||||
"https://www.pos.com.my/"
|
||||
]
|
||||
}
|
||||
}
|
||||
44
data/regions/NL/Default/Amsterdam.json
Normal file
44
data/regions/NL/Default/Amsterdam.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"region_name": "Netherlands - Amsterdam",
|
||||
"google_module": {
|
||||
"base_lat": 52.3676,
|
||||
"base_lon": 4.9041,
|
||||
"lang_params": "hl=nl&gl=NL",
|
||||
"valid_url_suffix": "nl"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.ing.nl/",
|
||||
"https://nos.nl/l/2611855",
|
||||
"https://www.ns.nl/",
|
||||
"https://www.belastingdienst.nl/",
|
||||
"https://nos.nl/l/2611872",
|
||||
"https://nos.nl/l/2611850",
|
||||
"https://nos.nl/l/2611861",
|
||||
"https://nos.nl/l/2611835",
|
||||
"https://www.bol.com/",
|
||||
"https://nos.nl/l/2611825",
|
||||
"https://nos.nl/l/2611863",
|
||||
"https://www.marktplaats.nl/",
|
||||
"https://www.rijksoverheid.nl/",
|
||||
"https://nos.nl/l/2611823",
|
||||
"https://nos.nl/l/2611873",
|
||||
"https://nos.nl/l/2611871",
|
||||
"https://nos.nl/l/2611859",
|
||||
"https://nos.nl/l/2611843",
|
||||
"https://nos.nl/l/2611819",
|
||||
"https://nos.nl/l/2611827",
|
||||
"https://www.buienradar.nl/",
|
||||
"https://nos.nl/l/2611849"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.rijksoverheid.nl/",
|
||||
"https://www.belastingdienst.nl/",
|
||||
"https://www.ing.nl/",
|
||||
"https://www.ns.nl/",
|
||||
"https://www.bol.com/",
|
||||
"https://www.marktplaats.nl/",
|
||||
"https://www.buienradar.nl/"
|
||||
]
|
||||
}
|
||||
}
|
||||
46
data/regions/SG/Default/Singapore.json
Normal file
46
data/regions/SG/Default/Singapore.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"region_name": "Singapore - Singapore",
|
||||
"google_module": {
|
||||
"base_lat": 1.3521,
|
||||
"base_lon": 103.8198,
|
||||
"lang_params": "hl=en-SG&gl=SG",
|
||||
"valid_url_suffix": "com.sg"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.channelnewsasia.com/business/us-chipmakers-hit-record-highs-intel-turbocharges-ai-rally-6079431",
|
||||
"https://www.fairprice.com.sg/",
|
||||
"https://www.channelnewsasia.com/world/us-justice-department-closes-investigation-fed-chair-jerome-powell-6079516",
|
||||
"https://www.channelnewsasia.com/sport/nzs-bracewell-handed-two-year-ban-after-testing-positive-cocaine-6079351",
|
||||
"https://www.nus.edu.sg/",
|
||||
"https://www.channelnewsasia.com/world/pentagon-chief-hegseth-says-iran-has-chance-make-good-deal-6079101",
|
||||
"https://www.channelnewsasia.com/sport/alcarazs-french-open-three-peat-dreams-dashed-wrist-injury-forces-withdrawal-6079616",
|
||||
"https://www.singpass.gov.sg/",
|
||||
"https://www.iras.gov.sg/",
|
||||
"https://www.channelnewsasia.com/sport/west-bromwich-albion-deducted-two-points-back-in-relegation-mix-6079696",
|
||||
"https://www.gov.sg/",
|
||||
"https://www.shopee.sg/",
|
||||
"https://www.dbs.com.sg/",
|
||||
"https://www.channelnewsasia.com/sport/sunderlands-brobbey-deserved-red-foul-spurs-romero-premier-league-panel-says-6079656",
|
||||
"https://www.cpf.gov.sg/",
|
||||
"https://www.channelnewsasia.com/sport/kohli-leads-bengaluru-chase-in-ipl-sudharsans-ton-goes-in-vain-6079791",
|
||||
"https://www.channelnewsasia.com/business/google-invest-up-40-billion-in-ai-rival-anthropic-6079611",
|
||||
"https://www.channelnewsasia.com/sport/frances-cosmobilis-in-talks-buy-world-rally-championship-rights-sources-say-6079736",
|
||||
"https://www.channelnewsasia.com/sport/third-third-bottom-tottenhams-survival-fight-takes-them-wolves-6079546",
|
||||
"https://www.channelnewsasia.com/business/german-prosecutors-investigate-phishing-attack-targeting-politicians-6079586",
|
||||
"https://www.channelnewsasia.com/sport/arsenal-welcome-back-saka-final-push-in-premier-league-title-race-6079441",
|
||||
"https://www.channelnewsasia.com/business/us-justice-department-intervenes-in-xai-challenge-colorado-tech-law-6079606",
|
||||
"https://www.channelnewsasia.com/today/up-close/pro-female-basketball-3x3-singapore-national-athlete-sports-pressure-6077861"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.gov.sg/",
|
||||
"https://www.cpf.gov.sg/",
|
||||
"https://www.iras.gov.sg/",
|
||||
"https://www.singpass.gov.sg/",
|
||||
"https://www.nus.edu.sg/",
|
||||
"https://www.dbs.com.sg/",
|
||||
"https://www.shopee.sg/",
|
||||
"https://www.fairprice.com.sg/"
|
||||
]
|
||||
}
|
||||
}
|
||||
48
data/regions/TW/Default/Taipei.json
Normal file
48
data/regions/TW/Default/Taipei.json
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"region_name": "Taiwan - Taipei",
|
||||
"google_module": {
|
||||
"base_lat": 25.033,
|
||||
"base_lon": 121.5654,
|
||||
"lang_params": "hl=zh-TW&gl=TW",
|
||||
"valid_url_suffix": "com.tw"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.post.gov.tw/",
|
||||
"https://news.google.com/rss/articles/CBMijgNBVV95cUxPRWQ4dndjaC1VSE5lUFM5WFJYUm1jR3pDbUZsc3JUa2dXdVIyU092cDlnMnRXaDRhTlZ2X3RuRXNsczFvenpjcEtKR3k4VkFnbU1QZjZZLWhFYUU3OGdtUDNSX2wtd0ZhMkgxU2pFbG1NQjhscE8yenctMVRUdllXMXIwM0xuLVZIQnpaYWdvX2dKbFlnYW42N3NZZDRnel9vWmdQb3VwMTNjRDFCYWRNclVaamhZbFJDVHVZcTFSWTM2UVlBeFgzMHpZN1lQdmZ2MURQazNSWUZFVzhaNlhkRElFQmdEWlNyd2dxc2hjTndSY1RIVG54RS1MWHF3MlpBN21IOWMwX0ZQdXhRRU1xLV9fcGNUV2VQRWt5eWJzOU9iWVUtM2ZVaU8tY1BqaXU3emtYZWFlMGlzWHlVeUhlYXh0YXJxU01wbThMLUp6cE1LY014LVAtS3JJTGk0QUR4RlNFYll5WVk0NDNSbnMwZmJRZmpZZ1pVNnZ2WlhEUTBsT241c1M3bTl6Y01IQQ?oc=5",
|
||||
"https://news.google.com/rss/articles/CBMiWEFVX3lxTE9ha2FzeEUzQUNTWG5XZm4yakpBb09Qc05seDhXcWFsNXk4ZlA2bUEzaFFXQU40RVpiRHAzYkxOMHFQSTF5a2g4QldWVExDUzV3UEpKMGtTOTM?oc=5",
|
||||
"https://news.google.com/rss/articles/CBMi6AJBVV95cUxQZnJUa0xNUEFFWE9ZdFRSZ05lSFN1b3ByczRqaWM4enhLS1hfS2NmbHcxbjJYWnYyMUU1bVdZY0xkSUpkNTE4dVFCN1hLU3dHZ2k4S1ltX2pUWmRxTGtUZThfR1RtMkhWSFh5b3V0S2Y2SDNOaWVWajBvMXJsa0pkM0pyWENVZmpFalpFTWNVV2tMeUh2eVcyUldHS0pjM2pMcVB1Y3JfMWVfaDNGUjlxSUMzVzhzclJaSlNKS0JkYVUzNmhCbmZOTlA0ZFF4N3ZXYmlkZXJ3amtnWndCa3hoT3hrbzRzUkY0V0RKcmt5MVRmZVFrX2VjTE44a1hHNWxWeGo2OWJNRHE2V2NGNlJsMWZ5WS1VYU1LM0hNQ3o5WXlQWFNxdW1LN2hWeTdKbGl3Wk5uNG9WRE1NVGNCOFRqV2x2UFpacl9DWmZQbExKTGhEcWpsWDRFNE5jUFZGdE9JY2lJWmFBVnI?oc=5",
|
||||
"https://www.momoshop.com.tw/",
|
||||
"https://news.google.com/rss/articles/CBMiUEFVX3lxTE1RWVU3RDhPd1pobEs3aDE3QjRkd25pTWdReU83OENZU280aDZvOE1sMkI2Y19ZOGt5eHRTTl92ZGVjX2xtc01tRVc3VDBXRHJx?oc=5",
|
||||
"https://news.google.com/rss/articles/CBMiYEFVX3lxTFBKRmJSck9sZ3ZuLWp6N2kxa0FEd0lmQjNKQkktM2VhejNYQWRscFpGdTRPOU90VTFubThfUVVZdjhZRTRmbTBSMThra3JlalBuUUhSQTFENS1BQnlmUTJZN9IBZkFVX3lxTE0xMUdHNnduQXdqV21yTkZETXdTQjk5MlhhbGxMeEd5Y0daVWdnemI5TDRmWFZUS2I0QW5RcXpxWGVfeC11X3R3NmZCVkljZUUxM2ZtRUtpR1h4ci1rdEUzMjNzRkhxQQ?oc=5",
|
||||
"https://news.google.com/rss/articles/CBMiW0FVX3lxTE1Tc3ptaDJCY0lEQWNrYjNFMnVaS1dBN1FCSTFGR3p4ekItbDUyWEZiLXB1TkpWTkd1WUFMYWNuMEVwZWFKMGhvQ0dyWnVCZmdlUTZwU2NOUW5pYzTSAWBBVV95cUxOWTFIYmpocUQwX012NkRsY0JRemNxbV95NWNZNG9halFYLXdPLS1EbEF3aU42NW5MeUpOZFVjRGs2azRyZ2hUQTlGTHMwREdOYWYySEtGUUJiRjZqYktLcGU?oc=5",
|
||||
"https://tw.yahoo.com/",
|
||||
"https://www.dcard.tw/",
|
||||
"https://news.google.com/rss/articles/CBMiT0FVX3lxTFBpc29SNm5FZTdDampFWVdIakxYc0k3a0ZfUGJMUWlzRWFXa2IxQXlicUZ4eGR1al9tZk1HaFhKOTJydUdlQ0ZmRHFkZ19DSDQ?oc=5",
|
||||
"https://www.thsrc.com.tw/",
|
||||
"https://www.ntu.edu.tw/",
|
||||
"https://www.cathaybk.com.tw/",
|
||||
"https://news.google.com/rss/articles/CBMiX0FVX3lxTE9hYThuYy1SeFVtVmtlRWloazhGcXQ2RThNWFZxVjdpSVFuek9UcUVoMXdpdWI4NWgzOXVaWmRSR2J5X1JCV1hVZXFrOW11V1JXcmlPREMyb1V3MC1sRHBj?oc=5",
|
||||
"https://news.google.com/rss/articles/CBMiUEFVX3lxTE5zLVNDaGlzQnhOM0JoOG9NSWJLSVhMc0xUWjBYUkpyR3hFMXc3bU4zZGdtSlU2Q3ViTlJkVlQ5aVJQNWhvRjlSQVhFeXNxY0tt0gFWQVVfeXFMTUVvWDNUZmpCM1RhdkdFWV83WmpQMjkxUTV6MWJncGJPWS1PSHVyVFVSLTVsZkFna2MxemVnMkJVbHNqZWVMcmtTeXdMQWtJcXUtemJpUkE?oc=5",
|
||||
"https://news.google.com/rss/articles/CBMiUEFVX3lxTE5xcDdhYjQwcVBQSDIxREN1SnlqY2Z6dE4yaGcySjFlc2dBeWNNRGFfTjFzNndYXy1nRHM0X3BSRTVNek5UVWxsVGR0RzZtQkcy0gFWQVVfeXFMTXVBRWxEUzZQQ2xvYlJVd25jTk5kRGRvb2xPRmdwckw2UGtXZXA2a2FvUTVMc2YtZmJhRFNzMUtLYTNqZXlFM1V0YmlrY1JIUkxZU01QREE?oc=5",
|
||||
"https://news.google.com/rss/articles/CBMi6AJBVV95cUxONXJKMzQxOGhGQ0RDZVpkNkxodkFTOUZPSnBUOGVBRWpwR2JicmptR1JhWGh6XzRsREVNblJfLVRoemZEblVUeWtWWmFfS19wRzE5RFRybGw5bVJ3RE9RbllQMjdHVGYzM3RkMmhLX240RjFWM09lRlN6d3dET2N0REozdDdaUDc0N3NMeXJBNFNtdzZaTzViRnZBOVFGMHRDd2pWT0ttQTVaRmVIR3pSU05vNHMyMEw0blpka3ZkeWdkSjNRSDZ2Z2o5akFUYmJ5a2twdWRKb0tvem83ZFFOZTNNTFNjb0VmZHV3OUltam0xNmdlSWs0UzlDcUtybk4xLUFMU2FZZTNLWHNTYkRsWUR3RUdmSzRheGt1aG52dDZFU0JrNWF3ZWVpeHhGcVNFaW04TWFyeVFnNUpqT0RqalQ4RGx0YXZ3QlBVV1NDM0FqVjlyblg2NUNRV0U2WFVsTDlkNXZkQXo?oc=5",
|
||||
"https://news.google.com/rss/articles/CBMiWkFVX3lxTE9wck9XVUlqa3FCY1I1VmNMeURQc3dRQWpIamhXdlpfUjRNeGtkQmV4VU8xa3JKRzRreDNQRU9IQWtIU05Xa1k3M19WRktrVGJqMUt1Yk9fNXY4UdIBX0FVX3lxTFA0SXZIRTZvS3JwQW96eU0teVk2VGVvd3A2S0dZZFFTWUNhbWZCbGxBdl9Sa1ZxUG1FRkNIajFJSkR0QmNpd0w4MS1KTHBXQ2FuSklnSlFld3dWbWdybVZn?oc=5",
|
||||
"https://news.google.com/rss/articles/CBMiTkFVX3lxTE8wMkF1X2l6QUxUMDU0TTJ5UGliYy1OSDBQdnNXLWdkb1h3WndWSk5McDhvcFFtbGpIWnBRY0xJNjFmX0Z5SGZmVWxvX0ROZ9IBakFVX3lxTE9iMFFLNnNtTGl5T3FNMURvd3pGU2RTcTg5SENmMXFKUWdScXVkaUNVTXdTd1JVbWFkX0pRbzhJWFV1eUxaU0RhdFpFSFUxOWRUVDFuRWFfM2hHTEJ6VFNIdEpBVDlOcXQ5OWc?oc=5",
|
||||
"https://news.google.com/rss/articles/CBMixwJBVV95cUxPeWtRdFpPd3FMT3BSazFyOE1IRy1ERnFPTlV0Q3dfUVJISkRSaUVfdlRCY3FOemEyTkxaaDQ4V09iMF8ySDRNcU91c3BPUzhCNmFRSkZDbi1VZWlJbEpRVnNCd1k3ZFpDZXc2U2tWMHRMUE54eGh1S1MtWXdIM25LUGVpbm5xMVZQRmp2ZHBqOV9fY24xOEsyUExEQVZ0T3VQYzBha20tZm1PdjU3R2hWOUdKWTBBU3VCNWkwVGRhMHZMYnpjSnFSVFNoRFV3ekVMbFczeFJNOVVfLV93aWtOeWZyUG5mUTloRThzYTA2TEN3UElrM1R3SnFyVEFrbjVVYmdWSWx2Rklyb0pBMDhia3pIa2ZtaFRVamxqTjRDeHkteFdOSnVUOXpJLVpxd2xrRVY4RXdIdi13RXhpWGZ1Q1Q1T3M2UWM?oc=5",
|
||||
"https://www.gov.tw/",
|
||||
"https://news.google.com/rss/articles/CBMilARBVV95cUxNdnBaYklQVkwwMXlyOWF2WDJWNHhiMGkzNXlCSW1Malo1bS1BRDJKaVBBSGRpYS1wUVRKandHVXB6TlpUNHpSbHJ3TWpfSGZCY2RjY3pUXzdJYkNCVkNYbnNTbzU1bTZLRG1GOVd3eWhxZnllRFdmbHc3UGJ5VWtMQ2FZVjZtSVgxczZORlJDN3lZWTh5SktvZUNzTC1tYzdzUDJ1MFpPa2Y4VmJ5LUljelRjMlplVTZHcFJUVUFoWi04UzhLbHhOSThvNGowYkNpc096V1FDNG9CQW1lelFheGg3YXRMRWxnQUVXVGlmZEs4SUV3RFVzbTRmT3V5RmV4akFjekZHb3ZaRG44SWlrQ3ozLXByNmNuY2p1ZGhqVndzSmx3c2lvOFRzc0c2UDhQMVpkbHdtT3ExZTNlRXFjQnJRcV9jLUtKY0R1c29iU2k0UG4zOEx5di1wSENUN3FHX0twUnVXNDNWS1F5MWZabTR2TVlBQnJ6eUNDcUdlR0VqV1F4R2ZqWnlCQnExVGp1M1BNX0F3TDJOTlk5T3lJTVJEendvMjNEb3lucUZwSkRVckQ4NmhrTV9GcHhhSHB5My1NZTlqYk9aaTgxYWtXcXc4WXlRRENIVTVaT0x4Z2UxOFZSVFo5RnNzNTVHR0wzSHJmVHl4SlcyOGtHM0pVZFd4YzFjaHNyRWQ0eWdNTGpVU0x6?oc=5",
|
||||
"https://www.pchome.com.tw/"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.gov.tw/",
|
||||
"https://www.post.gov.tw/",
|
||||
"https://www.ntu.edu.tw/",
|
||||
"https://www.cathaybk.com.tw/",
|
||||
"https://www.thsrc.com.tw/",
|
||||
"https://tw.yahoo.com/",
|
||||
"https://www.momoshop.com.tw/",
|
||||
"https://www.pchome.com.tw/",
|
||||
"https://www.dcard.tw/"
|
||||
]
|
||||
}
|
||||
}
|
||||
46
data/regions/UK/Default/Coventry.json
Normal file
46
data/regions/UK/Default/Coventry.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"region_name": "United Kingdom - Coventry",
|
||||
"google_module": {
|
||||
"base_lat": 52.4068,
|
||||
"base_lon": -1.5197,
|
||||
"lang_params": "hl=en&gl=GB",
|
||||
"valid_url_suffix": "co.uk"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.barclays.co.uk/",
|
||||
"https://www.bbc.com/news/articles/c77m242z2j8o?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.bbc.co.uk/sounds/play/p0ng84b4?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.bbc.com/news/articles/cz78x703lrvo?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.nationalrail.co.uk/",
|
||||
"https://www.amazon.co.uk/",
|
||||
"https://www.bbc.com/news/articles/cvg30jkvklno?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.bbc.com/news/videos/c75kpx2w452o?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.bbc.com/news/articles/cdrmvx8m8vgo?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.ebay.co.uk/",
|
||||
"https://www.bbc.com/sport/football/articles/c0rxl475x8jo?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.bbc.com/news/articles/ckgw72lrvn3o?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.bbc.com/news/articles/cz67qjn48eqo?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.bbc.com/news/articles/cn08j2g9ze9o?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.nhs.uk/",
|
||||
"https://www.postoffice.co.uk/",
|
||||
"https://www.bbc.com/news/articles/c74v2enw8l7o?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.gov.uk/",
|
||||
"https://www.bbc.co.uk/iplayer/episode/b01p6y1c/puss-in-boots?at_mid=ovYA47yS9y&at_campaign=Film_Puss_in_Boots&at_medium=display_ad&at_campaign_type=owned&at_nation=NET&at_audience_id=SS&at_product=iplayer&at_brand=b01p6y1c&at_ptr_name=bbc&at_ptr_type=media&at_format=image&at_objective=consumption&at_link_title=Film_Puss_in_Boots&at_bbc_team=BBC&at_creation=Film",
|
||||
"https://www.bbc.com/news/articles/cvg09n7gj3lo?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.bbc.com/news/articles/cj37d4gzz4xo?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.bbc.com/news/articles/cq8wnzly5j5o?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.ox.ac.uk/"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.gov.uk/",
|
||||
"https://www.nhs.uk/",
|
||||
"https://www.ox.ac.uk/",
|
||||
"https://www.barclays.co.uk/",
|
||||
"https://www.postoffice.co.uk/",
|
||||
"https://www.nationalrail.co.uk/",
|
||||
"https://www.amazon.co.uk/",
|
||||
"https://www.ebay.co.uk/"
|
||||
]
|
||||
}
|
||||
}
|
||||
46
data/regions/UK/Default/London.json
Normal file
46
data/regions/UK/Default/London.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"region_name": "United Kingdom - London",
|
||||
"google_module": {
|
||||
"base_lat": 51.5074,
|
||||
"base_lon": -0.1278,
|
||||
"lang_params": "hl=en&gl=GB",
|
||||
"valid_url_suffix": "co.uk"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.postoffice.co.uk/",
|
||||
"https://www.bbc.com/news/articles/c77m242z2j8o?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.bbc.com/news/articles/cn08j2g9ze9o?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.bbc.com/news/videos/c75kpx2w452o?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.ox.ac.uk/",
|
||||
"https://www.bbc.com/news/articles/cvg30jkvklno?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.ebay.co.uk/",
|
||||
"https://www.bbc.com/news/articles/cz78x703lrvo?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.bbc.com/news/articles/ckgw72lrvn3o?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.bbc.co.uk/iplayer/episode/b01p6y1c/puss-in-boots?at_mid=ovYA47yS9y&at_campaign=Film_Puss_in_Boots&at_medium=display_ad&at_campaign_type=owned&at_nation=NET&at_audience_id=SS&at_product=iplayer&at_brand=b01p6y1c&at_ptr_name=bbc&at_ptr_type=media&at_format=image&at_objective=consumption&at_link_title=Film_Puss_in_Boots&at_bbc_team=BBC&at_creation=Film",
|
||||
"https://www.bbc.com/news/articles/cj37d4gzz4xo?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.bbc.com/sport/football/articles/c0rxl475x8jo?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.bbc.com/news/articles/cvg09n7gj3lo?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.bbc.com/news/articles/cdrmvx8m8vgo?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.amazon.co.uk/",
|
||||
"https://www.gov.uk/",
|
||||
"https://www.nhs.uk/",
|
||||
"https://www.bbc.com/news/articles/cq8wnzly5j5o?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.nationalrail.co.uk/",
|
||||
"https://www.barclays.co.uk/",
|
||||
"https://www.bbc.co.uk/sounds/play/p0ng84b4?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.bbc.com/news/articles/cz67qjn48eqo?at_medium=RSS&at_campaign=rss",
|
||||
"https://www.bbc.com/news/articles/c74v2enw8l7o?at_medium=RSS&at_campaign=rss"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.gov.uk/",
|
||||
"https://www.nhs.uk/",
|
||||
"https://www.ox.ac.uk/",
|
||||
"https://www.barclays.co.uk/",
|
||||
"https://www.postoffice.co.uk/",
|
||||
"https://www.nationalrail.co.uk/",
|
||||
"https://www.amazon.co.uk/",
|
||||
"https://www.ebay.co.uk/"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"region_code": "US",
|
||||
"region_name": "美国 (美西洛杉矶)",
|
||||
"google_module": {
|
||||
"base_lat": 34.0522,
|
||||
"base_lon": -118.2437,
|
||||
"lang_params": "hl=en&gl=us&ceid=US:en",
|
||||
"valid_url_suffix": "google.com"
|
||||
}
|
||||
}
|
||||
50
data/regions/US/CA/Los_Angeles.json
Normal file
50
data/regions/US/CA/Los_Angeles.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"region_name": "United States - Los Angeles",
|
||||
"google_module": {
|
||||
"base_lat": 34.0522,
|
||||
"base_lon": -118.2437,
|
||||
"lang_params": "hl=en&gl=US",
|
||||
"valid_url_suffix": "com"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.cnn.com/2023/04/18/politics/white-house-toddler/index.html",
|
||||
"https://www.cdc.gov/",
|
||||
"https://www.cnn.com/style/article/ai-photo-win-sony-scli-intl/index.html",
|
||||
"https://www.cnn.com/videos/politics/2023/04/18/kevin-mccarthy-wall-street-speech-debt-ceiling-biden-economy-vpx.cnn",
|
||||
"https://www.irs.gov/",
|
||||
"https://www.cnn.com/cnn-underscored/reviews/mmmat-silicone-mats?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.cnn.com/2023/04/17/opinions/jim-jordan-clarence-thomas-judiciary-committee-obeidallah/index.html",
|
||||
"https://www.cnn.com/cnn-underscored/money/high-yield-savings-accounts?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.usa.gov/",
|
||||
"https://www.cnn.com/videos/travel/2023/04/18/century-old-shipwrecks-lake-superior-discovery-contd-orig-zt.cnn",
|
||||
"https://www.cnn.com/2023/04/18/politics/mccarthy-biden-debt-ceiling/index.html",
|
||||
"https://www.chase.com/",
|
||||
"https://www.harvard.edu/",
|
||||
"https://www.cnn.com/cnn-underscored/money/how-to-file-taxes?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.npr.org/2026/04/24/nx-s1-5794052/nfl-draft-pittsburgh-fans",
|
||||
"https://www.walmart.com/",
|
||||
"https://www.target.com/",
|
||||
"https://www.cnn.com/2023/04/18/us/benadryl-tiktok-challenge-teen-death-wellness/index.html",
|
||||
"https://www.bankofamerica.com/",
|
||||
"https://www.usps.com/",
|
||||
"https://www.npr.org/2026/04/24/nx-s1-5797964/after-2-failed-votes-mike-johnson-unveils-new-plan-to-extend-key-u-s-spy-powers",
|
||||
"https://www.cnn.com/2023/04/18/entertainment/jake-gyllenhaal-jamie-lee-curtis-pandemic-living/index.html",
|
||||
"https://www.cnn.com/2023/04/18/opinions/medication-abortion-mifepristone-miscarriage-jones-ctpr/index.html",
|
||||
"https://www.amazon.com/",
|
||||
"https://www.cnn.com/2023/04/16/opinions/top-secrets-come-spilling-out-opinion-column-galant/index.html"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.usa.gov/",
|
||||
"https://www.irs.gov/",
|
||||
"https://www.cdc.gov/",
|
||||
"https://www.harvard.edu/",
|
||||
"https://www.chase.com/",
|
||||
"https://www.bankofamerica.com/",
|
||||
"https://www.amazon.com/",
|
||||
"https://www.target.com/",
|
||||
"https://www.walmart.com/",
|
||||
"https://www.usps.com/"
|
||||
]
|
||||
}
|
||||
}
|
||||
50
data/regions/US/CA/San_Jose.json
Normal file
50
data/regions/US/CA/San_Jose.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"region_name": "United States - San Jose",
|
||||
"google_module": {
|
||||
"base_lat": 37.3382,
|
||||
"base_lon": -121.8863,
|
||||
"lang_params": "hl=en&gl=US",
|
||||
"valid_url_suffix": "com"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.cnn.com/2023/04/18/us/benadryl-tiktok-challenge-teen-death-wellness/index.html",
|
||||
"https://www.cnn.com/cnn-underscored/reviews/mmmat-silicone-mats?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.bankofamerica.com/",
|
||||
"https://www.cnn.com/2023/04/18/entertainment/jake-gyllenhaal-jamie-lee-curtis-pandemic-living/index.html",
|
||||
"https://www.cnn.com/videos/politics/2023/04/18/kevin-mccarthy-wall-street-speech-debt-ceiling-biden-economy-vpx.cnn",
|
||||
"https://www.walmart.com/",
|
||||
"https://www.cnn.com/2023/04/16/opinions/top-secrets-come-spilling-out-opinion-column-galant/index.html",
|
||||
"https://www.chase.com/",
|
||||
"https://www.cnn.com/style/article/ai-photo-win-sony-scli-intl/index.html",
|
||||
"https://www.usps.com/",
|
||||
"https://www.cnn.com/2023/04/18/politics/mccarthy-biden-debt-ceiling/index.html",
|
||||
"https://www.irs.gov/",
|
||||
"https://www.amazon.com/",
|
||||
"https://www.usa.gov/",
|
||||
"https://www.harvard.edu/",
|
||||
"https://www.cnn.com/cnn-underscored/money/high-yield-savings-accounts?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.cnn.com/2023/04/17/opinions/jim-jordan-clarence-thomas-judiciary-committee-obeidallah/index.html",
|
||||
"https://www.cnn.com/cnn-underscored/money/how-to-file-taxes?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.cnn.com/videos/travel/2023/04/18/century-old-shipwrecks-lake-superior-discovery-contd-orig-zt.cnn",
|
||||
"https://www.cnn.com/2023/04/18/opinions/medication-abortion-mifepristone-miscarriage-jones-ctpr/index.html",
|
||||
"https://www.npr.org/2026/04/24/nx-s1-5797964/after-2-failed-votes-mike-johnson-unveils-new-plan-to-extend-key-u-s-spy-powers",
|
||||
"https://www.cnn.com/2023/04/18/politics/white-house-toddler/index.html",
|
||||
"https://www.target.com/",
|
||||
"https://www.cdc.gov/",
|
||||
"https://www.npr.org/2026/04/24/nx-s1-5794052/nfl-draft-pittsburgh-fans"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.usa.gov/",
|
||||
"https://www.irs.gov/",
|
||||
"https://www.cdc.gov/",
|
||||
"https://www.harvard.edu/",
|
||||
"https://www.chase.com/",
|
||||
"https://www.bankofamerica.com/",
|
||||
"https://www.amazon.com/",
|
||||
"https://www.target.com/",
|
||||
"https://www.walmart.com/",
|
||||
"https://www.usps.com/"
|
||||
]
|
||||
}
|
||||
}
|
||||
50
data/regions/US/IL/Warrenville.json
Normal file
50
data/regions/US/IL/Warrenville.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"region_name": "United States - Warrenville",
|
||||
"google_module": {
|
||||
"base_lat": 41.8164,
|
||||
"base_lon": -88.1748,
|
||||
"lang_params": "hl=en&gl=US",
|
||||
"valid_url_suffix": "com"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.npr.org/2026/04/24/nx-s1-5797964/after-2-failed-votes-mike-johnson-unveils-new-plan-to-extend-key-u-s-spy-powers",
|
||||
"https://www.harvard.edu/",
|
||||
"https://www.npr.org/2026/04/24/nx-s1-5794052/nfl-draft-pittsburgh-fans",
|
||||
"https://www.cnn.com/2023/04/18/politics/white-house-toddler/index.html",
|
||||
"https://www.cnn.com/cnn-underscored/money/high-yield-savings-accounts?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.cnn.com/2023/04/18/entertainment/jake-gyllenhaal-jamie-lee-curtis-pandemic-living/index.html",
|
||||
"https://www.chase.com/",
|
||||
"https://www.cnn.com/2023/04/18/politics/mccarthy-biden-debt-ceiling/index.html",
|
||||
"https://www.bankofamerica.com/",
|
||||
"https://www.cnn.com/videos/travel/2023/04/18/century-old-shipwrecks-lake-superior-discovery-contd-orig-zt.cnn",
|
||||
"https://www.cnn.com/cnn-underscored/reviews/mmmat-silicone-mats?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.cnn.com/2023/04/17/opinions/jim-jordan-clarence-thomas-judiciary-committee-obeidallah/index.html",
|
||||
"https://www.cnn.com/2023/04/16/opinions/top-secrets-come-spilling-out-opinion-column-galant/index.html",
|
||||
"https://www.cnn.com/videos/politics/2023/04/18/kevin-mccarthy-wall-street-speech-debt-ceiling-biden-economy-vpx.cnn",
|
||||
"https://www.cnn.com/2023/04/18/opinions/medication-abortion-mifepristone-miscarriage-jones-ctpr/index.html",
|
||||
"https://www.walmart.com/",
|
||||
"https://www.cdc.gov/",
|
||||
"https://www.usa.gov/",
|
||||
"https://www.irs.gov/",
|
||||
"https://www.target.com/",
|
||||
"https://www.cnn.com/cnn-underscored/money/how-to-file-taxes?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.usps.com/",
|
||||
"https://www.cnn.com/style/article/ai-photo-win-sony-scli-intl/index.html",
|
||||
"https://www.amazon.com/",
|
||||
"https://www.cnn.com/2023/04/18/us/benadryl-tiktok-challenge-teen-death-wellness/index.html"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.usa.gov/",
|
||||
"https://www.irs.gov/",
|
||||
"https://www.cdc.gov/",
|
||||
"https://www.harvard.edu/",
|
||||
"https://www.chase.com/",
|
||||
"https://www.bankofamerica.com/",
|
||||
"https://www.amazon.com/",
|
||||
"https://www.target.com/",
|
||||
"https://www.walmart.com/",
|
||||
"https://www.usps.com/"
|
||||
]
|
||||
}
|
||||
}
|
||||
50
data/regions/US/NC/Charlotte.json
Normal file
50
data/regions/US/NC/Charlotte.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"region_name": "United States - Charlotte",
|
||||
"google_module": {
|
||||
"base_lat": 35.2271,
|
||||
"base_lon": -80.8431,
|
||||
"lang_params": "hl=en&gl=US",
|
||||
"valid_url_suffix": "com"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.usps.com/",
|
||||
"https://www.target.com/",
|
||||
"https://www.amazon.com/",
|
||||
"https://www.bankofamerica.com/",
|
||||
"https://www.cnn.com/2023/04/16/opinions/top-secrets-come-spilling-out-opinion-column-galant/index.html",
|
||||
"https://www.cnn.com/videos/travel/2023/04/18/century-old-shipwrecks-lake-superior-discovery-contd-orig-zt.cnn",
|
||||
"https://www.cnn.com/2023/04/18/politics/white-house-toddler/index.html",
|
||||
"https://www.cnn.com/cnn-underscored/reviews/mmmat-silicone-mats?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.cnn.com/videos/politics/2023/04/18/kevin-mccarthy-wall-street-speech-debt-ceiling-biden-economy-vpx.cnn",
|
||||
"https://www.npr.org/2026/04/24/nx-s1-5797964/after-2-failed-votes-mike-johnson-unveils-new-plan-to-extend-key-u-s-spy-powers",
|
||||
"https://www.cnn.com/2023/04/18/entertainment/jake-gyllenhaal-jamie-lee-curtis-pandemic-living/index.html",
|
||||
"https://www.harvard.edu/",
|
||||
"https://www.chase.com/",
|
||||
"https://www.cnn.com/cnn-underscored/money/high-yield-savings-accounts?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.npr.org/2026/04/24/nx-s1-5794052/nfl-draft-pittsburgh-fans",
|
||||
"https://www.usa.gov/",
|
||||
"https://www.walmart.com/",
|
||||
"https://www.cnn.com/2023/04/18/opinions/medication-abortion-mifepristone-miscarriage-jones-ctpr/index.html",
|
||||
"https://www.cnn.com/2023/04/18/us/benadryl-tiktok-challenge-teen-death-wellness/index.html",
|
||||
"https://www.cnn.com/2023/04/17/opinions/jim-jordan-clarence-thomas-judiciary-committee-obeidallah/index.html",
|
||||
"https://www.cdc.gov/",
|
||||
"https://www.irs.gov/",
|
||||
"https://www.cnn.com/style/article/ai-photo-win-sony-scli-intl/index.html",
|
||||
"https://www.cnn.com/2023/04/18/politics/mccarthy-biden-debt-ceiling/index.html",
|
||||
"https://www.cnn.com/cnn-underscored/money/how-to-file-taxes?iid=CNNUnderscoredHPcontainer"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.usa.gov/",
|
||||
"https://www.irs.gov/",
|
||||
"https://www.cdc.gov/",
|
||||
"https://www.harvard.edu/",
|
||||
"https://www.chase.com/",
|
||||
"https://www.bankofamerica.com/",
|
||||
"https://www.amazon.com/",
|
||||
"https://www.target.com/",
|
||||
"https://www.walmart.com/",
|
||||
"https://www.usps.com/"
|
||||
]
|
||||
}
|
||||
}
|
||||
50
data/regions/US/NV/Las_Vegas.json
Normal file
50
data/regions/US/NV/Las_Vegas.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"region_name": "United States - Las Vegas",
|
||||
"google_module": {
|
||||
"base_lat": 36.1699,
|
||||
"base_lon": -115.1398,
|
||||
"lang_params": "hl=en&gl=US",
|
||||
"valid_url_suffix": "com"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.cnn.com/cnn-underscored/reviews/mmmat-silicone-mats?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.irs.gov/",
|
||||
"https://www.harvard.edu/",
|
||||
"https://www.npr.org/2026/04/24/nx-s1-5794052/nfl-draft-pittsburgh-fans",
|
||||
"https://www.cnn.com/style/article/ai-photo-win-sony-scli-intl/index.html",
|
||||
"https://www.cnn.com/videos/politics/2023/04/18/kevin-mccarthy-wall-street-speech-debt-ceiling-biden-economy-vpx.cnn",
|
||||
"https://www.cnn.com/2023/04/18/us/benadryl-tiktok-challenge-teen-death-wellness/index.html",
|
||||
"https://www.cdc.gov/",
|
||||
"https://www.target.com/",
|
||||
"https://www.cnn.com/2023/04/18/opinions/medication-abortion-mifepristone-miscarriage-jones-ctpr/index.html",
|
||||
"https://www.walmart.com/",
|
||||
"https://www.amazon.com/",
|
||||
"https://www.cnn.com/cnn-underscored/money/high-yield-savings-accounts?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.cnn.com/2023/04/18/politics/white-house-toddler/index.html",
|
||||
"https://www.cnn.com/2023/04/18/entertainment/jake-gyllenhaal-jamie-lee-curtis-pandemic-living/index.html",
|
||||
"https://www.cnn.com/2023/04/17/opinions/jim-jordan-clarence-thomas-judiciary-committee-obeidallah/index.html",
|
||||
"https://www.cnn.com/2023/04/18/politics/mccarthy-biden-debt-ceiling/index.html",
|
||||
"https://www.cnn.com/videos/travel/2023/04/18/century-old-shipwrecks-lake-superior-discovery-contd-orig-zt.cnn",
|
||||
"https://www.chase.com/",
|
||||
"https://www.cnn.com/cnn-underscored/money/how-to-file-taxes?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.usa.gov/",
|
||||
"https://www.usps.com/",
|
||||
"https://www.npr.org/2026/04/24/nx-s1-5797964/after-2-failed-votes-mike-johnson-unveils-new-plan-to-extend-key-u-s-spy-powers",
|
||||
"https://www.bankofamerica.com/",
|
||||
"https://www.cnn.com/2023/04/16/opinions/top-secrets-come-spilling-out-opinion-column-galant/index.html"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.usa.gov/",
|
||||
"https://www.irs.gov/",
|
||||
"https://www.cdc.gov/",
|
||||
"https://www.harvard.edu/",
|
||||
"https://www.chase.com/",
|
||||
"https://www.bankofamerica.com/",
|
||||
"https://www.amazon.com/",
|
||||
"https://www.target.com/",
|
||||
"https://www.walmart.com/",
|
||||
"https://www.usps.com/"
|
||||
]
|
||||
}
|
||||
}
|
||||
50
data/regions/US/OR/Bend.json
Normal file
50
data/regions/US/OR/Bend.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"region_name": "United States - Bend",
|
||||
"google_module": {
|
||||
"base_lat": 44.0582,
|
||||
"base_lon": -121.3153,
|
||||
"lang_params": "hl=en&gl=US",
|
||||
"valid_url_suffix": "com"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.bankofamerica.com/",
|
||||
"https://www.chase.com/",
|
||||
"https://www.npr.org/2026/04/24/nx-s1-5794052/nfl-draft-pittsburgh-fans",
|
||||
"https://www.cnn.com/videos/politics/2023/04/18/kevin-mccarthy-wall-street-speech-debt-ceiling-biden-economy-vpx.cnn",
|
||||
"https://www.cnn.com/2023/04/18/politics/white-house-toddler/index.html",
|
||||
"https://www.cnn.com/2023/04/18/us/benadryl-tiktok-challenge-teen-death-wellness/index.html",
|
||||
"https://www.cnn.com/cnn-underscored/money/how-to-file-taxes?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.irs.gov/",
|
||||
"https://www.amazon.com/",
|
||||
"https://www.npr.org/2026/04/24/nx-s1-5797964/after-2-failed-votes-mike-johnson-unveils-new-plan-to-extend-key-u-s-spy-powers",
|
||||
"https://www.cnn.com/style/article/ai-photo-win-sony-scli-intl/index.html",
|
||||
"https://www.cnn.com/cnn-underscored/money/high-yield-savings-accounts?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.cnn.com/cnn-underscored/reviews/mmmat-silicone-mats?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.harvard.edu/",
|
||||
"https://www.cnn.com/videos/travel/2023/04/18/century-old-shipwrecks-lake-superior-discovery-contd-orig-zt.cnn",
|
||||
"https://www.usps.com/",
|
||||
"https://www.cnn.com/2023/04/18/opinions/medication-abortion-mifepristone-miscarriage-jones-ctpr/index.html",
|
||||
"https://www.cnn.com/2023/04/18/politics/mccarthy-biden-debt-ceiling/index.html",
|
||||
"https://www.cnn.com/2023/04/17/opinions/jim-jordan-clarence-thomas-judiciary-committee-obeidallah/index.html",
|
||||
"https://www.cnn.com/2023/04/16/opinions/top-secrets-come-spilling-out-opinion-column-galant/index.html",
|
||||
"https://www.cdc.gov/",
|
||||
"https://www.target.com/",
|
||||
"https://www.walmart.com/",
|
||||
"https://www.usa.gov/",
|
||||
"https://www.cnn.com/2023/04/18/entertainment/jake-gyllenhaal-jamie-lee-curtis-pandemic-living/index.html"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.usa.gov/",
|
||||
"https://www.irs.gov/",
|
||||
"https://www.cdc.gov/",
|
||||
"https://www.harvard.edu/",
|
||||
"https://www.chase.com/",
|
||||
"https://www.bankofamerica.com/",
|
||||
"https://www.amazon.com/",
|
||||
"https://www.target.com/",
|
||||
"https://www.walmart.com/",
|
||||
"https://www.usps.com/"
|
||||
]
|
||||
}
|
||||
}
|
||||
45
data/regions/US/TX/Dallas.json
Normal file
45
data/regions/US/TX/Dallas.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"region_name": "United States - Dallas",
|
||||
"google_module": {
|
||||
"base_lat": 32.7767,
|
||||
"base_lon": -96.7970,
|
||||
"lang_params": "hl=en&gl=US",
|
||||
"valid_url_suffix": "com"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.dallasnews.com/",
|
||||
"https://www.texas.gov/",
|
||||
"https://www.wfaa.com/",
|
||||
"https://www.nbcdfw.com/",
|
||||
"https://www.fox4news.com/",
|
||||
"https://www.dallascityhall.com/",
|
||||
"https://www.cnn.com/",
|
||||
"https://www.foxnews.com/",
|
||||
"https://www.nytimes.com/",
|
||||
"https://www.wsj.com/",
|
||||
"https://www.usatoday.com/",
|
||||
"https://www.amazon.com/",
|
||||
"https://www.walmart.com/",
|
||||
"https://www.target.com/",
|
||||
"https://www.chase.com/",
|
||||
"https://www.bankofamerica.com/",
|
||||
"https://www.wellsfargo.com/",
|
||||
"https://www.zillow.com/",
|
||||
"https://www.espn.com/",
|
||||
"https://www.weather.com/"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.texas.gov/",
|
||||
"https://www.dallasnews.com/",
|
||||
"https://www.cnn.com/",
|
||||
"https://www.foxnews.com/",
|
||||
"https://www.nytimes.com/",
|
||||
"https://www.amazon.com/",
|
||||
"https://www.walmart.com/",
|
||||
"https://www.target.com/",
|
||||
"https://www.chase.com/",
|
||||
"https://www.bankofamerica.com/"
|
||||
]
|
||||
}
|
||||
}
|
||||
50
data/regions/US/UT/Salt_Lake_City.json
Normal file
50
data/regions/US/UT/Salt_Lake_City.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"region_name": "United States - Salt Lake City",
|
||||
"google_module": {
|
||||
"base_lat": 40.7608,
|
||||
"base_lon": -111.891,
|
||||
"lang_params": "hl=en&gl=US",
|
||||
"valid_url_suffix": "com"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.cnn.com/2023/04/18/entertainment/jake-gyllenhaal-jamie-lee-curtis-pandemic-living/index.html",
|
||||
"https://www.cnn.com/2023/04/16/opinions/top-secrets-come-spilling-out-opinion-column-galant/index.html",
|
||||
"https://www.npr.org/2026/04/24/nx-s1-5794052/nfl-draft-pittsburgh-fans",
|
||||
"https://www.cnn.com/style/article/ai-photo-win-sony-scli-intl/index.html",
|
||||
"https://www.cnn.com/videos/politics/2023/04/18/kevin-mccarthy-wall-street-speech-debt-ceiling-biden-economy-vpx.cnn",
|
||||
"https://www.cnn.com/2023/04/18/politics/mccarthy-biden-debt-ceiling/index.html",
|
||||
"https://www.cnn.com/cnn-underscored/money/high-yield-savings-accounts?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.chase.com/",
|
||||
"https://www.harvard.edu/",
|
||||
"https://www.cnn.com/2023/04/17/opinions/jim-jordan-clarence-thomas-judiciary-committee-obeidallah/index.html",
|
||||
"https://www.npr.org/2026/04/24/nx-s1-5797964/after-2-failed-votes-mike-johnson-unveils-new-plan-to-extend-key-u-s-spy-powers",
|
||||
"https://www.cnn.com/2023/04/18/politics/white-house-toddler/index.html",
|
||||
"https://www.cnn.com/cnn-underscored/reviews/mmmat-silicone-mats?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.cnn.com/videos/travel/2023/04/18/century-old-shipwrecks-lake-superior-discovery-contd-orig-zt.cnn",
|
||||
"https://www.target.com/",
|
||||
"https://www.cnn.com/2023/04/18/opinions/medication-abortion-mifepristone-miscarriage-jones-ctpr/index.html",
|
||||
"https://www.cdc.gov/",
|
||||
"https://www.bankofamerica.com/",
|
||||
"https://www.cnn.com/2023/04/18/us/benadryl-tiktok-challenge-teen-death-wellness/index.html",
|
||||
"https://www.irs.gov/",
|
||||
"https://www.usa.gov/",
|
||||
"https://www.walmart.com/",
|
||||
"https://www.amazon.com/",
|
||||
"https://www.usps.com/",
|
||||
"https://www.cnn.com/cnn-underscored/money/how-to-file-taxes?iid=CNNUnderscoredHPcontainer"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.usa.gov/",
|
||||
"https://www.irs.gov/",
|
||||
"https://www.cdc.gov/",
|
||||
"https://www.harvard.edu/",
|
||||
"https://www.chase.com/",
|
||||
"https://www.bankofamerica.com/",
|
||||
"https://www.amazon.com/",
|
||||
"https://www.target.com/",
|
||||
"https://www.walmart.com/",
|
||||
"https://www.usps.com/"
|
||||
]
|
||||
}
|
||||
}
|
||||
50
data/regions/US/WA/Seattle.json
Normal file
50
data/regions/US/WA/Seattle.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"region_name": "United States - Seattle",
|
||||
"google_module": {
|
||||
"base_lat": 47.6062,
|
||||
"base_lon": -122.3321,
|
||||
"lang_params": "hl=en&gl=US",
|
||||
"valid_url_suffix": "com"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://www.cnn.com/2023/04/18/politics/white-house-toddler/index.html",
|
||||
"https://www.cnn.com/cnn-underscored/money/high-yield-savings-accounts?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.walmart.com/",
|
||||
"https://www.cnn.com/2023/04/18/us/benadryl-tiktok-challenge-teen-death-wellness/index.html",
|
||||
"https://www.npr.org/2026/04/24/nx-s1-5797964/after-2-failed-votes-mike-johnson-unveils-new-plan-to-extend-key-u-s-spy-powers",
|
||||
"https://www.cnn.com/2023/04/18/opinions/medication-abortion-mifepristone-miscarriage-jones-ctpr/index.html",
|
||||
"https://www.cnn.com/style/article/ai-photo-win-sony-scli-intl/index.html",
|
||||
"https://www.cnn.com/cnn-underscored/money/how-to-file-taxes?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.cnn.com/videos/travel/2023/04/18/century-old-shipwrecks-lake-superior-discovery-contd-orig-zt.cnn",
|
||||
"https://www.usa.gov/",
|
||||
"https://www.cnn.com/cnn-underscored/reviews/mmmat-silicone-mats?iid=CNNUnderscoredHPcontainer",
|
||||
"https://www.cnn.com/2023/04/18/politics/mccarthy-biden-debt-ceiling/index.html",
|
||||
"https://www.cnn.com/videos/politics/2023/04/18/kevin-mccarthy-wall-street-speech-debt-ceiling-biden-economy-vpx.cnn",
|
||||
"https://www.harvard.edu/",
|
||||
"https://www.npr.org/2026/04/24/nx-s1-5794052/nfl-draft-pittsburgh-fans",
|
||||
"https://www.cnn.com/2023/04/16/opinions/top-secrets-come-spilling-out-opinion-column-galant/index.html",
|
||||
"https://www.bankofamerica.com/",
|
||||
"https://www.cnn.com/2023/04/18/entertainment/jake-gyllenhaal-jamie-lee-curtis-pandemic-living/index.html",
|
||||
"https://www.cdc.gov/",
|
||||
"https://www.target.com/",
|
||||
"https://www.amazon.com/",
|
||||
"https://www.chase.com/",
|
||||
"https://www.usps.com/",
|
||||
"https://www.irs.gov/",
|
||||
"https://www.cnn.com/2023/04/17/opinions/jim-jordan-clarence-thomas-judiciary-committee-obeidallah/index.html"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://www.usa.gov/",
|
||||
"https://www.irs.gov/",
|
||||
"https://www.cdc.gov/",
|
||||
"https://www.harvard.edu/",
|
||||
"https://www.chase.com/",
|
||||
"https://www.bankofamerica.com/",
|
||||
"https://www.amazon.com/",
|
||||
"https://www.target.com/",
|
||||
"https://www.walmart.com/",
|
||||
"https://www.usps.com/"
|
||||
]
|
||||
}
|
||||
}
|
||||
44
data/regions/VN/Default/Hanoi.json
Normal file
44
data/regions/VN/Default/Hanoi.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"region_name": "Vietnam - Hanoi",
|
||||
"google_module": {
|
||||
"base_lat": 21.0285,
|
||||
"base_lon": 105.8542,
|
||||
"lang_params": "hl=vi&gl=VN",
|
||||
"valid_url_suffix": "vn"
|
||||
},
|
||||
"trust_module": {
|
||||
"white_urls": [
|
||||
"https://shopee.vn/",
|
||||
"https://vnexpress.net/nguoi-dan-ong-dam-o-2-be-gai-bi-bat-5066872.html",
|
||||
"https://vnexpress.net/sat-hai-dong-nghiep-roi-khai-gian-la-tai-nan-5066845.html",
|
||||
"https://vnexpress.net/tim-mot-nguoi-de-o-lai-khong-phai-de-di-qua-5066545.html",
|
||||
"https://www.vietcombank.com.vn/",
|
||||
"https://vnexpress.net/de-xuat-doi-dat-ben-xe-mien-dong-lam-du-an-giam-ket-xe-hang-xanh-binh-trieu-5066900.html",
|
||||
"https://vnexpress.net/thoi-trang-sanh-doi-cua-hau-ve-vu-van-thanh-va-ban-gai-5065410.html",
|
||||
"https://vnu.edu.vn/",
|
||||
"https://vnexpress.net/toi-chon-o-do-bua-bon-giong-chong-de-gia-dinh-luon-vui-ve-5066891.html",
|
||||
"https://vnexpress.net/viet-nam-mua-oto-tu-nuoc-nao-nhieu-nhat-5066853.html",
|
||||
"https://vnexpress.net/msb-dat-muc-tieu-lai-8-000-ty-dong-5066868.html",
|
||||
"https://vnexpress.net/",
|
||||
"https://vnexpress.net/tim-nguoi-dong-hanh-chan-thanh-giua-cuoc-song-gian-di-5066547.html",
|
||||
"https://dichvucong.gov.vn/",
|
||||
"https://vnexpress.net/van-may-12-con-giap-con-giap-may-man-hom-nay-4-con-giap-thu-nhap-gap-doi-giau-sang-phu-quy-vao-cuoi-tuan-5066289.html",
|
||||
"https://vnexpress.net/cuu-vien-pho-va-kiem-sat-vien-vu-nu-sinh-tu-vong-o-vinh-long-bi-khoi-to-5066878.html",
|
||||
"https://tiki.vn/",
|
||||
"https://vnexpress.net/petrolimex-du-kien-lo-hon-1-000-ty-dong-kinh-doanh-xang-dau-quy-i-5066857.html",
|
||||
"https://vnexpress.net/khung-hoang-nha-ve-sinh-cong-cong-o-anh-5066244.html",
|
||||
"https://vnexpress.net/trang-phuc-cua-phu-nhan-tong-thong-han-quoc-khi-o-viet-nam-5066803.html",
|
||||
"https://vnexpress.net/bao-malaysia-viet-nam-qua-manh-5066906.html",
|
||||
"https://chinhphu.vn/"
|
||||
],
|
||||
"static_urls": [
|
||||
"https://chinhphu.vn/",
|
||||
"https://dichvucong.gov.vn/",
|
||||
"https://vnu.edu.vn/",
|
||||
"https://www.vietcombank.com.vn/",
|
||||
"https://shopee.vn/",
|
||||
"https://tiki.vn/",
|
||||
"https://vnexpress.net/"
|
||||
]
|
||||
}
|
||||
}
|
||||
4004
data/user_agents.txt
4004
data/user_agents.txt
File diff suppressed because it is too large
Load Diff
358
master/install_master.sh
Normal file → Executable file
358
master/install_master.sh
Normal file → Executable file
@@ -1,39 +1,258 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: install_master.sh (IP-Sentinel 控制中枢部署脚本)
|
||||
# 核心功能: 安装 SQLite3、初始化数据库表、配置后台守护进程
|
||||
# 脚本名称: install_master.sh (IP-Sentinel 控制中枢部署脚本 - 动态锚点版)
|
||||
# 核心功能: 部署/卸载调度中枢、SQLite 资产管理、平滑热更新引擎
|
||||
# ==========================================================
|
||||
|
||||
# ==========================================================
|
||||
# 🛑 核心权限防线: 检查是否以 root 权限运行
|
||||
# ==========================================================
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo -e "\033[31m❌ 权限被拒绝: 部署 IP-Sentinel 需要最高系统权限。\033[0m"
|
||||
echo -e "💡 请切换到 root 用户 (执行 su root 或 sudo -i) 后重新运行指令。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 你的 GitHub 仓库 Raw 数据直链前缀
|
||||
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||
# 临时改为开发地址用于测试
|
||||
# REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/v3.6.2-rc"
|
||||
|
||||
# [核心: 动态提取 Master 专属版本锚点 (KV 解析法)]
|
||||
# 通过 grep 定位 MASTER_VERSION 行,再通过 cut 提取等号右侧的值
|
||||
# [修复] 增加 -L 与双栈容灾 (-4),解决纯 V6 或 V6 优先机器连接 GitHub Raw 易超时的问题
|
||||
TARGET_VERSION=$( (curl -sL -m 5 "${REPO_RAW_URL}/version.txt" || curl -4 -sL -m 5 "${REPO_RAW_URL}/version.txt") 2>/dev/null | grep "^MASTER_VERSION=" | cut -d'=' -f2 | tr -d '[:space:]')
|
||||
|
||||
# 🛡️ 兜底防线:如果网络波动拉取失败,启用内置的最新兜底版本
|
||||
TARGET_VERSION=${TARGET_VERSION:-"4.0.0"}
|
||||
|
||||
MASTER_DIR="/opt/ip_sentinel_master"
|
||||
DB_FILE="${MASTER_DIR}/sentinel.db"
|
||||
|
||||
echo "========================================================"
|
||||
echo " 🧠 准备部署 IP-Sentinel Master 控制中枢"
|
||||
# [修改] 将欢迎语改为更通用的文案,因为现在不仅能部署,还能卸载
|
||||
echo " 🧠 欢迎使用 IP-Sentinel Master (控制中枢) v${TARGET_VERSION}"
|
||||
echo "========================================================"
|
||||
|
||||
# 1. 环境依赖安装
|
||||
echo "[1/4] 安装核心依赖 (curl, jq, sqlite3)..."
|
||||
if [ -f /etc/debian_version ]; then
|
||||
apt-get update -y >/dev/null 2>&1
|
||||
apt-get install -y curl jq sqlite3 procps >/dev/null 2>&1
|
||||
elif [ -f /etc/redhat-release ]; then
|
||||
yum install -y curl jq sqlite >/dev/null 2>&1
|
||||
# ==========================================================
|
||||
# [v3.6.1 核心] 拦截司令部静默 OTA 升级模式 (强行接管执行流)
|
||||
# ==========================================================
|
||||
if [ "$SILENT_MASTER_OTA" == "true" ]; then
|
||||
echo -e "\n⏳ [OTA] 中枢重构指令已确认,正在剥离控制台交互..."
|
||||
ACTION_CHOICE=1
|
||||
UPGRADE_MODE="true"
|
||||
KEEP_DB="true"
|
||||
|
||||
# 汲取原配置进入内存
|
||||
if [ -f "${MASTER_DIR}/master.conf" ]; then
|
||||
source "${MASTER_DIR}/master.conf"
|
||||
|
||||
# 同步新版本号至配置文件
|
||||
if grep -q "^MASTER_VERSION=" "${MASTER_DIR}/master.conf"; then
|
||||
sed -i "s/^MASTER_VERSION=.*/MASTER_VERSION=\"$TARGET_VERSION\"/" "${MASTER_DIR}/master.conf"
|
||||
else
|
||||
echo "MASTER_VERSION=\"$TARGET_VERSION\"" >> "${MASTER_DIR}/master.conf"
|
||||
fi
|
||||
fi
|
||||
echo -e "\033[32m✅ 已激活 [中枢静默重构模式],即将无损覆写内核...\033[0m"
|
||||
else
|
||||
# [新增] 交互式操作菜单:支持选择部署或调用卸载程序
|
||||
echo -e "\n请选择操作:"
|
||||
echo " 1) 🚀 部署 Master 控制中枢"
|
||||
echo " 2) 🗑️ 一键卸载 Master 中枢"
|
||||
read -p "请输入选择 [1-2] (默认1): " ACTION_CHOICE
|
||||
|
||||
# [v3.5.2 修复] 防止用户直接回车导致变量为空,从而漏过下方的平滑升级判定被误删档
|
||||
ACTION_CHOICE=${ACTION_CHOICE:-1}
|
||||
|
||||
if [ "$ACTION_CHOICE" == "2" ]; then
|
||||
echo -e "\n⏳ 正在拉取卸载程序..."
|
||||
curl -sL "${REPO_RAW_URL}/master/uninstall_master.sh" -o "/tmp/uninstall_master.sh"
|
||||
chmod +x "/tmp/uninstall_master.sh"
|
||||
bash "/tmp/uninstall_master.sh"
|
||||
rm -f "/tmp/uninstall_master.sh"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ================== [v3.2.2 新增: 平滑升级模式嗅探] ==================
|
||||
UPGRADE_MODE="false"
|
||||
KEEP_DB="true"
|
||||
|
||||
if [ "$ACTION_CHOICE" == "1" ] && [ -f "${MASTER_DIR}/master.conf" ]; then
|
||||
echo -e "\n\033[33m💡 司令部雷达提示:检测到本机已部署过 Master 中枢。\033[0m"
|
||||
read -p "👉 是否按原配置直接进行平滑升级?(y/n, 默认y): " UPGRADE_CHOICE
|
||||
if [[ -z "$UPGRADE_CHOICE" || "$UPGRADE_CHOICE" =~ ^[Yy]$ ]]; then
|
||||
UPGRADE_MODE="true"
|
||||
read -p "👉 是否保留历史节点数据库 (SQLite)?(y/n, 默认y): " DB_CHOICE
|
||||
if [[ "$DB_CHOICE" =~ ^[Nn]$ ]]; then
|
||||
KEEP_DB="false"
|
||||
fi
|
||||
|
||||
source "${MASTER_DIR}/master.conf"
|
||||
|
||||
if grep -q "^MASTER_VERSION=" "${MASTER_DIR}/master.conf"; then
|
||||
sed -i "s/^MASTER_VERSION=.*/MASTER_VERSION=\"$TARGET_VERSION\"/" "${MASTER_DIR}/master.conf"
|
||||
else
|
||||
echo "MASTER_VERSION=\"$TARGET_VERSION\"" >> "${MASTER_DIR}/master.conf"
|
||||
fi
|
||||
|
||||
echo -e "\033[32m✅ 已激活 [平滑升级模式],版本已锚定为 v${TARGET_VERSION}...\033[0m"
|
||||
else
|
||||
echo -e "\033[33m🔄 您选择了重新配置,旧的中枢数据将被彻底抹除。\033[0m"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# ================== [v3.2.2 优化: 安装前环境纯净度清理与数据保护] ==================
|
||||
echo -e "\n⏳ 正在清理旧版 Master 守护进程..."
|
||||
# [新增] 优雅停止 Systemd 服务,防止代码替换时引发无限复活风暴
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl stop ip-sentinel-master.service >/dev/null 2>&1 || true
|
||||
fi
|
||||
pkill -9 -f "tg_master.sh" >/dev/null 2>&1 || true
|
||||
|
||||
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
if [ "$KEEP_DB" == "false" ]; then
|
||||
rm -f "$DB_FILE" 2>/dev/null
|
||||
echo -e "🗑️ 历史节点数据库已按指令清空。"
|
||||
else
|
||||
echo -e "📦 历史节点数据库 (SQLite) 已绝密保留。"
|
||||
fi
|
||||
# 删除旧的核心脚本,准备拉取新的
|
||||
rm -f "${MASTER_DIR}/tg_master.sh" 2>/dev/null
|
||||
else
|
||||
# 焦土政策:如果不是升级模式,直接扬了整个司令部目录
|
||||
rm -rf "$MASTER_DIR" 2>/dev/null
|
||||
fi
|
||||
echo -e "\033[32m✅ 旧进程已肃清!\033[0m"
|
||||
# =======================================================================
|
||||
|
||||
# 1. 依赖检查与智能安装 (v3.6.0 兼容性与优雅性升级)
|
||||
echo -e "\n[1/4] 正在探测核心依赖 (curl, jq, sqlite3, crontab, pgrep, openssl)..."
|
||||
|
||||
REQUIRED_CMDS=("curl" "jq" "sqlite3" "crontab" "pgrep" "openssl")
|
||||
MISSING_CMDS=()
|
||||
|
||||
# 基础探测:预检查缺失的命令
|
||||
for cmd in "${REQUIRED_CMDS[@]}"; do
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
MISSING_CMDS+=("$cmd")
|
||||
fi
|
||||
done
|
||||
|
||||
# 如果有缺失,才执行包管理器拉取逻辑
|
||||
if [ ${#MISSING_CMDS[@]} -gt 0 ]; then
|
||||
echo "⏳ 发现缺失依赖: ${MISSING_CMDS[*]},正在尝试自动补齐..."
|
||||
|
||||
if command -v apt-get >/dev/null 2>&1; then
|
||||
apt-get update -y >/dev/null 2>&1
|
||||
# [v3.6.3 抽脂级优化] 注入 --no-install-recommends 拒绝捆绑销售
|
||||
apt-get install -y --no-install-recommends curl jq sqlite3 cron procps openssl >/dev/null 2>&1
|
||||
systemctl enable cron >/dev/null 2>&1 && systemctl start cron >/dev/null 2>&1
|
||||
elif command -v yum >/dev/null 2>&1 || command -v dnf >/dev/null 2>&1; then
|
||||
PKG_MGR="yum"
|
||||
OPT_ARGS=""
|
||||
if command -v dnf >/dev/null 2>&1; then
|
||||
PKG_MGR="dnf"
|
||||
# [v3.6.3 抽脂级优化] 强行关闭 DNF 的弱依赖拉取
|
||||
OPT_ARGS="--setopt=install_weak_deps=False"
|
||||
fi
|
||||
$PKG_MGR install -y $OPT_ARGS curl jq sqlite cronie procps-ng openssl >/dev/null 2>&1
|
||||
systemctl enable crond >/dev/null 2>&1 && systemctl start crond >/dev/null 2>&1
|
||||
elif command -v apk >/dev/null 2>&1; then
|
||||
echo "Alpine 探测到系统类型为 Alpine Linux,正在执行轻量级安装..."
|
||||
# [修复] 优先尝试 cronie,若失败则回退至系统内置 cron,彻底避免单点依赖拖垮全局
|
||||
apk add --no-cache curl jq sqlite cronie procps bash openssl || apk add --no-cache curl jq sqlite procps bash openssl
|
||||
mkdir -p /var/spool/cron/crontabs
|
||||
rc-update add crond default >/dev/null 2>&1
|
||||
service crond start >/dev/null 2>&1
|
||||
elif command -v pacman >/dev/null 2>&1; then
|
||||
pacman -Sy --noconfirm curl jq sqlite cronie procps-ng openssl >/dev/null 2>&1
|
||||
mkdir -p /root/.cache/crontab 2>/dev/null
|
||||
systemctl enable cronie >/dev/null 2>&1 && systemctl start cronie >/dev/null 2>&1
|
||||
else
|
||||
echo -e "\033[31m❌ 自动安装失败:系统未知的包管理器。\033[0m"
|
||||
echo -e "\033[33m⚠️ 请手动执行以下安装命令后重新运行本脚本:\033[0m"
|
||||
echo -e " Debian/Ubuntu: \033[36mapt-get update && apt-get install -y --no-install-recommends curl jq sqlite3 cron procps openssl\033[0m"
|
||||
echo -e " CentOS/RHEL: \033[36myum install -y curl jq sqlite cronie procps-ng openssl\033[0m"
|
||||
echo -e " Alpine Linux: \033[36mapk add --no-cache curl jq sqlite cronie procps bash openssl\033[0m"
|
||||
echo -e " Arch Linux: \033[36mpacman -Sy curl jq sqlite cronie procps-ng openssl\033[0m"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 安装后二次复检
|
||||
for cmd in "${REQUIRED_CMDS[@]}"; do
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
echo -e "\033[31m❌ 致命错误:核心命令 '$cmd' 仍未找到!\033[0m"
|
||||
echo -e "请手动修复您的包管理器源,或联系 VPS 供应商。"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
fi
|
||||
echo -e "\033[32m✅ 基础环境检测通过。\033[0m"
|
||||
|
||||
mkdir -p "$MASTER_DIR"
|
||||
|
||||
# 2. 交互配置机器人
|
||||
echo -e "\n[2/4] 配置控制中枢机器人:"
|
||||
read -p "请输入 Telegram Bot Token: " TG_TOKEN
|
||||
# ==========================================================
|
||||
# 🛑 如果是全新部署,才询问 Token 并写入配置
|
||||
# ==========================================================
|
||||
if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
# 2. 交互配置机器人
|
||||
echo -e "\n[2/4] 配置控制中枢机器人:"
|
||||
read -p "请输入 Telegram Bot Token: " TG_TOKEN
|
||||
|
||||
# [v3.6.0 新增] 官方网关模式选项 (用于屏蔽全局 OTA 按钮)
|
||||
echo -e "\n请选择您的部署环境身份:"
|
||||
echo " 1) 🛡️ 私有独立中枢 (默认推荐,保留完整 OTA 遥控权限)"
|
||||
echo " 2) ☁️ 官方公共网关 (面向大众服务,将强制物理隐藏全局 OTA 按钮防滥用)"
|
||||
read -p "请输入选择 [1-2] (默认1): " GATEWAY_TYPE
|
||||
GATEWAY_TYPE=${GATEWAY_TYPE:-1}
|
||||
|
||||
IS_OFFICIAL_GATEWAY="false"
|
||||
ENABLE_MASTER_OTA="false"
|
||||
if [ "$GATEWAY_TYPE" == "2" ]; then
|
||||
IS_OFFICIAL_GATEWAY="true"
|
||||
echo -e "\033[33m⚠️ 已开启官方公共网关模式,全舰队与司令部的 OTA 将被强制屏蔽。\033[0m"
|
||||
else
|
||||
# [v3.6.1] 私有模式开放中枢 OTA 授权向导
|
||||
echo -e "\n[2.1/4] 司令部自我进化授权"
|
||||
echo -e "💡 开启后,您可以在 TG 菜单一键将中枢核心系统热更新至最新版本。"
|
||||
read -p "是否允许司令部接收 OTA 重构指令?(y/n, 默认y): " M_OTA_CHOICE
|
||||
if [[ "$M_OTA_CHOICE" =~ ^[Nn]$ ]]; then
|
||||
ENABLE_MASTER_OTA="false"
|
||||
echo -e "🛡️ \033[33m已关闭司令部 OTA 权限,中枢内核未来仅支持 SSH 升级。\033[0m"
|
||||
else
|
||||
ENABLE_MASTER_OTA="true"
|
||||
echo -e "✅ \033[32m已开启司令部 OTA 权限,金蝉脱壳引信已挂载。\033[0m"
|
||||
fi
|
||||
fi
|
||||
|
||||
cat > "${MASTER_DIR}/master.conf" << EOF
|
||||
cat > "${MASTER_DIR}/master.conf" << EOF
|
||||
# IP-Sentinel Master 本地固化配置 (v${TARGET_VERSION})
|
||||
MASTER_VERSION="$TARGET_VERSION"
|
||||
TG_TOKEN="$TG_TOKEN"
|
||||
DB_FILE="$DB_FILE"
|
||||
MASTER_DIR="$MASTER_DIR"
|
||||
# [v3.6.0 核心] 官方网关 UI 熔断标识
|
||||
IS_OFFICIAL_GATEWAY="$IS_OFFICIAL_GATEWAY"
|
||||
# [v3.6.1 新增] 司令部自身 OTA 授权标识
|
||||
ENABLE_MASTER_OTA="$ENABLE_MASTER_OTA"
|
||||
EOF
|
||||
fi
|
||||
|
||||
# 3. 初始化 SQLite 数据库
|
||||
# [v3.6.1 热修复] 老司令部平滑升级时,自动补齐缺失字段
|
||||
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
if ! grep -q "^IS_OFFICIAL_GATEWAY=" "${MASTER_DIR}/master.conf"; then
|
||||
echo "IS_OFFICIAL_GATEWAY=\"false\"" >> "${MASTER_DIR}/master.conf"
|
||||
fi
|
||||
if ! grep -q "^ENABLE_MASTER_OTA=" "${MASTER_DIR}/master.conf"; then
|
||||
echo "ENABLE_MASTER_OTA=\"false\"" >> "${MASTER_DIR}/master.conf"
|
||||
fi
|
||||
fi
|
||||
# 🛑 拦截块结束
|
||||
|
||||
# 3. 初始化 SQLite 数据库 (幂等操作,升级模式下由 tg_master.sh 负责热修补)
|
||||
echo -e "\n[3/4] 正在初始化 SQLite 数据库表结构..."
|
||||
sqlite3 "$DB_FILE" <<EOF
|
||||
CREATE TABLE IF NOT EXISTS nodes (
|
||||
@@ -42,26 +261,113 @@ CREATE TABLE IF NOT EXISTS nodes (
|
||||
agent_ip TEXT,
|
||||
agent_port TEXT,
|
||||
last_seen DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
region TEXT DEFAULT 'UNKNOWN',
|
||||
node_alias TEXT,
|
||||
enable_google TEXT DEFAULT 'true',
|
||||
enable_trust TEXT DEFAULT 'true',
|
||||
enable_ota TEXT DEFAULT 'false',
|
||||
PRIMARY KEY(chat_id, node_name)
|
||||
);
|
||||
|
||||
-- [v4.0.0 新增, v4.0.2 扩容] 核心情报表:记录历史 IP 质量数据,用于绘制趋势图
|
||||
CREATE TABLE IF NOT EXISTS ip_trend_log (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
node_name TEXT,
|
||||
check_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
scam_score INTEGER,
|
||||
goog_status TEXT,
|
||||
nf_status TEXT,
|
||||
gpt_status TEXT
|
||||
);
|
||||
EOF
|
||||
echo "✅ 数据库创建成功: $DB_FILE"
|
||||
|
||||
# ================== [v3.0.3 变更: 敏感文件权限收敛] ==================
|
||||
chmod 600 "${MASTER_DIR}/master.conf"
|
||||
chmod 600 "$DB_FILE"
|
||||
# ====================================================================
|
||||
|
||||
# 4. 拉取核心调度代码并运行
|
||||
echo -e "\n[4/4] 部署 TG 调度守护进程..."
|
||||
curl -sL "https://git.94211762.xyz/hotyue/IP-Sentinel/raw/branch/main/master/tg_master.sh" -o "${MASTER_DIR}/tg_master.sh"
|
||||
# [修改] 剥离了写死的网址,改用顶部的 ${REPO_RAW_URL} 变量,确保与卸载脚本的数据源同源
|
||||
curl -sL "${REPO_RAW_URL}/master/tg_master.sh" -o "${MASTER_DIR}/tg_master.sh"
|
||||
chmod +x "${MASTER_DIR}/tg_master.sh"
|
||||
|
||||
# 写入看门狗 Cron
|
||||
crontab -l 2>/dev/null | grep -v "tg_master.sh" > /tmp/cron_master
|
||||
echo "* * * * * pgrep -f tg_master.sh >/dev/null || nohup bash ${MASTER_DIR}/tg_master.sh >/dev/null 2>&1 &" >> /tmp/cron_master
|
||||
crontab /tmp/cron_master
|
||||
rm -f /tmp/cron_master
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
echo "💡 检测到 Systemd 环境,正在部署原生守护服务..."
|
||||
|
||||
cat > /etc/systemd/system/ip-sentinel-master.service << EOF
|
||||
[Unit]
|
||||
Description=IP-Sentinel Master Command Center Service
|
||||
After=network.target
|
||||
|
||||
# 立刻启动
|
||||
pgrep -f tg_master.sh >/dev/null || nohup bash "${MASTER_DIR}/tg_master.sh" >/dev/null 2>&1 &
|
||||
[Service]
|
||||
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
SyslogIdentifier=ip-sentinel
|
||||
Type=simple
|
||||
ExecStart=/bin/bash ${MASTER_DIR}/tg_master.sh
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
User=root
|
||||
WorkingDirectory=${MASTER_DIR}
|
||||
CPUSchedulingPolicy=idle
|
||||
IOSchedulingClass=idle
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now ip-sentinel-master.service
|
||||
systemctl restart ip-sentinel-master.service
|
||||
|
||||
# 清理可能残留的历史 Cron
|
||||
crontab -l 2>/dev/null | grep -v "tg_master.sh" > /tmp/cron_master || true
|
||||
[ -f /tmp/cron_master ] && crontab /tmp/cron_master 2>/dev/null
|
||||
rm -f /tmp/cron_master
|
||||
else
|
||||
echo "💡 未检测到 Systemd,回退到 Cron 看门狗调度模式..."
|
||||
crontab -l 2>/dev/null | grep -v "tg_master.sh" > /tmp/cron_master || true
|
||||
echo "* * * * * pgrep -f tg_master.sh >/dev/null || nohup bash ${MASTER_DIR}/tg_master.sh >/dev/null 2>&1 &" >> /tmp/cron_master
|
||||
[ -f /tmp/cron_master ] && crontab /tmp/cron_master 2>/dev/null
|
||||
rm -f /tmp/cron_master
|
||||
|
||||
pgrep -f tg_master.sh >/dev/null || { nohup bash "${MASTER_DIR}/tg_master.sh" >/dev/null 2>&1 & disown 2>/dev/null; }
|
||||
fi
|
||||
|
||||
# ================== [v3.2.2 优化 & v3.6.1 OTA捷报: 战报文案分流] ==================
|
||||
echo "========================================================"
|
||||
echo "🎉 Master 控制中枢部署完成!"
|
||||
echo "🤖 机器人现已开始全局接客,等待边缘节点注册。"
|
||||
echo "========================================================"
|
||||
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||
echo "🎉 Master 控制中枢平滑热更新完成!"
|
||||
echo "🤖 新版中枢引擎已接管数据库,继续等待边缘节点汇报。"
|
||||
|
||||
# [v3.6.1 核心] 静默 OTA 完成后,由幽灵进程主动向指挥官发送捷报
|
||||
if [ "$SILENT_MASTER_OTA" == "true" ] && [ -n "$OTA_CHAT_ID" ] && [ -n "$TG_TOKEN" ]; then
|
||||
echo -e "\n📡 正在向指挥官发送司令部重构捷报..."
|
||||
curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
|
||||
-d "chat_id=${OTA_CHAT_ID}" \
|
||||
-d "parse_mode=Markdown" \
|
||||
-d "text=✨ *司令部中枢热重载完成!*
|
||||
🚀 当前内核已跃升至:\`v${TARGET_VERSION}\`
|
||||
🤖 新版金蝉脱壳引擎已接管阵地,全舰队指控链路恢复正常。" > /dev/null
|
||||
fi
|
||||
else
|
||||
echo "🎉 Master 控制中枢部署完成!"
|
||||
echo "🤖 机器人现已开始全局接客,等待边缘节点注册。"
|
||||
fi
|
||||
echo "========================================================"
|
||||
# =================================================================
|
||||
|
||||
# ================== [v3.1.2 新增: 玻璃房透明装机统计] ==================
|
||||
# [修复] 仅在全新部署时触发统计,司令部热重载时绝对不触发
|
||||
if [ "$UPGRADE_MODE" == "false" ]; then
|
||||
echo -e "\n📡 正在向开源社区汇报装机量 (完全匿名,不收集IP)..."
|
||||
MASTER_COUNT=$(curl -s -m 3 "https://ip-sentinel-count.samanthaestime296.workers.dev/ping/master" || echo "")
|
||||
|
||||
if [ -n "$MASTER_COUNT" ] && [[ "$MASTER_COUNT" =~ ^[0-9]+$ ]]; then
|
||||
echo -e "\033[32m✅ 感谢您成为全球第 ${MASTER_COUNT} 名 IP-Sentinel 指挥官!\033[0m"
|
||||
else
|
||||
echo -e "\033[32m✅ 感谢您建立 IP-Sentinel 司令部!\033[0m"
|
||||
fi
|
||||
echo -e "\n"
|
||||
fi
|
||||
|
||||
819
master/tg_master.sh
Normal file → Executable file
819
master/tg_master.sh
Normal file → Executable file
@@ -1,38 +1,124 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: tg_master.sh (Master 端调度枢纽 V1.2)
|
||||
# 核心功能: 监听 TG、操作 SQLite、Webhook 调度、僵尸节点清理
|
||||
# 脚本名称: tg_master.sh (Master 端调度枢纽 - 动态锚点版)
|
||||
# 核心功能: 监听 TG、操作 SQLite、Webhook 精准调度、403权限拦截、僵尸节点清理
|
||||
# ==========================================================
|
||||
|
||||
CONF="/opt/ip_sentinel_master/master.conf"
|
||||
[ ! -f "$CONF" ] && exit 1
|
||||
source "$CONF"
|
||||
|
||||
OFFSET_FILE="/tmp/tg_master_offset"
|
||||
# [核心: 运行态版本继承与云通信地址]
|
||||
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||
|
||||
# MASTER_VERSION 已经在上方的 source "$CONF" 中被载入
|
||||
# 如果本地极度陈旧没有该变量,才给定一个基础兜底值,避免变量为空导致崩溃
|
||||
MASTER_VERSION=${MASTER_VERSION:-"3.5.0"}
|
||||
|
||||
OFFSET_FILE="${MASTER_DIR}/.tg_offset"
|
||||
[[ -f $OFFSET_FILE ]] || echo "0" > $OFFSET_FILE
|
||||
|
||||
# --- 工具函数 ---
|
||||
# ================== [v4.0.3 核心: 全球全能旗帜渲染引擎] ==================
|
||||
get_flag() {
|
||||
local region=$(echo "$1" | tr 'a-z' 'A-Z')
|
||||
local base_cc="${region%%-*}" # 提取横杠前的主国家代码 (例如 US-TX 提取为 US)
|
||||
local flag="🌐"
|
||||
case "$base_cc" in
|
||||
US) flag="🇺🇸" ;; JP) flag="🇯🇵" ;; HK) flag="🇭🇰" ;; TW) flag="🇹🇼" ;; SG) flag="🇸🇬" ;;
|
||||
UK|GB) flag="🇬🇧" ;; DE) flag="🇩🇪" ;; FR) flag="🇫🇷" ;; NL) flag="🇳🇱" ;; CA) flag="🇨🇦" ;;
|
||||
AU) flag="🇦🇺" ;; KR) flag="🇰🇷" ;; IN) flag="🇮🇳" ;; BR) flag="🇧🇷" ;; RU) flag="🇷🇺" ;;
|
||||
CH) flag="🇨🇭" ;; SE) flag="🇸🇪" ;; NO) flag="🇳🇴" ;; DK) flag="🇩🇰" ;; FI) flag="🇫🇮" ;;
|
||||
IT) flag="🇮🇹" ;; ES) flag="🇪🇸" ;; PT) flag="🇵🇹" ;; IE) flag="🇮🇪" ;; PL) flag="🇵🇱" ;;
|
||||
AT) flag="🇦🇹" ;; BE) flag="🇧🇪" ;; TR) flag="🇹🇷" ;; ZA) flag="🇿🇦" ;; AE) flag="🇦🇪" ;;
|
||||
MY) flag="🇲🇾" ;; ID) flag="🇮🇩" ;; VN) flag="🇻🇳" ;; TH) flag="🇹🇭" ;; PH) flag="🇵🇭" ;;
|
||||
NZ) flag="🇳🇿" ;; AR) flag="🇦🇷" ;; CL) flag="🇨🇱" ;; MX) flag="🇲🇽" ;; IL) flag="🇮🇱" ;;
|
||||
SA) flag="🇸🇦" ;; EG) flag="🇪🇬" ;; NG) flag="🇳🇬" ;; KE) flag="🇰🇪" ;; RO) flag="🇷🇴" ;;
|
||||
BG) flag="🇧🇬" ;; CZ) flag="🇨🇿" ;; HU) flag="🇭🇺" ;; GR) flag="🇬🇷" ;; UA) flag="🇺🇦" ;;
|
||||
esac
|
||||
echo "$flag"
|
||||
}
|
||||
|
||||
send_ui() {
|
||||
curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
|
||||
curl -s --connect-timeout 5 -m 10 -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"chat_id\":\"$1\",\"text\":\"$2\",\"parse_mode\":\"Markdown\",\"reply_markup\":{\"inline_keyboard\":$3}}" > /dev/null
|
||||
}
|
||||
|
||||
send_msg() {
|
||||
curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
|
||||
curl -s --connect-timeout 5 -m 10 -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
|
||||
-d "chat_id=$1" -d "text=$2" -d "parse_mode=Markdown" > /dev/null
|
||||
}
|
||||
|
||||
# 数据库执行函数
|
||||
db_exec() {
|
||||
sqlite3 "$DB_FILE" "$1"
|
||||
# ================== [v3.0.1 新增: 消息原位刷新函数] ==================
|
||||
edit_msg() {
|
||||
curl -s --connect-timeout 5 -m 10 -X POST "https://api.telegram.org/bot${TG_TOKEN}/editMessageText" \
|
||||
-d "chat_id=$1" -d "message_id=$2" -d "text=$3" -d "parse_mode=Markdown" > /dev/null
|
||||
}
|
||||
|
||||
# [v3.5.3 新增: 支持内联键盘的原位 UI 重绘函数]
|
||||
edit_ui() {
|
||||
curl -s --connect-timeout 5 -m 10 -X POST "https://api.telegram.org/bot${TG_TOKEN}/editMessageText" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"chat_id\":\"$1\",\"message_id\":\"$2\",\"text\":\"$3\",\"parse_mode\":\"Markdown\",\"reply_markup\":{\"inline_keyboard\":$4}}" > /dev/null
|
||||
}
|
||||
|
||||
# 数据库执行函数 (v3.6.3 终极静默版: 动用 .timeout 点命令防泄露)
|
||||
db_exec() {
|
||||
printf ".timeout 5000\n%s\n" "$1" | sqlite3 "$DB_FILE"
|
||||
}
|
||||
|
||||
# ================== [v3.0.4 核心: 动态 HMAC 签名生成器] ==================
|
||||
# 用法: generate_signed_url <IP> <PORT> <PATH>
|
||||
generate_signed_url() {
|
||||
local target_ip=$1
|
||||
local target_port=$2
|
||||
local action_path=$3
|
||||
local current_t=$(date +%s)
|
||||
|
||||
# 构建加密载荷: "路径:时间戳"
|
||||
local payload="${action_path}:${current_t}"
|
||||
|
||||
# 使用 CHAT_ID 作为密钥,生成 SHA256 HMAC 签名
|
||||
local signature=$(echo -n "$payload" | openssl dgst -sha256 -hmac "$CHAT_ID" | awk '{print $NF}')
|
||||
|
||||
# 返回最终带签名的 URL
|
||||
echo "https://${target_ip}:${target_port}${action_path}?t=${current_t}&sign=${signature}"
|
||||
}
|
||||
# ========================================================================
|
||||
|
||||
# ================== [v3.6.3 核心: 激活 SQLite 高并发 WAL 引擎] ==================
|
||||
db_exec "PRAGMA journal_mode=WAL;" > /dev/null 2>&1
|
||||
db_exec "PRAGMA synchronous=NORMAL;" > /dev/null 2>&1
|
||||
# ==============================================================================
|
||||
|
||||
# ================== [v3.1.3-v3.6.0 核心: 数据库结构无损热升级] ==================
|
||||
# 自动探测并增加缺失字段,屏蔽已存在的报错,保护老节点数据
|
||||
db_exec "ALTER TABLE nodes ADD COLUMN region TEXT DEFAULT 'UNKNOWN';" 2>/dev/null
|
||||
db_exec "ALTER TABLE nodes ADD COLUMN node_alias TEXT;" 2>/dev/null
|
||||
db_exec "ALTER TABLE nodes ADD COLUMN enable_google TEXT DEFAULT 'true';" 2>/dev/null
|
||||
db_exec "ALTER TABLE nodes ADD COLUMN enable_trust TEXT DEFAULT 'true';" 2>/dev/null
|
||||
db_exec "ALTER TABLE nodes ADD COLUMN enable_ota TEXT DEFAULT 'false';" 2>/dev/null
|
||||
# ========================================================================
|
||||
|
||||
# ================== [v4.0.0/v4.0.2 核心: 增加 IP 质量趋势追踪表] ==================
|
||||
db_exec "CREATE TABLE IF NOT EXISTS ip_trend_log (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
node_name TEXT,
|
||||
check_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
scam_score INTEGER,
|
||||
nf_status TEXT
|
||||
);" 2>/dev/null
|
||||
# [v4.0.2 热更新] 动态扩容 谷歌 与 ChatGPT 状态追踪字段
|
||||
db_exec "ALTER TABLE ip_trend_log ADD COLUMN goog_status TEXT DEFAULT 'Unknown';" 2>/dev/null
|
||||
db_exec "ALTER TABLE ip_trend_log ADD COLUMN gpt_status TEXT DEFAULT 'Unknown';" 2>/dev/null
|
||||
# ========================================================================
|
||||
|
||||
# --- 核心轮询循环 ---
|
||||
while true; do
|
||||
OFFSET=$(cat $OFFSET_FILE)
|
||||
UPDATES=$(curl -s "https://api.telegram.org/bot${TG_TOKEN}/getUpdates?offset=${OFFSET}&timeout=30")
|
||||
UPDATES=$(curl -s --connect-timeout 5 -m 35 "https://api.telegram.org/bot${TG_TOKEN}/getUpdates?offset=${OFFSET}&timeout=30")
|
||||
|
||||
COUNT=$(echo "$UPDATES" | jq -r '.result | length' 2>/dev/null)
|
||||
|
||||
@@ -44,98 +130,713 @@ while true; do
|
||||
CHAT_ID=$(echo "$UPDATE" | jq -r '.message.chat.id // .callback_query.message.chat.id')
|
||||
TEXT=$(echo "$UPDATE" | jq -r '.message.text // .callback_query.data')
|
||||
|
||||
# ================== [v4.0.2 核心: 态势感知按钮一键入库] ==================
|
||||
if [[ "$TEXT" == "svq|"* ]]; then
|
||||
# 格式: svq|NODE_NAME|SCORE|GOOG|NF|GPT
|
||||
IFS='|' read -r MAGIC NODE_ID SCORE GOOG_ST NF_ST GPT_ST <<< "$TEXT"
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
|
||||
if [ -n "$NODE_ID" ] && [ -n "$SCORE" ]; then
|
||||
# 1. 写入 SQLite
|
||||
db_exec "INSERT INTO ip_trend_log (node_name, scam_score, goog_status, nf_status, gpt_status) VALUES ('$NODE_ID', '$SCORE', '$GOOG_ST', '$NF_ST', '$GPT_ST');"
|
||||
|
||||
# 2. 无损修改原消息:移除入库按钮,展示绿勾状态 (不破坏 Markdown 战报原文)
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
curl -s --connect-timeout 5 -m 10 -X POST "https://api.telegram.org/bot${TG_TOKEN}/editMessageReplyMarkup" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"chat_id\":\"${CHAT_ID}\",\"message_id\":\"${MSG_ID}\",\"reply_markup\":{\"inline_keyboard\":[[{\"text\":\"✅ 此报告已存档\",\"callback_data\":\"ignore\"}]]}}" > /dev/null
|
||||
fi
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
# ======================================================================
|
||||
|
||||
REPLY_TO_TEXT=$(echo "$UPDATE" | jq -r '.message.reply_to_message.text // empty')
|
||||
|
||||
# ================== [v3.5.2 新增: 拦截别名修改的对话回复] ==================
|
||||
if [[ "$REPLY_TO_TEXT" == *"✏️ 请回复本消息以重命名节点:"* ]]; then
|
||||
# 精准提取被回复消息中的节点主键名
|
||||
TARGET_NODE=$(echo "$REPLY_TO_TEXT" | grep -v "✏️" | grep -v "仅限" | tr -d '\` ' | tr -cd 'a-zA-Z0-9_.-' | head -n 1)
|
||||
|
||||
# [v3.5.2 热修复] 废除 Bash 原生 tr 命令的中文白名单 (不支持 Unicode 会误删中文)。
|
||||
# 改用黑名单策略:仅自动转化下划线,剔除引号、特殊符号和冒号(防止破坏内部路由),
|
||||
# 将完整的中文原样送入 Base64 编码,最终严格正则清洗交由 Agent 的 Python 引擎处理!
|
||||
NEW_ALIAS=$(echo "$TEXT" | sed 's/_/-/g' | tr -d '"'\''\`\$\|&;<>\n\r:' | cut -c 1-30)
|
||||
|
||||
if [ -n "$TARGET_NODE" ] && [ -n "$NEW_ALIAS" ]; then
|
||||
# 强行重写内部路由
|
||||
TEXT="do_rename:${TARGET_NODE}:${NEW_ALIAS}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ================== [v3.0.1 新增: 消除转圈圈与获取消息ID] ==================
|
||||
CB_ID=$(echo "$UPDATE" | jq -r '.callback_query.id // empty')
|
||||
MSG_ID=$(echo "$UPDATE" | jq -r '.callback_query.message.message_id // empty')
|
||||
|
||||
# 告诉 TG 官方“指令已收到”,立刻消除按钮上的加载圈圈
|
||||
if [ -n "$CB_ID" ]; then
|
||||
curl -s --connect-timeout 5 -m 10 -X POST "https://api.telegram.org/bot${TG_TOKEN}/answerCallbackQuery" -d "callback_query_id=${CB_ID}" > /dev/null
|
||||
fi
|
||||
|
||||
# ==========================================
|
||||
# 1. 节点注册通道 (处理 Agent 发来的注册暗号)
|
||||
# 格式: #REGISTER#|<NodeName>|<IP>|<Port>
|
||||
# 1. 节点注册通道 (V3.1.3 大区拓扑升级版)
|
||||
# ==========================================
|
||||
if [[ "$TEXT" == *"#REGISTER#"* ]]; then
|
||||
# 提取包含暗号的那一行,并剔除可能误复制的反引号和空格
|
||||
REG_LINE=$(echo "$TEXT" | grep "#REGISTER#" | head -n 1 | tr -d '\` ')
|
||||
IFS='|' read -r MAGIC NODE_NAME AGENT_IP AGENT_PORT <<< "$REG_LINE"
|
||||
|
||||
# UPSERT 逻辑
|
||||
db_exec "INSERT INTO nodes (chat_id, node_name, agent_ip, agent_port, last_seen) VALUES ('$CHAT_ID', '$NODE_NAME', '$AGENT_IP', '$AGENT_PORT', CURRENT_TIMESTAMP) ON CONFLICT(chat_id, node_name) DO UPDATE SET agent_ip='$AGENT_IP', agent_port='$AGENT_PORT', last_seen=CURRENT_TIMESTAMP;"
|
||||
send_msg "$CHAT_ID" "✅ 司令部已确认!节点接入成功: \`$NODE_NAME\` ($AGENT_IP:$AGENT_PORT)"
|
||||
# V3.6.0 兼容性拆包: 支持 7字段(OTA)、6字段(双轨)、5字段(单轨)、4字段(远古)
|
||||
FIELD_COUNT=$(echo "$REG_LINE" | awk -F'|' '{print NF}')
|
||||
if [ "$FIELD_COUNT" -ge 7 ]; then
|
||||
IFS='|' read -r MAGIC RAW_REGION RAW_NODE RAW_IP RAW_PORT RAW_ALIAS RAW_OTA <<< "$REG_LINE"
|
||||
elif [ "$FIELD_COUNT" -eq 6 ]; then
|
||||
IFS='|' read -r MAGIC RAW_REGION RAW_NODE RAW_IP RAW_PORT RAW_ALIAS <<< "$REG_LINE"
|
||||
RAW_OTA="false"
|
||||
elif [ "$FIELD_COUNT" -eq 5 ]; then
|
||||
IFS='|' read -r MAGIC RAW_REGION RAW_NODE RAW_IP RAW_PORT <<< "$REG_LINE"
|
||||
RAW_ALIAS="$RAW_NODE"
|
||||
RAW_OTA="false"
|
||||
else
|
||||
IFS='|' read -r MAGIC RAW_NODE RAW_IP RAW_PORT <<< "$REG_LINE"
|
||||
RAW_REGION="UNKNOWN"
|
||||
RAW_ALIAS="$RAW_NODE"
|
||||
RAW_OTA="false"
|
||||
fi
|
||||
|
||||
# 🛡️ 强制字符白名单过滤:保留历史特征不变
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
AGENT_REGION=$(echo "$RAW_REGION" | tr -cd 'a-zA-Z0-9' | cut -c 1-10)
|
||||
NODE_NAME=$(echo "$RAW_NODE" | tr -cd 'a-zA-Z0-9_.-' | cut -c 1-30)
|
||||
AGENT_IP=$(echo "$RAW_IP" | tr -cd 'a-zA-Z0-9.:\[\]-' | cut -c 1-50)
|
||||
AGENT_PORT=$(echo "$RAW_PORT" | tr -cd '0-9' | cut -c 1-5)
|
||||
NODE_ALIAS=$(echo "$RAW_ALIAS" | tr -d '"'\''\`\$\|&;<>\n\r' | cut -c 1-30)
|
||||
[ -z "$NODE_ALIAS" ] && NODE_ALIAS="$NODE_NAME"
|
||||
AGENT_OTA=$(echo "$RAW_OTA" | tr -cd 'a-z')
|
||||
[ -z "$AGENT_OTA" ] && AGENT_OTA="false"
|
||||
|
||||
if [[ "$AGENT_IP" =~ ^127\.|^10\.|^192\.168\.|^172\.(1[6-9]|2[0-9]|3[0-1])\.|^::1$|^localhost$ ]]; then
|
||||
send_msg "$CHAT_ID" "⛔ **安全拦截**:禁止注册内网或回环 IP,防止 SSRF 攻击渗透。"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ -z "$NODE_NAME" ] || [ -z "$AGENT_IP" ] || [ -z "$AGENT_PORT" ] || [ -z "$CHAT_ID" ]; then
|
||||
send_msg "$CHAT_ID" "⛔ **安全拦截**:检测到非法注册载荷,请求已拒绝。"
|
||||
continue
|
||||
fi
|
||||
|
||||
# [核心] 入库时追加 node_alias 与 enable_ota 字段
|
||||
db_exec "INSERT INTO nodes (chat_id, node_name, agent_ip, agent_port, last_seen, region, node_alias, enable_ota) VALUES ('$CHAT_ID', '$NODE_NAME', '$AGENT_IP', '$AGENT_PORT', CURRENT_TIMESTAMP, '$AGENT_REGION', '$NODE_ALIAS', '$AGENT_OTA') ON CONFLICT(chat_id, node_name) DO UPDATE SET agent_ip='$AGENT_IP', agent_port='$AGENT_PORT', last_seen=CURRENT_TIMESTAMP, region='$AGENT_REGION', node_alias='$NODE_ALIAS', enable_ota='$AGENT_OTA';"
|
||||
send_msg "$CHAT_ID" "✅ **司令部确认 (v${MASTER_VERSION})**%0A节点 \`${NODE_ALIAS}\` 档案已录入!"
|
||||
|
||||
# ================== [v3.1.3 丝滑连招: 直接呼出全球大区雷达] ==================
|
||||
REGION_DATA=$(db_exec "SELECT region, COUNT(*) FROM nodes WHERE chat_id='$CHAT_ID' GROUP BY region;")
|
||||
if [ -n "$REGION_DATA" ]; then
|
||||
BTNS="["
|
||||
while IFS='|' read -r REGION_NAME NODE_COUNT; do
|
||||
[ -z "$REGION_NAME" ] && REGION_NAME="UNKNOWN"
|
||||
FLAG=$(get_flag "$REGION_NAME")
|
||||
BTNS="$BTNS[{\"text\":\"$FLAG $REGION_NAME ($NODE_COUNT 台)\",\"callback_data\":\"region:$REGION_NAME\"}],"
|
||||
done <<< "$REGION_DATA"
|
||||
BTNS="${BTNS%,}]"
|
||||
send_ui "$CHAT_ID" "🌍 **全视界战略雷达**\n请选择要检阅的战区:" "$BTNS"
|
||||
fi
|
||||
# ========================================================================
|
||||
|
||||
continue
|
||||
fi
|
||||
|
||||
# ==========================================
|
||||
# 2. 交互菜单与下发通道 (主控逻辑)
|
||||
# 2. 交互菜单与下发通道
|
||||
# ==========================================
|
||||
case "$TEXT" in
|
||||
"/start"|"/menu")
|
||||
BTNS="[[{\"text\":\"🖥️ 我的节点列表\",\"callback_data\":\"list_nodes\"}], [{\"text\":\"🚀 全节点一键维护\",\"callback_data\":\"all_run\"}]]"
|
||||
send_ui "$CHAT_ID" "🛡️ **IP-Sentinel 司令部**\n欢迎回来,长官。请下达指令:" "$BTNS"
|
||||
# [核心: 抓取云端最新 Master 版本 (KV 解析法)]
|
||||
REMOTE_VER=$(curl -s -m 2 "${REPO_RAW_URL}/version.txt" | grep "^MASTER_VERSION=" | cut -d'=' -f2 | tr -d '[:space:]')
|
||||
VER_INFO="当前版本: \`v${MASTER_VERSION}\`"
|
||||
|
||||
BTN_MASTER_OTA=""
|
||||
if [ -n "$REMOTE_VER" ] && [ "$REMOTE_VER" != "$MASTER_VERSION" ]; then
|
||||
VER_INFO="${VER_INFO}\n✨ **发现新版本**: \`v${REMOTE_VER}\` (可执行中枢热重载)"
|
||||
|
||||
# 仅当非官方网关 且 开启了中枢 OTA 权限时,才渲染升级按钮
|
||||
if [ "$IS_OFFICIAL_GATEWAY" != "true" ] && [ "${ENABLE_MASTER_OTA:-false}" == "true" ]; then
|
||||
BTN_MASTER_OTA="[{\"text\":\"🆙 升级司令部至 v${REMOTE_VER}\",\"callback_data\":\"master_ota_confirm\"}],"
|
||||
fi
|
||||
fi
|
||||
|
||||
NODE_COUNT=$(db_exec "SELECT COUNT(*) FROM nodes WHERE chat_id='$CHAT_ID';")
|
||||
[ -z "$NODE_COUNT" ] && NODE_COUNT=0
|
||||
|
||||
# L0 扁平化重构:将司令部升级按钮动态置于最顶层
|
||||
if [ "$IS_OFFICIAL_GATEWAY" != "true" ]; then
|
||||
BTNS="[${BTN_MASTER_OTA}[{\"text\":\"🌍 进入全球战区雷达 (管理节点)\",\"callback_data\":\"list_nodes\"}], [{\"text\":\"🚀 全军总攻\",\"callback_data\":\"all_run\"}, {\"text\":\"📊 全军简报\",\"callback_data\":\"all_reports\"}], [{\"text\":\"☢️ 全舰队 OTA 热重载\",\"callback_data\":\"all_ota_confirm\"}]]"
|
||||
else
|
||||
BTNS="[[{\"text\":\"🌍 进入全球战区雷达 (管理节点)\",\"callback_data\":\"list_nodes\"}], [{\"text\":\"🚀 全军总攻\",\"callback_data\":\"all_run\"}, {\"text\":\"📊 全军简报\",\"callback_data\":\"all_reports\"}]]"
|
||||
fi
|
||||
TEXT_MSG="🛡️ **IP-Sentinel 司令部**\n${VER_INFO}\n\n📊 舰队状态: 共有 \`${NODE_COUNT}\` 台哨兵在线\n欢迎回来,长官。请下达战略指令:"
|
||||
send_ui "$CHAT_ID" "$TEXT_MSG" "$BTNS"
|
||||
;;
|
||||
|
||||
"all_ota_confirm")
|
||||
CONFIRM_BTNS="[[{\"text\":\"🚨 我已了解风险,下发核按钮指令!\",\"callback_data\":\"all_ota_execute\"}], [{\"text\":\"取消操作\",\"callback_data\":\"/start\"}]]"
|
||||
WARNING_MSG="☢️ **【最高指令:全舰队 OTA 升级】**\n\n此操作将向您名下**所有开启 OTA 权限的节点**下发重组指令,强制从云端拉取最新代码并进行热重载。\n\n⚠️ **核按钮风险提示**:\n1. 升级过程中守护进程会短暂重启,节点可能出现临时离线。\n2. 若遇 GitHub 源屏蔽或网络极度恶劣,少数节点可能需要手动干预。\n\n**是否确定挂载并执行 OTA 指令?**"
|
||||
send_ui "$CHAT_ID" "$WARNING_MSG" "$CONFIRM_BTNS"
|
||||
;;
|
||||
|
||||
"all_ota_execute")
|
||||
NODE_DATA=$(db_exec "SELECT node_name, agent_ip, agent_port FROM nodes WHERE chat_id='$CHAT_ID' AND enable_ota='true';")
|
||||
if [ -z "$NODE_DATA" ]; then
|
||||
send_msg "$CHAT_ID" "⚠️ 您名下暂无开启 OTA 权限的在线节点。"
|
||||
else
|
||||
send_msg "$CHAT_ID" "📢 **司令部指令下达:正在唤醒全舰队执行 OTA 升级...**%0A*(节点升级成功后会主动发回新的入库确认,请注意查收)*"
|
||||
echo "$NODE_DATA" | while IFS='|' read -r NNAME AIP APORT; do
|
||||
TARGET_URL=$(generate_signed_url "$AIP" "$APORT" "/trigger_ota")
|
||||
# [灾难救援通道] 仅针对 OTA 允许一次明文回退,抢救缺失证书的老节点
|
||||
{ curl -k -s -m 5 "$TARGET_URL" || curl -s -m 5 "${TARGET_URL/https:\/\//http:\/\/}"; } > /dev/null &
|
||||
sleep 0.3 # 严格流量削峰
|
||||
done
|
||||
fi
|
||||
;;
|
||||
|
||||
"master_ota_confirm")
|
||||
CONFIRM_BTNS="[[{\"text\":\"🚨 确认重构司令部\",\"callback_data\":\"master_ota_execute\"}], [{\"text\":\"取消操作\",\"callback_data\":\"/start\"}]]"
|
||||
WARNING_MSG="☢️ **【最高指令:中枢金蝉脱壳】**\n\n此操作将拉取最新源码并强行覆盖司令部核心进程。\n\n⚠️ **风险提示**:\n升级期间司令部将短暂失联(约3-5秒)。完成后会自动发送捷报。\n\n**是否确定执行司令部自我升级?**"
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
edit_ui "$CHAT_ID" "$MSG_ID" "$WARNING_MSG" "$CONFIRM_BTNS"
|
||||
else
|
||||
send_ui "$CHAT_ID" "$WARNING_MSG" "$CONFIRM_BTNS"
|
||||
fi
|
||||
;;
|
||||
|
||||
"master_ota_execute")
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
edit_msg "$CHAT_ID" "$MSG_ID" "⏳ 正在下载重构图纸,司令部即将进入静默重启..."
|
||||
else
|
||||
send_msg "$CHAT_ID" "⏳ 正在下载重构图纸,司令部即将进入静默重启..."
|
||||
fi
|
||||
|
||||
# 下载最新的 master install 脚本作为幽灵进程
|
||||
curl -fsSL "${REPO_RAW_URL}/master/install_master.sh" -o "/tmp/install_master.sh"
|
||||
|
||||
# [v3.6.3 修复] 🚀 OTA 防砖机制:严格校验脚本完整性
|
||||
if ! bash -n "/tmp/install_master.sh" >/dev/null 2>&1; then
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
edit_msg "$CHAT_ID" "$MSG_ID" "❌ OTA 传输受损:脚本下载不完整,已触发防砖熔断,升级取消!"
|
||||
else
|
||||
send_msg "$CHAT_ID" "❌ OTA 传输受损:脚本下载不完整,已触发防砖熔断,升级取消!"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
chmod +x "/tmp/install_master.sh"
|
||||
|
||||
# 抛出幽灵进程进行脱壳升级,传递静默变量与回执 ID
|
||||
# [修复] 必须显式将环境变量注入到 bash -c 的指令串中,防止被 systemd-run 沙盒隔离丢弃
|
||||
if command -v systemd-run >/dev/null 2>&1; then
|
||||
systemd-run --quiet --no-block /bin/bash -c "export SILENT_MASTER_OTA='true'; export OTA_CHAT_ID='$CHAT_ID'; bash /tmp/install_master.sh"
|
||||
else
|
||||
export SILENT_MASTER_OTA="true"
|
||||
export OTA_CHAT_ID="$CHAT_ID"
|
||||
nohup bash /tmp/install_master.sh >/dev/null 2>&1 & disown
|
||||
fi
|
||||
|
||||
# 当前旧进程休眠并等待被幽灵进程处决
|
||||
sleep 10
|
||||
;;
|
||||
|
||||
"all_reports")
|
||||
NODE_DATA=$(db_exec "SELECT node_name, agent_ip, agent_port FROM nodes WHERE chat_id='$CHAT_ID';")
|
||||
if [ -z "$NODE_DATA" ]; then
|
||||
send_msg "$CHAT_ID" "⚠️ 您名下暂无在线节点。"
|
||||
else
|
||||
# [文案优化] 提前告知指挥官需要排队等待
|
||||
send_msg "$CHAT_ID" "📢 **司令部指令下达:正在召唤所有哨兵回传简报...**%0A*(为防止触发 TG 官方限流,简报将排队依次送达,请耐心等待)*"
|
||||
echo "$NODE_DATA" | while IFS='|' read -r NNAME AIP APORT; do
|
||||
TARGET_URL=$(generate_signed_url "$AIP" "$APORT" "/trigger_report")
|
||||
curl -k -s -m 5 "$TARGET_URL" > /dev/null &
|
||||
# [致命修复] 强行休眠 2 秒!错开 TG 官方 1条/秒 的发信红线
|
||||
sleep 2
|
||||
done
|
||||
fi
|
||||
;;
|
||||
|
||||
# ================== [补充缺失的全节点一键维护功能] ==================
|
||||
"all_run")
|
||||
NODE_DATA=$(db_exec "SELECT node_name, agent_ip, agent_port FROM nodes WHERE chat_id='$CHAT_ID';")
|
||||
if [ -z "$NODE_DATA" ]; then
|
||||
send_msg "$CHAT_ID" "⚠️ 您名下暂无在线节点。"
|
||||
else
|
||||
send_msg "$CHAT_ID" "📢 **司令部指令下达:正在唤醒所有哨兵执行系统维护...**"
|
||||
echo "$NODE_DATA" | while IFS='|' read -r NNAME AIP APORT; do
|
||||
TARGET_URL=$(generate_signed_url "$AIP" "$APORT" "/trigger_run")
|
||||
curl -k -s -m 5 "$TARGET_URL" > /dev/null &
|
||||
sleep 0.2 # [新增] 流量削峰:防止瞬间 fork 导致句柄耗尽
|
||||
done
|
||||
fi
|
||||
;;
|
||||
# ====================================================================
|
||||
|
||||
# ------------------- 🚨 请将下面这段代码插入在这里 -------------------
|
||||
|
||||
# ================== [v4.0.0 新增: 文本指令直接控制通道] ==================
|
||||
"/quality"|"/quality@"*)
|
||||
TARGET_NODE=$(echo "$TEXT" | awk '{print $2}')
|
||||
if [ -z "$TARGET_NODE" ]; then
|
||||
send_msg "$CHAT_ID" "⚠️ 请指定目标节点。例如: \`/quality HK-1\`%0A或通过雷达面板进行选择操作。"
|
||||
else
|
||||
TARGET_NODE=$(echo "$TARGET_NODE" | tr -cd 'a-zA-Z0-9_.-')
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
|
||||
# [加密通讯逻辑]
|
||||
AGENT_INFO=$(db_exec "SELECT agent_ip, agent_port FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
AGENT_IP=$(echo "$AGENT_INFO" | cut -d'|' -f1)
|
||||
AGENT_PORT=$(echo "$AGENT_INFO" | cut -d'|' -f2)
|
||||
|
||||
if [ -n "$AGENT_IP" ] && [ -n "$AGENT_PORT" ]; then
|
||||
send_msg "$CHAT_ID" "⏳ 正在向 \`$TARGET_NODE\` ($AGENT_IP) 下发 [quality] 指令,请稍候..."
|
||||
|
||||
# 动态 HMAC 签名防篡改
|
||||
TARGET_URL=$(generate_signed_url "$AGENT_IP" "$AGENT_PORT" "/trigger_quality")
|
||||
RESPONSE=$(curl -k -s -m 5 "$TARGET_URL" || echo "FAILED")
|
||||
if [ "$RESPONSE" == "FAILED" ] || [ -z "$RESPONSE" ]; then
|
||||
TARGET_URL_HTTP="${TARGET_URL/https:\/\//http:\/\/}"
|
||||
RESPONSE=$(curl -s -m 5 "$TARGET_URL_HTTP" || echo "FAILED")
|
||||
fi
|
||||
|
||||
# 结果判定
|
||||
if [ "$RESPONSE" == "FAILED" ]; then
|
||||
send_msg "$CHAT_ID" "❌ 指令下发超时或失败!请检查节点公网 IP 或防火墙端口 ($AGENT_PORT) 是否放行。"
|
||||
elif [[ "$RESPONSE" == *"403"* ]]; then
|
||||
send_msg "$CHAT_ID" "⚠️ **拒绝执行**:该节点未在本地开启此模块,请检查安装时的配置!"
|
||||
else
|
||||
send_msg "$CHAT_ID" "✅ 节点 \`$TARGET_NODE\` 回应: 🔍 深海声呐已投放!请等待异步战报回传。"
|
||||
fi
|
||||
else
|
||||
send_msg "$CHAT_ID" "❌ 数据库中未找到该节点的通讯地址。"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
|
||||
"/trend"|"/trend@"*)
|
||||
TARGET_NODE=$(echo "$TEXT" | awk '{print $2}')
|
||||
if [ -z "$TARGET_NODE" ]; then
|
||||
send_msg "$CHAT_ID" "⚠️ 请指定目标节点。例如: \`/trend HK-1\`%0A或通过雷达面板进行选择操作。"
|
||||
else
|
||||
TARGET_NODE=$(echo "$TARGET_NODE" | tr -cd 'a-zA-Z0-9_.-')
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
|
||||
TREND_DATA=$(db_exec "SELECT datetime(check_time, 'localtime'), scam_score, goog_status, nf_status, gpt_status FROM ip_trend_log WHERE node_name='$TARGET_NODE' ORDER BY check_time DESC LIMIT 15;")
|
||||
|
||||
if [ -z "$TREND_DATA" ]; then
|
||||
send_msg "$CHAT_ID" "⚠️ 节点 \`$TARGET_NODE\` 暂无历史体检档案。请先执行 /quality 投放声呐进行探测。"
|
||||
else
|
||||
TARGET_ALIAS=$(db_exec "SELECT IFNULL(node_alias, node_name) FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
[ -z "$TARGET_ALIAS" ] && TARGET_ALIAS="$TARGET_NODE"
|
||||
|
||||
TEXT_RES="📈 *[${TARGET_ALIAS}] 历史态势感知 (近15次)*\n\n"
|
||||
TEXT_RES+="时间(本地) | 风险 | 谷歌 | NF | GPT\n"
|
||||
TEXT_RES+="-----------------------------------------\n"
|
||||
|
||||
while IFS='|' read -r c_time score goog nf gpt; do
|
||||
[ -z "$score" ] && score="0"
|
||||
[ -z "$goog" ] && goog="未知"
|
||||
[ -z "$nf" ] && nf="未知"
|
||||
[ -z "$gpt" ] && gpt="未知"
|
||||
|
||||
short_time=$(echo "$c_time" | cut -c 6-16)
|
||||
|
||||
if [ "$score" -le 20 ]; then SCORE_EMJ="🟢"
|
||||
elif [ "$score" -le 60 ]; then SCORE_EMJ="🟡"
|
||||
else SCORE_EMJ="🔴"
|
||||
fi
|
||||
|
||||
TEXT_RES+="\`${short_time}\` | ${SCORE_EMJ}\`${score}\` | \`${goog}\` | \`${nf}\` | \`${gpt}\`\n"
|
||||
done <<< "$TREND_DATA"
|
||||
TEXT_RES+="\n_💡 提示:🔴风险分 >60 极易触发网页验证码拦截;谷歌显示 CN 即为高危送中。_"
|
||||
|
||||
# [v4.0.3 体验升级] 注入交互式控制台按钮
|
||||
BTNS="[[{\"text\":\"⚙️ 调出该节点控制台\",\"callback_data\":\"manage:$TARGET_NODE\"}]]"
|
||||
send_ui "$CHAT_ID" "$TEXT_RES" "$BTNS"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
# ------------------- 🚨 插入代码到此结束 -------------------
|
||||
|
||||
"list_nodes")
|
||||
# 从 SQLite 查询属于该 CHAT_ID 的节点
|
||||
NODE_LIST=$(db_exec "SELECT node_name FROM nodes WHERE chat_id='$CHAT_ID';")
|
||||
if [ -z "$NODE_LIST" ]; then
|
||||
# 【V3.1.3】一级菜单:大区聚合并列出数量
|
||||
REGION_DATA=$(db_exec "SELECT region, COUNT(*) FROM nodes WHERE chat_id='$CHAT_ID' GROUP BY region;")
|
||||
if [ -z "$REGION_DATA" ]; then
|
||||
send_msg "$CHAT_ID" "⚠️ 您名下暂无在线节点,请先在边缘机执行部署。"
|
||||
else
|
||||
BTNS="["
|
||||
for N in $NODE_LIST; do
|
||||
BTNS="$BTNS[{\"text\":\"🖥️ $N\",\"callback_data\":\"manage:$N\"}],"
|
||||
done
|
||||
BTNS="${BTNS%,}]"
|
||||
send_ui "$CHAT_ID" "🔍 您名下的活跃节点:" "$BTNS"
|
||||
while IFS='|' read -r REGION_NAME NODE_COUNT; do
|
||||
[ -z "$REGION_NAME" ] && REGION_NAME="UNKNOWN"
|
||||
FLAG=$(get_flag "$REGION_NAME")
|
||||
BTNS="$BTNS[{\"text\":\"$FLAG $REGION_NAME ($NODE_COUNT 台)\",\"callback_data\":\"region:$REGION_NAME\"}],"
|
||||
done <<< "$REGION_DATA"
|
||||
# L1 追加返回中枢逃生舱
|
||||
BTNS="$BTNS[{\"text\":\"🏠 回到司令部\",\"callback_data\":\"/start\"}]]"
|
||||
send_ui "$CHAT_ID" "🌍 **全视界战略雷达**\n已为您聚合当前舰队的部署大区,请选择要检阅的战区:" "$BTNS"
|
||||
fi
|
||||
;;
|
||||
|
||||
region:*)
|
||||
# 【V3.1.3】二级菜单:目标大区下的节点双列排版
|
||||
TARGET_REGION=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9')
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
|
||||
# [v3.5.2] 提取物理主键和展示别名
|
||||
NODE_LIST=$(db_exec "SELECT node_name, IFNULL(node_alias, node_name) FROM nodes WHERE chat_id='$CHAT_ID' AND region='$TARGET_REGION';")
|
||||
if [ -z "$NODE_LIST" ]; then
|
||||
send_msg "$CHAT_ID" "⚠️ 该战区下暂无可用节点。"
|
||||
else
|
||||
BTNS="["
|
||||
COL=0
|
||||
ROW_STR="["
|
||||
while IFS='|' read -r N_NAME N_ALIAS; do
|
||||
[ -z "$N_NAME" ] && continue
|
||||
ROW_STR="$ROW_STR{\"text\":\"🖥️ $N_ALIAS\",\"callback_data\":\"manage:$N_NAME\"},"
|
||||
COL=$((COL+1))
|
||||
if [ $COL -eq 2 ]; then
|
||||
ROW_STR="${ROW_STR%,}]"
|
||||
BTNS="$BTNS$ROW_STR,"
|
||||
COL=0
|
||||
ROW_STR="["
|
||||
fi
|
||||
done <<< "$NODE_LIST"
|
||||
# 如果是奇数,补齐最后的尾巴
|
||||
if [ $COL -eq 1 ]; then
|
||||
ROW_STR="${ROW_STR%,}]"
|
||||
BTNS="$BTNS$ROW_STR,"
|
||||
fi
|
||||
# L2 追加双重逃生舱
|
||||
BTNS="$BTNS[{\"text\":\"⬅️ 返回战区地图\",\"callback_data\":\"list_nodes\"}, {\"text\":\"🏠 回到司令部\",\"callback_data\":\"/start\"}]]"
|
||||
send_ui "$CHAT_ID" "📍 **[$TARGET_REGION] 战区哨兵矩阵**\n请锁定要执行战术动作的具体目标:" "$BTNS"
|
||||
fi
|
||||
;;
|
||||
|
||||
manage:*)
|
||||
TARGET_NODE=${TEXT#*:}
|
||||
# 【升级点 1】重构战术面板排版,新增 [剔除失联节点] 按钮,采用 3 行优雅布局
|
||||
BTNS="[[{\"text\":\"▶️ 执行深度伪装\",\"callback_data\":\"run:$TARGET_NODE\"}, {\"text\":\"📜 查看实时日志\",\"callback_data\":\"log:$TARGET_NODE\"}], [{\"text\":\"📊 索要统计战报\",\"callback_data\":\"report:$TARGET_NODE\"}, {\"text\":\"🗑️ 剔除失联节点\",\"callback_data\":\"del:$TARGET_NODE\"}], [{\"text\":\"⬅️ 返回主列表\",\"callback_data\":\"list_nodes\"}]]"
|
||||
send_ui "$CHAT_ID" "⚙️ **目标锁定**: \`$TARGET_NODE\`\n请选择战术动作:" "$BTNS"
|
||||
;;
|
||||
|
||||
del:*)
|
||||
# 【升级点 2】执行数据库硬删除,并自动刷新节点列表
|
||||
TARGET_NODE=${TEXT#*:}
|
||||
db_exec "DELETE FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE';"
|
||||
send_msg "$CHAT_ID" "🗑️ 节点 \`$TARGET_NODE\` 的档案已从司令部彻底销毁!"
|
||||
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
|
||||
TARGET_ALIAS=$(db_exec "SELECT IFNULL(node_alias, node_name) FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
[ -z "$TARGET_ALIAS" ] && TARGET_ALIAS="$TARGET_NODE"
|
||||
|
||||
# 删除后自动重新渲染节点列表展示
|
||||
NODE_LIST=$(db_exec "SELECT node_name FROM nodes WHERE chat_id='$CHAT_ID';")
|
||||
if [ -z "$NODE_LIST" ]; then
|
||||
send_msg "$CHAT_ID" "⚠️ 当前司令部已无任何节点挂载。"
|
||||
# 抓取节点全景元数据
|
||||
TOGGLE_INFO=$(db_exec "SELECT enable_google, enable_trust, enable_ota, agent_ip, IFNULL(last_seen, '未知') FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
ST_GOOGLE=$(echo "$TOGGLE_INFO" | cut -d'|' -f1)
|
||||
ST_TRUST=$(echo "$TOGGLE_INFO" | cut -d'|' -f2)
|
||||
ST_OTA=$(echo "$TOGGLE_INFO" | cut -d'|' -f3)
|
||||
A_IP=$(echo "$TOGGLE_INFO" | cut -d'|' -f4)
|
||||
LAST_SEEN=$(echo "$TOGGLE_INFO" | cut -d'|' -f5)
|
||||
|
||||
# 动态渲染状态文字
|
||||
[ "$ST_GOOGLE" == "true" ] && BTN_G="🟢 Google巡逻: 已开" && ACT_G="false" || { BTN_G="🔴 Google巡逻: 已停"; ACT_G="true"; }
|
||||
[ "$ST_TRUST" == "true" ] && BTN_T="🟢 信用净化: 已开" && ACT_T="false" || { BTN_T="🔴 信用净化: 已停"; ACT_T="true"; }
|
||||
|
||||
# 模块一:即时战术动作 (V4.0.0 引入深海声呐与趋势面板)
|
||||
BTN_ACTION="[{\"text\":\"📍 触发 Google 纠偏\",\"callback_data\":\"google:$TARGET_NODE\"}, {\"text\":\"🛡️ 触发信用净化\",\"callback_data\":\"trust:$TARGET_NODE\"}], [{\"text\":\"🔍 投放深海声呐 (查IP质量)\",\"callback_data\":\"quality:$TARGET_NODE\"}, {\"text\":\"📈 查看 IP 污染趋势图\",\"callback_data\":\"trend:$TARGET_NODE\"}], [{\"text\":\"📜 提取终端实时日志\",\"callback_data\":\"log:$TARGET_NODE\"}, {\"text\":\"📊 生成单机战报\",\"callback_data\":\"report:$TARGET_NODE\"}]"
|
||||
|
||||
# 模块二:养护状态启停
|
||||
BTN_TOGGLE="[{\"text\":\"$BTN_G\",\"callback_data\":\"toggle:google:$TARGET_NODE:$ACT_G\"}, {\"text\":\"$BTN_T\",\"callback_data\":\"toggle:trust:$TARGET_NODE:$ACT_T\"}]"
|
||||
|
||||
# 模块三:深度配置管理 (结合 UI 熔断)
|
||||
if [ "$IS_OFFICIAL_GATEWAY" != "true" ] && [ "$ST_OTA" == "true" ]; then
|
||||
BTN_CONFIG="[{\"text\":\"✏️ 更改终端展示代号\",\"callback_data\":\"rename:$TARGET_NODE\"}, {\"text\":\"🆙 OTA 静默升级\",\"callback_data\":\"ota_confirm:$TARGET_NODE\"}]"
|
||||
else
|
||||
BTNS="["
|
||||
for N in $NODE_LIST; do
|
||||
BTNS="$BTNS[{\"text\":\"🖥️ $N\",\"callback_data\":\"manage:$N\"}],"
|
||||
done
|
||||
BTNS="${BTNS%,}]"
|
||||
send_ui "$CHAT_ID" "🔍 刷新后的节点列表:" "$BTNS"
|
||||
BTN_CONFIG="[{\"text\":\"✏️ 更改终端展示代号\",\"callback_data\":\"rename:$TARGET_NODE\"}]"
|
||||
fi
|
||||
|
||||
# 模块四:危险区与逃生舱
|
||||
BTN_DANGER="[{\"text\":\"🗑️ 从中枢销毁该档案\",\"callback_data\":\"del:$TARGET_NODE\"}, {\"text\":\"⬅️ 返回战区列表\",\"callback_data\":\"list_nodes\"}]"
|
||||
|
||||
# 组合终极矩阵
|
||||
BTNS="[$BTN_ACTION, $BTN_TOGGLE, $BTN_CONFIG, $BTN_DANGER]"
|
||||
|
||||
TEXT_MSG="⚙️ **目标锁定**: \`$TARGET_ALIAS\`\n(底层标识: \`$TARGET_NODE\`)\n🌐 IP 坐标: \`$A_IP\`\n🕒 最后通讯: \`$LAST_SEEN\`\n\n请下达精确控制指令:"
|
||||
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
edit_ui "$CHAT_ID" "$MSG_ID" "$TEXT_MSG" "$BTNS"
|
||||
else
|
||||
send_ui "$CHAT_ID" "$TEXT_MSG" "$BTNS"
|
||||
fi
|
||||
;;
|
||||
|
||||
run:*|report:*|log:*)
|
||||
ACTION_TYPE=$(echo "$TEXT" | cut -d':' -f1)
|
||||
TARGET_NODE=$(echo "$TEXT" | cut -d':' -f2)
|
||||
toggle:*)
|
||||
# [动态启停通信闭环]
|
||||
IFS=':' read -r CMD MOD_NAME TARGET_NODE TARGET_STATE <<< "$TEXT"
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
|
||||
AGENT_INFO=$(db_exec "SELECT agent_ip, agent_port FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
AGENT_IP=$(echo "$AGENT_INFO" | cut -d'|' -f1)
|
||||
AGENT_PORT=$(echo "$AGENT_INFO" | cut -d'|' -f2)
|
||||
|
||||
if [ -n "$AGENT_IP" ] && [ -n "$AGENT_PORT" ]; then
|
||||
TARGET_URL=$(generate_signed_url "$AGENT_IP" "$AGENT_PORT" "/trigger_toggle")
|
||||
TARGET_URL="${TARGET_URL}&mod=${MOD_NAME}&state=${TARGET_STATE}"
|
||||
|
||||
RESPONSE=$(curl -k -s -m 5 "$TARGET_URL" || echo "FAILED")
|
||||
|
||||
if [[ "$RESPONSE" == *"Action Accepted"* ]]; then
|
||||
# 下发成功,更新 DB,原位重绘
|
||||
db_exec "UPDATE nodes SET enable_${MOD_NAME}='$TARGET_STATE' WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE';"
|
||||
|
||||
TOGGLE_INFO=$(db_exec "SELECT enable_google, enable_trust FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
ST_GOOGLE=$(echo "$TOGGLE_INFO" | cut -d'|' -f1)
|
||||
ST_TRUST=$(echo "$TOGGLE_INFO" | cut -d'|' -f2)
|
||||
[ "$ST_GOOGLE" == "true" ] && BTN_G="🔴 停用 Google 纠偏" && ACT_G="false" || { BTN_G="🟢 启用 Google 纠偏"; ACT_G="true"; }
|
||||
[ "$ST_TRUST" == "true" ] && BTN_T="🔴 停用信用净化" && ACT_T="false" || { BTN_T="🟢 启用信用净化"; ACT_T="true"; }
|
||||
|
||||
# 切换后直接复用扁平化 L3 面板的重绘逻辑
|
||||
TOGGLE_INFO=$(db_exec "SELECT enable_google, enable_trust, enable_ota, agent_ip, IFNULL(last_seen, '未知') FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
ST_GOOGLE=$(echo "$TOGGLE_INFO" | cut -d'|' -f1)
|
||||
ST_TRUST=$(echo "$TOGGLE_INFO" | cut -d'|' -f2)
|
||||
ST_OTA=$(echo "$TOGGLE_INFO" | cut -d'|' -f3)
|
||||
A_IP=$(echo "$TOGGLE_INFO" | cut -d'|' -f4)
|
||||
LAST_SEEN=$(echo "$TOGGLE_INFO" | cut -d'|' -f5)
|
||||
|
||||
[ "$ST_GOOGLE" == "true" ] && BTN_G="🟢 Google巡逻: 已开" && ACT_G="false" || { BTN_G="🔴 Google巡逻: 已停"; ACT_G="true"; }
|
||||
[ "$ST_TRUST" == "true" ] && BTN_T="🟢 信用净化: 已开" && ACT_T="false" || { BTN_T="🔴 信用净化: 已停"; ACT_T="true"; }
|
||||
|
||||
# 模块一:即时战术动作 (V4.0.0 引入深海声呐与趋势面板)
|
||||
BTN_ACTION="[{\"text\":\"📍 触发 Google 纠偏\",\"callback_data\":\"google:$TARGET_NODE\"}, {\"text\":\"🛡️ 触发信用净化\",\"callback_data\":\"trust:$TARGET_NODE\"}], [{\"text\":\"🔍 投放深海声呐 (查IP质量)\",\"callback_data\":\"quality:$TARGET_NODE\"}, {\"text\":\"📈 查看 IP 污染趋势图\",\"callback_data\":\"trend:$TARGET_NODE\"}], [{\"text\":\"📜 提取终端实时日志\",\"callback_data\":\"log:$TARGET_NODE\"}, {\"text\":\"📊 生成单机战报\",\"callback_data\":\"report:$TARGET_NODE\"}]"
|
||||
BTN_TOGGLE="[{\"text\":\"$BTN_G\",\"callback_data\":\"toggle:google:$TARGET_NODE:$ACT_G\"}, {\"text\":\"$BTN_T\",\"callback_data\":\"toggle:trust:$TARGET_NODE:$ACT_T\"}]"
|
||||
|
||||
if [ "$IS_OFFICIAL_GATEWAY" != "true" ] && [ "$ST_OTA" == "true" ]; then
|
||||
BTN_CONFIG="[{\"text\":\"✏️ 更改终端展示代号\",\"callback_data\":\"rename:$TARGET_NODE\"}, {\"text\":\"🆙 OTA 静默升级\",\"callback_data\":\"ota_confirm:$TARGET_NODE\"}]"
|
||||
else
|
||||
BTN_CONFIG="[{\"text\":\"✏️ 更改终端展示代号\",\"callback_data\":\"rename:$TARGET_NODE\"}]"
|
||||
fi
|
||||
BTN_DANGER="[{\"text\":\"🗑️ 从中枢销毁该档案\",\"callback_data\":\"del:$TARGET_NODE\"}, {\"text\":\"⬅️ 返回战区列表\",\"callback_data\":\"list_nodes\"}]"
|
||||
|
||||
BTNS="[$BTN_ACTION, $BTN_TOGGLE, $BTN_CONFIG, $BTN_DANGER]"
|
||||
TARGET_ALIAS=$(db_exec "SELECT IFNULL(node_alias, node_name) FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
|
||||
TEXT_MSG="⚙️ **目标锁定**: \`$TARGET_ALIAS\`\n(底层标识: \`$TARGET_NODE\`)\n🌐 IP 坐标: \`$A_IP\`\n🕒 最后通讯: \`$LAST_SEEN\`\n\n✅ **执行成功**: 模块 [$MOD_NAME] 状态已切换为 $TARGET_STATE!"
|
||||
edit_ui "$CHAT_ID" "$MSG_ID" "$TEXT_MSG" "$BTNS"
|
||||
else
|
||||
send_msg "$CHAT_ID" "❌ 指令下发失败,安全策略禁止降级重试。"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
|
||||
del:*)
|
||||
# 🛡️ 提取并强制过滤节点名与 CHAT_ID 防注入
|
||||
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
|
||||
db_exec "DELETE FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE';"
|
||||
send_msg "$CHAT_ID" "🗑️ 节点 \`$TARGET_NODE\` 的档案已从司令部彻底销毁!"
|
||||
|
||||
# 剔除后直接返回上级一级雷达菜单
|
||||
REGION_DATA=$(db_exec "SELECT region, COUNT(*) FROM nodes WHERE chat_id='$CHAT_ID' GROUP BY region;")
|
||||
if [ -z "$REGION_DATA" ]; then
|
||||
send_msg "$CHAT_ID" "⚠️ 当前司令部已无任何节点挂载。"
|
||||
else
|
||||
BTNS="["
|
||||
while IFS='|' read -r REGION_NAME NODE_COUNT; do
|
||||
[ -z "$REGION_NAME" ] && REGION_NAME="UNKNOWN"
|
||||
FLAG=$(get_flag "$REGION_NAME")
|
||||
BTNS="$BTNS[{\"text\":\"$FLAG $REGION_NAME ($NODE_COUNT 台)\",\"callback_data\":\"region:$REGION_NAME\"}],"
|
||||
done <<< "$REGION_DATA"
|
||||
BTNS="${BTNS%,}]"
|
||||
send_ui "$CHAT_ID" "🌍 刷新后的全视界雷达:" "$BTNS"
|
||||
fi
|
||||
;;
|
||||
|
||||
rename:*)
|
||||
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
# [v3.5.2] 发送 ForceReply 引导用户回复
|
||||
curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"chat_id\":\"$CHAT_ID\",\"text\":\"✏️ 请回复本消息以重命名节点:\n\`$TARGET_NODE\`\n(仅限中英文、数字,最长20字符)\",\"parse_mode\":\"Markdown\",\"reply_markup\":{\"force_reply\":true}}" > /dev/null
|
||||
;;
|
||||
|
||||
do_rename:*)
|
||||
# [v3.5.2] 内部重命名路由 (已被第2处的代码拦截并格式化)
|
||||
IFS=':' read -r CMD TARGET_NODE NEW_ALIAS <<< "$TEXT"
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
|
||||
# 从 DB 提取 IP 和 Port
|
||||
AGENT_INFO=$(db_exec "SELECT agent_ip, agent_port FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
AGENT_IP=$(echo "$AGENT_INFO" | cut -d'|' -f1)
|
||||
AGENT_PORT=$(echo "$AGENT_INFO" | cut -d'|' -f2)
|
||||
|
||||
if [ -n "$AGENT_IP" ] && [ -n "$AGENT_PORT" ]; then
|
||||
send_msg "$CHAT_ID" "⏳ 正在向 \`$TARGET_NODE\` ($AGENT_IP) 下发 [$ACTION_TYPE] 指令,请稍候..."
|
||||
send_msg "$CHAT_ID" "⏳ 正在向 \`$TARGET_NODE\` 下发重命名指令,正在建立加密隧道..."
|
||||
|
||||
# 向 Agent 的开放端口发送动态 Webhook 唤醒指令
|
||||
RESPONSE=$(curl -s -m 5 "http://${AGENT_IP}:${AGENT_PORT}/trigger_${ACTION_TYPE}" || echo "FAILED")
|
||||
TARGET_URL=$(generate_signed_url "$AGENT_IP" "$AGENT_PORT" "/trigger_rename")
|
||||
|
||||
# [绝密防线: Base64 编码绕过一切传输限制与 WAF 拦截]
|
||||
ALIAS_B64=$(echo -n "$NEW_ALIAS" | base64 | tr -d '\n' | tr '+/' '-_')
|
||||
TARGET_URL="${TARGET_URL}&b64=${ALIAS_B64}"
|
||||
|
||||
RESPONSE=$(curl -k -s -m 5 "$TARGET_URL" || echo "FAILED")
|
||||
|
||||
if [ "$RESPONSE" == "FAILED" ]; then
|
||||
send_msg "$CHAT_ID" "❌ 指令下发超时或失败!请检查节点公网 IP 或防火墙端口 ($AGENT_PORT) 是否放行。"
|
||||
send_msg "$CHAT_ID" "❌ 指令下发超时!为防范劫持风险,已终止请求。"
|
||||
elif [[ "$RESPONSE" == *"Action Accepted"* ]]; then
|
||||
# [v3.5.2 极致丝滑] 确认 Agent 修改成功后,Master 立即自动同步本地 SQLite 数据库!
|
||||
db_exec "UPDATE nodes SET node_alias='$NEW_ALIAS' WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE';"
|
||||
send_msg "$CHAT_ID" "✅ 通讯成功!节点别名已下发: \`$NEW_ALIAS\`%0A*(司令部档案已自动刷新,雷达面板已同步)*"
|
||||
else
|
||||
if [ "$ACTION_TYPE" == "run" ]; then
|
||||
send_msg "$CHAT_ID" "✅ 节点 \`$TARGET_NODE\` 回应: 指令已接收,伪装程序启动。"
|
||||
fi
|
||||
# 增加输出 RESPONSE 调试信息,排查任何拦截死因
|
||||
send_msg "$CHAT_ID" "⚠️ 节点拒绝了请求,请确保 Agent 已更新至 v3.5.2%0A(回传信息: \`${RESPONSE}\`)"
|
||||
fi
|
||||
else
|
||||
send_msg "$CHAT_ID" "❌ 数据库中未找到该节点的通讯地址。"
|
||||
fi
|
||||
;;
|
||||
|
||||
ota_confirm:*)
|
||||
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
|
||||
# 将取消动作引导回 manage,因为 adv 已经被删除了
|
||||
CONFIRM_BTNS="[[{\"text\":\"🚨 确认执行远程升级\",\"callback_data\":\"ota_execute:$TARGET_NODE\"}], [{\"text\":\"取消\",\"callback_data\":\"manage:$TARGET_NODE\"}]]"
|
||||
send_ui "$CHAT_ID" "☢️ **操作确认**:即将向 \`$TARGET_NODE\` 下发 OTA 热更新指令。\n节点更新完成后会自动发送包含新版本号的注册回执,确定执行?" "$CONFIRM_BTNS"
|
||||
;;
|
||||
|
||||
ota_execute:*)
|
||||
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
|
||||
AGENT_INFO=$(db_exec "SELECT agent_ip, agent_port FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
AGENT_IP=$(echo "$AGENT_INFO" | cut -d'|' -f1)
|
||||
AGENT_PORT=$(echo "$AGENT_INFO" | cut -d'|' -f2)
|
||||
|
||||
if [ -n "$AGENT_IP" ] && [ -n "$AGENT_PORT" ]; then
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
edit_msg "$CHAT_ID" "$MSG_ID" "⏳ 正在向 \`$TARGET_NODE\` 发送 OTA 触发报文..."
|
||||
else
|
||||
send_msg "$CHAT_ID" "⏳ 正在向 \`$TARGET_NODE\` 发送 OTA 触发报文..."
|
||||
fi
|
||||
|
||||
TARGET_URL=$(generate_signed_url "$AGENT_IP" "$AGENT_PORT" "/trigger_ota")
|
||||
RESPONSE=$(curl -k -s -m 5 "$TARGET_URL" || echo "FAILED")
|
||||
|
||||
# [灾难救援通道] 仅针对 OTA 开放一次性明文降级,用于抢救缺失 openssl 证书的老节点
|
||||
if [ "$RESPONSE" == "FAILED" ]; then
|
||||
TARGET_URL_HTTP="${TARGET_URL/https:\/\//http:\/\/}"
|
||||
RESPONSE=$(curl -s -m 5 "$TARGET_URL_HTTP" || echo "FAILED")
|
||||
|
||||
if [[ "$RESPONSE" == *"Action Accepted"* ]]; then
|
||||
TEXT_RES="⚠️ **明文救援成功**:该节点因缺失证书处于 HTTP 裸奔状态!已强行下发 OTA 抢救指令,请等待其重构 TLS 装甲。"
|
||||
else
|
||||
TEXT_RES="❌ OTA 指令下发彻底失败!节点已失联或网络阻断。"
|
||||
fi
|
||||
elif [[ "$RESPONSE" == *"403"* ]]; then
|
||||
TEXT_RES="⚠️ **节点拒绝执行**:该节点本地未开启 OTA 权限或运行在官方网关下!"
|
||||
else
|
||||
TEXT_RES="✅ OTA (TLS加密) 触发成功!节点正在后台执行拉取重构..."
|
||||
fi
|
||||
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
edit_msg "$CHAT_ID" "$MSG_ID" "$TEXT_RES"
|
||||
else
|
||||
send_msg "$CHAT_ID" "$TEXT_RES"
|
||||
fi
|
||||
else
|
||||
send_msg "$CHAT_ID" "❌ 数据库中未找到该节点的通讯地址。"
|
||||
fi
|
||||
;;
|
||||
|
||||
# 【核心升级 v4.0.0】增加拦截规则,支持 quality 前缀
|
||||
google:*|trust:*|run:*|report:*|log:*|quality:*)
|
||||
# 🛡️ 提取并强制过滤动作参数、节点名与 CHAT_ID
|
||||
ACTION_TYPE=$(echo "$TEXT" | cut -d':' -f1)
|
||||
TARGET_NODE=$(echo "$TEXT" | cut -d':' -f2 | tr -cd 'a-zA-Z0-9_.-')
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
|
||||
AGENT_INFO=$(db_exec "SELECT agent_ip, agent_port FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
AGENT_IP=$(echo "$AGENT_INFO" | cut -d'|' -f1)
|
||||
AGENT_PORT=$(echo "$AGENT_INFO" | cut -d'|' -f2)
|
||||
|
||||
if [ -n "$AGENT_IP" ] && [ -n "$AGENT_PORT" ]; then
|
||||
# [v3.0.2 防刷屏] 原位刷新菜单为等待状态
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
edit_msg "$CHAT_ID" "$MSG_ID" "⏳ 正在向 \`$TARGET_NODE\` ($AGENT_IP) 下发 [$ACTION_TYPE] 指令,请稍候..."
|
||||
else
|
||||
send_msg "$CHAT_ID" "⏳ 正在向 \`$TARGET_NODE\` ($AGENT_IP) 下发 [$ACTION_TYPE] 指令,请稍候..."
|
||||
fi
|
||||
|
||||
# 🛡️ [v3.0.4] 动态签名生成与触发 (防重放与防篡改)
|
||||
TARGET_URL=$(generate_signed_url "$AGENT_IP" "$AGENT_PORT" "/trigger_${ACTION_TYPE}")
|
||||
RESPONSE=$(curl -k -s -m 5 "$TARGET_URL" || echo "FAILED")
|
||||
|
||||
# 结果判定
|
||||
if [ "$RESPONSE" == "FAILED" ]; then
|
||||
TEXT_RES="❌ 指令下发超时或失败!为保护链路安全,已终止通信 (严禁降级为 HTTP)。"
|
||||
elif [[ "$RESPONSE" == *"403"* ]]; then
|
||||
TEXT_RES="⚠️ **拒绝执行**:该节点未在本地开启此模块,请检查安装时的配置!"
|
||||
else
|
||||
if [ "$ACTION_TYPE" == "google" ] || [ "$ACTION_TYPE" == "run" ]; then
|
||||
TEXT_RES="✅ 节点 \`$TARGET_NODE\` 回应: 📍 Google 纠偏程序启动。"
|
||||
elif [ "$ACTION_TYPE" == "trust" ]; then
|
||||
TEXT_RES="✅ 节点 \`$TARGET_NODE\` 回应: 🛡️ IP 信用净化程序启动。"
|
||||
elif [ "$ACTION_TYPE" == "quality" ]; then
|
||||
TEXT_RES="✅ 节点 \`$TARGET_NODE\` 回应: 🔍 深海声呐已投放!请等待异步战报回传。"
|
||||
elif [ "$ACTION_TYPE" == "log" ]; then
|
||||
TEXT_RES="✅ 节点 \`$TARGET_NODE\` 正在抓取日志..."
|
||||
else
|
||||
TEXT_RES="✅ 节点 \`$TARGET_NODE\` 接收指令: $ACTION_TYPE"
|
||||
fi
|
||||
fi
|
||||
|
||||
# [v3.0.1 防刷屏] 将等待状态刷新为最终结果
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
edit_msg "$CHAT_ID" "$MSG_ID" "$TEXT_RES"
|
||||
else
|
||||
send_msg "$CHAT_ID" "$TEXT_RES"
|
||||
fi
|
||||
else
|
||||
send_msg "$CHAT_ID" "❌ 数据库中未找到该节点的通讯地址。"
|
||||
fi
|
||||
;;
|
||||
|
||||
|
||||
trend:*)
|
||||
# [v4.0.2 优化: 扩容 15 次追踪并引入 GOOG/GPT 状态]
|
||||
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
|
||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||
|
||||
TREND_DATA=$(db_exec "SELECT datetime(check_time, 'localtime'), scam_score, goog_status, nf_status, gpt_status FROM ip_trend_log WHERE node_name='$TARGET_NODE' ORDER BY check_time DESC LIMIT 15;")
|
||||
|
||||
if [ -z "$TREND_DATA" ]; then
|
||||
TEXT_RES="⚠️ 节点 \`$TARGET_NODE\` 暂无历史体检档案。请先执行 [🔍 投放深海声呐] 进行探测。"
|
||||
else
|
||||
TARGET_ALIAS=$(db_exec "SELECT IFNULL(node_alias, node_name) FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
|
||||
[ -z "$TARGET_ALIAS" ] && TARGET_ALIAS="$TARGET_NODE"
|
||||
|
||||
TEXT_RES="📈 *[${TARGET_ALIAS}] 历史态势感知 (近15次)*\n\n"
|
||||
TEXT_RES+="时间(本地) | 风险 | 谷歌 | NF | GPT\n"
|
||||
TEXT_RES+="-----------------------------------------\n"
|
||||
|
||||
while IFS='|' read -r c_time score goog nf gpt; do
|
||||
[ -z "$score" ] && score="0"
|
||||
[ -z "$goog" ] && goog="未知"
|
||||
[ -z "$nf" ] && nf="未知"
|
||||
[ -z "$gpt" ] && gpt="未知"
|
||||
|
||||
# 时间做极简切割 (截取 04-24 20:52) 节省横向空间
|
||||
short_time=$(echo "$c_time" | cut -c 6-16)
|
||||
|
||||
if [ "$score" -le 20 ]; then SCORE_EMJ="🟢"
|
||||
elif [ "$score" -le 60 ]; then SCORE_EMJ="🟡"
|
||||
else SCORE_EMJ="🔴"
|
||||
fi
|
||||
|
||||
# 拼接紧凑排版
|
||||
TEXT_RES+="\`${short_time}\` | ${SCORE_EMJ}\`${score}\` | \`${goog}\` | \`${nf}\` | \`${gpt}\`\n"
|
||||
done <<< "$TREND_DATA"
|
||||
TEXT_RES+="\n_💡 提示:🔴风险分 >60 极易触发网页验证码拦截;谷歌显示 CN 即为高危送中。_"
|
||||
fi
|
||||
|
||||
# [v4.0.3 体验升级] 注入交互式控制台按钮,并调用原生 UI 重绘函数
|
||||
BTNS="[[{\"text\":\"⚙️ 调出该节点控制台\",\"callback_data\":\"manage:$TARGET_NODE\"}]]"
|
||||
|
||||
if [ -n "$MSG_ID" ]; then
|
||||
edit_ui "$CHAT_ID" "$MSG_ID" "$TEXT_RES" "$BTNS"
|
||||
else
|
||||
send_ui "$CHAT_ID" "$TEXT_RES" "$BTNS"
|
||||
fi
|
||||
;;
|
||||
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
67
master/uninstall_master.sh
Normal file
67
master/uninstall_master.sh
Normal file
@@ -0,0 +1,67 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# 脚本名称: uninstall_master.sh (IP-Sentinel Master 一键卸载脚本 - 动态锚点版)
|
||||
# 核心功能: 终止调度进程、清理看门狗定时任务、抹除数据库与配置
|
||||
# ==========================================================
|
||||
|
||||
# ==========================================================
|
||||
# 🛑 核心权限防线: 检查是否以 root 权限运行
|
||||
# ==========================================================
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo -e "\033[31m❌ 权限被拒绝: 卸载 IP-Sentinel 需要最高系统权限。\033[0m"
|
||||
echo -e "💡 请切换到 root 用户 (执行 su root 或 sudo -i) 后重新运行指令。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MASTER_DIR="/opt/ip_sentinel_master"
|
||||
CONF_FILE="${MASTER_DIR}/master.conf"
|
||||
|
||||
echo "========================================================"
|
||||
echo " 🗑️ 准备卸载 IP-Sentinel Master (控制中枢)"
|
||||
|
||||
# [v3.4.0 优化] 卸载前读取并播报中枢版本号
|
||||
if [ -f "$CONF_FILE" ]; then
|
||||
MASTER_VER=$(grep "^MASTER_VERSION=" "$CONF_FILE" | cut -d'"' -f2)
|
||||
[ -n "$MASTER_VER" ] && echo " 📍 目标版本: v${MASTER_VER}"
|
||||
fi
|
||||
echo "========================================================"
|
||||
|
||||
echo -e "\n⚠️ 警告: 此操作将永久删除包含所有节点档案的 SQLite 数据库!"
|
||||
read -p "确定要继续卸载吗?(y/n) [默认 n]: " CONFIRM_DEL
|
||||
if [[ ! "$CONFIRM_DEL" =~ ^[Yy]$ ]]; then
|
||||
echo "已取消卸载操作。"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 1. 停止并删除 Systemd 服务 (适配新架构)
|
||||
echo "[1/4] 正在停止并删除 Systemd 服务..."
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
echo "💡 检测到 Systemd 环境,正在抹除 Systemd 服务单元..."
|
||||
systemctl disable --now ip-sentinel-master.service >/dev/null 2>&1
|
||||
rm -f /etc/systemd/system/ip-sentinel-master.service
|
||||
systemctl daemon-reload
|
||||
systemctl reset-failed
|
||||
else
|
||||
echo "💡 未检测到 Systemd,跳过此步骤..."
|
||||
fi
|
||||
|
||||
# 2. 停止运行中的 Master 守护进程 (兜底清理老版进程)
|
||||
echo "[2/4] 正在终止后台中枢调度进程..."
|
||||
pkill -9 -f "tg_master.sh" >/dev/null 2>&1 || true
|
||||
|
||||
# 3. 清除看门狗定时任务 (Cron)
|
||||
echo "[3/4] 正在清理系统定时任务 (Cron)..."
|
||||
crontab -l 2>/dev/null | grep -v "tg_master.sh" > /tmp/cron_backup
|
||||
crontab /tmp/cron_backup
|
||||
rm -f /tmp/cron_backup
|
||||
|
||||
# 4. 删除所有文件、配置与数据库
|
||||
echo "[4/4] 正在抹除核心程序、配置文件与 SQLite 数据库..."
|
||||
if [ -d "$MASTER_DIR" ]; then
|
||||
rm -rf "$MASTER_DIR"
|
||||
fi
|
||||
|
||||
echo "========================================================"
|
||||
echo "✅ 卸载彻底完成!Master 司令部已从您的系统中无痕移除。"
|
||||
echo "========================================================"
|
||||
83
scripts/fetch_trends.py
Normal file
83
scripts/fetch_trends.py
Normal file
@@ -0,0 +1,83 @@
|
||||
import urllib.request
|
||||
import xml.etree.ElementTree as ET
|
||||
import os
|
||||
import json
|
||||
import re
|
||||
|
||||
# ================== [路径防弹装甲] ==================
|
||||
# 无论在哪里执行该脚本,都能精准反推项目根目录
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
PROJECT_ROOT = os.path.dirname(SCRIPT_DIR)
|
||||
|
||||
MAP_JSON_PATH = os.path.join(PROJECT_ROOT, "data", "map.json")
|
||||
DATA_DIR = os.path.join(PROJECT_ROOT, "data", "keywords")
|
||||
# ====================================================
|
||||
|
||||
# 特殊战区代码映射 (Google Trends RSS 要求)
|
||||
GEO_FIX = {'UK': 'GB'}
|
||||
|
||||
HEADERS = {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
|
||||
}
|
||||
|
||||
def get_active_regions():
|
||||
"""动态提取 map.json 中的战区 (适配 v3.5.0 大洲战区降维架构)"""
|
||||
try:
|
||||
with open(MAP_JSON_PATH, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
regions = []
|
||||
# 第一层穿透:遍历所有大洲战区 (continents)
|
||||
for continent in data.get('continents', []):
|
||||
# 第二层穿透:遍历大洲下的所有国家 (countries)
|
||||
for country in continent.get('countries', []):
|
||||
if 'id' in country:
|
||||
regions.append(country['id'])
|
||||
return regions
|
||||
except Exception as e:
|
||||
print(f"❌ [读取地图失败]: {e}")
|
||||
return []
|
||||
|
||||
def fetch_trends(region_code):
|
||||
"""从 Google Trends 抓取当日热搜"""
|
||||
geo = GEO_FIX.get(region_code, region_code)
|
||||
url = f"https://trends.google.com/trending/rss?geo={geo}"
|
||||
try:
|
||||
req = urllib.request.Request(url, headers=HEADERS)
|
||||
with urllib.request.urlopen(req, timeout=10) as response:
|
||||
xml_data = response.read()
|
||||
root = ET.fromstring(xml_data)
|
||||
return [re.sub(r'[\n\r\t]', ' ', item.find('title').text).strip()
|
||||
for item in root.findall('./channel/item')
|
||||
if item.find('title') is not None]
|
||||
except Exception as e:
|
||||
print(f"⚠️ {region_code} 抓取异常: {e}")
|
||||
return []
|
||||
|
||||
def update_file(region, new_words):
|
||||
"""滑动窗口更新,保留 200 条最热记录"""
|
||||
os.makedirs(DATA_DIR, exist_ok=True)
|
||||
file_path = os.path.join(DATA_DIR, f"kw_{region}.txt")
|
||||
old_words = []
|
||||
if os.path.exists(file_path):
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
old_words = [l.strip() for l in f if l.strip()]
|
||||
|
||||
# 新词排在最前面,去重
|
||||
combined = new_words + [w for w in old_words if w not in new_words]
|
||||
final_list = combined[:200]
|
||||
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.write('\n'.join(final_list) + '\n')
|
||||
print(f"✅ [同步完成] {region}: 注入 {len(new_words)} 条新热点")
|
||||
|
||||
if __name__ == '__main__':
|
||||
regions = get_active_regions()
|
||||
if not regions:
|
||||
print("🛑 未发现活跃战区,请检查 map.json")
|
||||
exit(1)
|
||||
|
||||
for r in regions:
|
||||
print(f"📡 正在拉取 {r} 战区情报...")
|
||||
words = fetch_trends(r)
|
||||
if words:
|
||||
update_file(r, words)
|
||||
103
scripts/fetch_trust_urls.py
Normal file
103
scripts/fetch_trust_urls.py
Normal file
@@ -0,0 +1,103 @@
|
||||
import urllib.request
|
||||
import xml.etree.ElementTree as ET
|
||||
import os
|
||||
import json
|
||||
import random
|
||||
|
||||
# ================== [路径防弹装甲] ==================
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
PROJECT_ROOT = os.path.dirname(SCRIPT_DIR)
|
||||
REGIONS_DIR = os.path.join(PROJECT_ROOT, "data", "regions")
|
||||
# ====================================================
|
||||
|
||||
HEADERS = {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
|
||||
}
|
||||
|
||||
# 全球骨干新闻 RSS 监听矩阵
|
||||
RSS_FEEDS = {
|
||||
"US": ["http://rss.cnn.com/rss/cnn_topstories.rss", "https://feeds.npr.org/1001/rss.xml"],
|
||||
"UK": ["http://feeds.bbci.co.uk/news/rss.xml"],
|
||||
"AU": ["https://www.abc.net.au/news/feed/51120/rss.xml"],
|
||||
"CA": ["https://www.cbc.ca/cmlink/rss-topstories"],
|
||||
"DE": ["https://www.tagesschau.de/xml/rss2"],
|
||||
"FR": ["https://www.france24.com/fr/rss"],
|
||||
"ES": ["https://feeds.elpais.com/mrss-s/pages/ep/site/elpais.com/portada"],
|
||||
"JP": ["https://news.yahoo.co.jp/rss/topics/top-picks.xml"],
|
||||
"HK": ["https://hk.news.yahoo.com/rss/hong-kong"],
|
||||
"TW": ["https://news.google.com/rss?hl=zh-TW&gl=TW&ceid=TW:zh-Hant"],
|
||||
"KR": ["https://www.yonhapnewstv.co.kr/category/news/headline/feed/"],
|
||||
"SG": ["https://www.channelnewsasia.com/api/v1/rss-outbound-feed?_format=xml"],
|
||||
"NL": ["https://feeds.nos.nl/nosnieuwsalgemeen"],
|
||||
"VN": ["https://vnexpress.net/rss/tin-moi-nhat.rss"],
|
||||
"MY": ["https://news.google.com/rss?hl=en-MY&gl=MY&ceid=MY:en"]
|
||||
}
|
||||
|
||||
def fetch_rss_links(region_code, max_items=15):
|
||||
"""抓取该战区最新的 RSS 新闻链接"""
|
||||
feeds = RSS_FEEDS.get(region_code, [])
|
||||
if not feeds:
|
||||
return []
|
||||
|
||||
links = []
|
||||
for url in feeds:
|
||||
try:
|
||||
req = urllib.request.Request(url, headers=HEADERS)
|
||||
with urllib.request.urlopen(req, timeout=10) as response:
|
||||
xml_data = response.read()
|
||||
root = ET.fromstring(xml_data)
|
||||
for item in root.findall('.//item'):
|
||||
link = item.find('link')
|
||||
if link is not None and link.text:
|
||||
clean_link = link.text.strip()
|
||||
if clean_link.startswith('http'):
|
||||
links.append(clean_link)
|
||||
except Exception as e:
|
||||
print(f"⚠️ [{region_code}] RSS 抓取异常 ({url}): {e}")
|
||||
|
||||
# 去重并截取最新
|
||||
return list(set(links))[:max_items]
|
||||
|
||||
def process_json_file(file_path, region_code):
|
||||
"""融合静态基石与动态新闻"""
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
|
||||
trust_mod = data.get("trust_module", {})
|
||||
if not trust_mod or "static_urls" not in trust_mod:
|
||||
return
|
||||
|
||||
static_urls = trust_mod.get("static_urls", [])
|
||||
|
||||
# 抓取今日该战区的活体新闻流
|
||||
daily_news_urls = fetch_rss_links(region_code)
|
||||
|
||||
# 战术混合:基石(保证高权重) + 新闻(保证活体动态)
|
||||
combined_urls = static_urls + daily_news_urls
|
||||
|
||||
# 深度洗牌,打破机械顺序特征
|
||||
combined_urls = list(set(combined_urls))
|
||||
random.shuffle(combined_urls)
|
||||
|
||||
# 覆写回供 Agent 拉取的 white_urls
|
||||
trust_mod["white_urls"] = combined_urls
|
||||
data["trust_module"] = trust_mod
|
||||
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
print(f"✅ [信用融合] {os.path.basename(file_path)}: 骨干 {len(static_urls)} 条 + 活体 {len(daily_news_urls)} 条")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ [处理失败] {file_path}: {e}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("========== 启动 IP-Sentinel 活体新闻流融合引擎 ==========")
|
||||
for root_dir, _, files in os.walk(REGIONS_DIR):
|
||||
for file in files:
|
||||
if file.endswith(".json"):
|
||||
file_path = os.path.join(root_dir, file)
|
||||
region_code = os.path.relpath(file_path, REGIONS_DIR).split(os.sep)[0]
|
||||
process_json_file(file_path, region_code)
|
||||
print("========== 融合引擎执行完毕 ==========")
|
||||
77
scripts/ua_generator.py
Normal file
77
scripts/ua_generator.py
Normal file
@@ -0,0 +1,77 @@
|
||||
import random
|
||||
import os
|
||||
|
||||
# ==========================================
|
||||
# IP-Sentinel 超大型高保真指纹工厂 (V3.1.5)
|
||||
# 无需第三方库,直接生成千万级组合的真实指纹
|
||||
# ==========================================
|
||||
|
||||
def generate_chrome_version():
|
||||
# 模拟 2024 年主流 Chrome 内核号 (122 - 125)
|
||||
major = random.randint(122, 125)
|
||||
build = random.randint(5000, 6500)
|
||||
patch = random.randint(10, 150)
|
||||
return f"{major}.0.{build}.{patch}"
|
||||
|
||||
def generate_windows_ua(count=1000):
|
||||
uas = set()
|
||||
while len(uas) < count:
|
||||
# 现代 Windows UA 已经固化为 Windows NT 10.0
|
||||
uas.add(f"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{generate_chrome_version()} Safari/537.36")
|
||||
return list(uas)
|
||||
|
||||
def generate_macos_ua(count=1000):
|
||||
uas = set()
|
||||
while len(uas) < count:
|
||||
mac_os_minor = random.randint(11, 15)
|
||||
mac_os_patch = random.randint(1, 6)
|
||||
if random.choice([True, False]):
|
||||
# Chrome on Mac
|
||||
uas.add(f"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_{mac_os_minor}_{mac_os_patch}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{generate_chrome_version()} Safari/537.36")
|
||||
else:
|
||||
# Safari on Mac
|
||||
safari_build = f"605.1.{random.randint(10, 15)}"
|
||||
safari_version = f"17.{random.randint(1, 4)}"
|
||||
uas.add(f"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_{mac_os_minor}_{mac_os_patch}) AppleWebKit/{safari_build} (KHTML, like Gecko) Version/{safari_version} Safari/{safari_build}")
|
||||
return list(uas)
|
||||
|
||||
def generate_ios_ua(count=1000):
|
||||
uas = set()
|
||||
devices = ["iPhone", "iPad"]
|
||||
while len(uas) < count:
|
||||
device = random.choice(devices)
|
||||
ios_major = random.randint(16, 17)
|
||||
ios_minor = random.randint(1, 5)
|
||||
ios_patch = random.randint(1, 3)
|
||||
safari_build = f"605.1.{random.randint(10, 15)}"
|
||||
safari_version = f"{ios_major}.{random.choice(['0', '1', '2', '3'])}"
|
||||
|
||||
uas.add(f"Mozilla/5.0 ({device}; CPU {'iPhone ' if device=='iPhone' else ''}OS {ios_major}_{ios_minor}_{ios_patch} like Mac OS X) AppleWebKit/{safari_build} (KHTML, like Gecko) Version/{safari_version} Mobile/15E148 Safari/604.1")
|
||||
return list(uas)
|
||||
|
||||
def generate_android_ua(count=1000):
|
||||
uas = set()
|
||||
# 主流 Android 机型库
|
||||
models = ["Pixel 8 Pro", "Pixel 8", "Pixel 7a", "Pixel 7 Pro", "SM-S928B", "SM-S928U", "SM-S918B", "SM-A546B", "SM-A346B", "23113RKC6C", "23049PCD8G", "CPH2437", "V2227A", "PGT-AN10", "NX729J"]
|
||||
while len(uas) < count:
|
||||
android_ver = random.randint(12, 14)
|
||||
model = random.choice(models)
|
||||
uas.add(f"Mozilla/5.0 (Linux; Android {android_ver}; {model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{generate_chrome_version()} Mobile Safari/537.36")
|
||||
return list(uas)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 确保输出目录存在
|
||||
os.makedirs('data', exist_ok=True)
|
||||
|
||||
# 严格按照“绝对坐标”顺序生成 4000 条数据
|
||||
pool = []
|
||||
pool.extend(generate_windows_ua(1000)) # 行 1-1000
|
||||
pool.extend(generate_macos_ua(1000)) # 行 1001-2000
|
||||
pool.extend(generate_ios_ua(1000)) # 行 2001-3000
|
||||
pool.extend(generate_android_ua(1000)) # 行 3001-4000
|
||||
|
||||
with open('data/user_agents.txt', 'w') as f:
|
||||
for ua in pool:
|
||||
f.write(ua + '\n')
|
||||
|
||||
print(f"✅ 成功生成 4000 条高保真绝对坐标指纹库!")
|
||||
75
telemetry/worker.js
Normal file
75
telemetry/worker.js
Normal file
@@ -0,0 +1,75 @@
|
||||
// IP-Sentinel Glasshouse Telemetry (全透明装机量统计中枢)
|
||||
// 部署环境: Cloudflare Workers + KV
|
||||
// 隐私声明: 绝对不采集、不存储用户的 IP 地址、Header、Token 及任何系统特征参数。仅做纯粹的原子累加。
|
||||
|
||||
export default {
|
||||
async fetch(request, env) {
|
||||
const url = new URL(request.url);
|
||||
const path = url.pathname;
|
||||
|
||||
// 全局跨域头,确保 GitHub README 的 Shields.io 徽章能正常读取
|
||||
const corsHeaders = {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "GET",
|
||||
};
|
||||
|
||||
// 核心原子操作:无情的 +1 机器
|
||||
async function incrementCounter(key) {
|
||||
let count = await env.SENTINEL_KV.get(key);
|
||||
count = count ? parseInt(count) + 1 : 1;
|
||||
await env.SENTINEL_KV.put(key, count.toString());
|
||||
return count;
|
||||
}
|
||||
|
||||
async function getCounter(key) {
|
||||
let count = await env.SENTINEL_KV.get(key);
|
||||
return count ? parseInt(count) : 0;
|
||||
}
|
||||
|
||||
try {
|
||||
// 1. Agent (哨兵) 部署触发接口
|
||||
if (path === '/ping/agent') {
|
||||
const count = await incrementCounter('agent_count');
|
||||
return new Response(count.toString(), { headers: corsHeaders });
|
||||
}
|
||||
|
||||
// 2. Master (指挥部) 部署触发接口
|
||||
if (path === '/ping/master') {
|
||||
const count = await incrementCounter('master_count');
|
||||
return new Response(count.toString(), { headers: corsHeaders });
|
||||
}
|
||||
|
||||
// 3. GitHub README Agent 徽章接口 (输出给 Shields.io)
|
||||
if (path === '/stats/agent') {
|
||||
const count = await getCounter('agent_count');
|
||||
const shield = {
|
||||
schemaVersion: 1,
|
||||
label: "Agent Nodes",
|
||||
message: count.toString(),
|
||||
color: "blue"
|
||||
};
|
||||
return new Response(JSON.stringify(shield), {
|
||||
headers: { ...corsHeaders, "Content-Type": "application/json" }
|
||||
});
|
||||
}
|
||||
|
||||
// 4. GitHub README Master 徽章接口 (输出给 Shields.io)
|
||||
if (path === '/stats/master') {
|
||||
const count = await getCounter('master_count');
|
||||
const shield = {
|
||||
schemaVersion: 1,
|
||||
label: "Master Commands",
|
||||
message: count.toString(),
|
||||
color: "orange"
|
||||
};
|
||||
return new Response(JSON.stringify(shield), {
|
||||
headers: { ...corsHeaders, "Content-Type": "application/json" }
|
||||
});
|
||||
}
|
||||
|
||||
return new Response("IP-Sentinel Glasshouse Telemetry API (No IP Logged, 100% Transparent)", { status: 200 });
|
||||
} catch (err) {
|
||||
return new Response("Error", { status: 500 });
|
||||
}
|
||||
}
|
||||
};
|
||||
2
version.txt
Normal file
2
version.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
MASTER_VERSION=4.0.3
|
||||
AGENT_VERSION=4.0.3
|
||||
Reference in New Issue
Block a user