pymecli 0.5.0__tar.gz → 0.5.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. {pymecli-0.5.0 → pymecli-0.5.2}/PKG-INFO +1 -1
  2. pymecli-0.5.2/cli/dst.py +292 -0
  3. {pymecli-0.5.0 → pymecli-0.5.2}/cli/util.py +4 -8
  4. {pymecli-0.5.0 → pymecli-0.5.2}/core/config.py +1 -1
  5. pymecli-0.5.2/data/template.yaml +156 -0
  6. {pymecli-0.5.0 → pymecli-0.5.2}/pyproject.toml +2 -2
  7. pymecli-0.5.0/cli/baidu.py +0 -93
  8. pymecli-0.5.0/cli/dst.py +0 -222
  9. pymecli-0.5.0/data/template.yaml +0 -70
  10. {pymecli-0.5.0 → pymecli-0.5.2}/.gitignore +0 -0
  11. {pymecli-0.5.0 → pymecli-0.5.2}/README.md +0 -0
  12. {pymecli-0.5.0 → pymecli-0.5.2}/api/__init__.py +0 -0
  13. {pymecli-0.5.0 → pymecli-0.5.2}/api/v1/__init__.py +0 -0
  14. {pymecli-0.5.0 → pymecli-0.5.2}/api/v1/clash.py +0 -0
  15. {pymecli-0.5.0 → pymecli-0.5.2}/api/v1/redis.py +0 -0
  16. {pymecli-0.5.0 → pymecli-0.5.2}/cli/__init__.py +0 -0
  17. {pymecli-0.5.0 → pymecli-0.5.2}/cli/bitget.py +0 -0
  18. {pymecli-0.5.0 → pymecli-0.5.2}/cli/example.py +0 -0
  19. {pymecli-0.5.0 → pymecli-0.5.2}/cli/fast.py +0 -0
  20. {pymecli-0.5.0 → pymecli-0.5.2}/cli/gate.py +0 -0
  21. {pymecli-0.5.0 → pymecli-0.5.2}/cli/redis_csv.py +0 -0
  22. {pymecli-0.5.0 → pymecli-0.5.2}/core/__init__.py +0 -0
  23. {pymecli-0.5.0 → pymecli-0.5.2}/core/clash.py +0 -0
  24. {pymecli-0.5.0 → pymecli-0.5.2}/core/redis_client.py +0 -0
  25. {pymecli-0.5.0 → pymecli-0.5.2}/crypto/__init__.py +0 -0
  26. {pymecli-0.5.0 → pymecli-0.5.2}/crypto/bitget.py +0 -0
  27. {pymecli-0.5.0 → pymecli-0.5.2}/crypto/gate.py +0 -0
  28. {pymecli-0.5.0 → pymecli-0.5.2}/data/__init__.py +0 -0
  29. {pymecli-0.5.0 → pymecli-0.5.2}/data/dou_dict.py +0 -0
  30. {pymecli-0.5.0 → pymecli-0.5.2}/data/dou_list.py +0 -0
  31. {pymecli-0.5.0 → pymecli-0.5.2}/data/main.py +0 -0
  32. {pymecli-0.5.0 → pymecli-0.5.2}/models/__init__.py +0 -0
  33. {pymecli-0.5.0 → pymecli-0.5.2}/models/douzero_model.py +0 -0
  34. {pymecli-0.5.0 → pymecli-0.5.2}/models/ocr_model.py +0 -0
  35. {pymecli-0.5.0 → pymecli-0.5.2}/models/response.py +0 -0
  36. {pymecli-0.5.0 → pymecli-0.5.2}/utils/__init__.py +0 -0
  37. {pymecli-0.5.0 → pymecli-0.5.2}/utils/elapsed.py +0 -0
  38. {pymecli-0.5.0 → pymecli-0.5.2}/utils/helper.py +0 -0
  39. {pymecli-0.5.0 → pymecli-0.5.2}/utils/logger.py +0 -0
  40. {pymecli-0.5.0 → pymecli-0.5.2}/utils/mysql.py +0 -0
  41. {pymecli-0.5.0 → pymecli-0.5.2}/utils/path.py +0 -0
  42. {pymecli-0.5.0 → pymecli-0.5.2}/utils/pd.py +0 -0
  43. {pymecli-0.5.0 → pymecli-0.5.2}/utils/pyredis.py +0 -0
  44. {pymecli-0.5.0 → pymecli-0.5.2}/utils/sleep.py +0 -0
  45. {pymecli-0.5.0 → pymecli-0.5.2}/utils/text.py +0 -0
  46. {pymecli-0.5.0 → pymecli-0.5.2}/utils/toml.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pymecli
3
- Version: 0.5.0
3
+ Version: 0.5.2
4
4
  Summary: My CLI
5
5
  Project-URL: Homepage, https://pypi.org/project/pymecli
6
6
  Project-URL: Repository, https://github.com/meme2046/pymecli
@@ -0,0 +1,292 @@
1
+ import re
2
+ from typing import Dict, List, Optional
3
+
4
+ import requests
5
+ import typer
6
+ from slpp import slpp as lua
7
+
8
+ app = typer.Typer()
9
+
10
+ STEAM_API_URL = (
11
+ "https://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v0001"
12
+ )
13
+
14
+
15
+ def get_mod_versions(workshop_ids: List[str]) -> Dict[str, Dict[str, Optional[str]]]:
16
+ if not workshop_ids:
17
+ return {}
18
+
19
+ data = {"itemcount": str(len(workshop_ids))}
20
+ for i, wid in enumerate(workshop_ids):
21
+ data[f"publishedfileids[{i}]"] = wid
22
+
23
+ try:
24
+ response = requests.post(STEAM_API_URL, data=data, timeout=15)
25
+ response.raise_for_status()
26
+ result = response.json()
27
+ except Exception as e:
28
+ typer.secho(f"✗ Steam API 请求失败: {e}", fg=typer.colors.RED)
29
+ return {}
30
+
31
+ versions: Dict[str, Dict[str, Optional[str]]] = {}
32
+ for detail in result["response"].get("publishedfiledetails", []):
33
+ publishedfileid = detail.get("publishedfileid")
34
+ title = detail.get("title")
35
+ tags = detail.get("tags", [])
36
+ version = None
37
+ for tag in tags:
38
+ tag_str = tag.get("tag", "")
39
+ if tag_str.startswith("version:"):
40
+ version = tag_str.replace("version:", "")
41
+ break
42
+ versions[publishedfileid] = {"version": version, "title": title}
43
+
44
+ return versions
45
+
46
+
47
+ def extract_workshop_id(key: str) -> Optional[str]:
48
+ match = re.match(r"workshop-(\d+)", key)
49
+ return match.group(1) if match else None
50
+
51
+
52
+ def parse_lua_file(file_path: str) -> dict:
53
+ with open(file_path, "r", encoding="utf-8") as f:
54
+ source = f.read()
55
+ source = source.strip()
56
+ if source.startswith("return"):
57
+ source = source[6:].strip()
58
+ result = lua.decode(source)
59
+ assert isinstance(result, dict)
60
+ return result
61
+
62
+
63
+ def write_lua_file(file_path: str, data: dict):
64
+ with open(file_path, "w", encoding="utf-8") as f:
65
+ f.write("return " + lua.encode(data) + "\n")
66
+
67
+
68
+ def get_client_mods(file_path: str, mod_id: str) -> Optional[tuple[dict, dict]]:
69
+ """
70
+ 解析 lua 文件,返回 (data, client_mods)。
71
+ 失败返回 None(错误信息已由函数自行打印)。
72
+ """
73
+ try:
74
+ data = parse_lua_file(file_path)
75
+ except FileNotFoundError:
76
+ typer.secho(f"✗ 文件不存在: {file_path}", fg=typer.colors.RED)
77
+ return None
78
+ except Exception as e:
79
+ typer.secho(f"✗ 解析 Lua 文件失败: {e}", fg=typer.colors.RED)
80
+ return None
81
+
82
+ main_mod_key = f"workshop-{mod_id}"
83
+ main_mod = data.get(main_mod_key)
84
+ if not main_mod:
85
+ typer.secho(f"✗ 未找到 {main_mod_key}", fg=typer.colors.RED)
86
+ return data, {}
87
+
88
+ client_mods = main_mod.get("configuration_options", {}).get("client_mods_list", {})
89
+ if not client_mods:
90
+ typer.secho("✗ 未找到 client_mods", fg=typer.colors.RED)
91
+ return data, {}
92
+
93
+ return data, client_mods
94
+
95
+
96
+ # update_client_mods
97
+ @app.command()
98
+ def convert_update(
99
+ file_path: str = typer.Argument(
100
+ "d:/github/meme2046/docker/dst/modoverrides.lua",
101
+ help="modoverrides.lua 文件路径",
102
+ ),
103
+ mod_id: str = typer.Option(
104
+ "3486375086",
105
+ "--mod-id",
106
+ "-m",
107
+ help="模组: 客户端Mod转为服务器Mod<3486375086>的ID, 固定值",
108
+ ),
109
+ output: List[str] = typer.Option(
110
+ None,
111
+ "--output",
112
+ "-o",
113
+ help="额外的输出路径,可多次指定",
114
+ ),
115
+ ):
116
+ """
117
+ 更新modoverrides.lua中<3486375086>的mods的版本信息
118
+ """
119
+ result = get_client_mods(file_path, mod_id)
120
+ if result is None:
121
+ raise typer.Exit(code=1)
122
+ data, client_mods = result
123
+
124
+ workshop_ids = []
125
+ for key in client_mods.keys():
126
+ wid = extract_workshop_id(key)
127
+ if wid:
128
+ workshop_ids.append(wid)
129
+
130
+ if not workshop_ids:
131
+ typer.secho("✗ 未找到任何 client_mods", fg=typer.colors.RED)
132
+ raise typer.Exit(code=1)
133
+
134
+ typer.secho(
135
+ f"🔍 发现 {len(workshop_ids)} 个 client mods, 正在查询 Steam API...",
136
+ fg=typer.colors.BLUE,
137
+ )
138
+ versions = get_mod_versions(workshop_ids)
139
+
140
+ if not versions:
141
+ typer.secho("✗ 未能获取steam版本信息", fg=typer.colors.RED)
142
+ raise typer.Exit(code=1)
143
+
144
+ typer.secho("📝 正在更新版本号...", fg=typer.colors.BLUE)
145
+ updated_count = 0
146
+ for mod_key, mod_info in client_mods.items():
147
+ wid = extract_workshop_id(mod_key)
148
+ if not wid:
149
+ continue
150
+ mod_data = versions.get(wid)
151
+ if mod_data is None:
152
+ typer.secho(f"✗ {mod_key}: 未获取到版本信息", fg=typer.colors.YELLOW)
153
+ continue
154
+ new_version = mod_data.get("version")
155
+ title = mod_data.get("title", "")
156
+ if new_version is None:
157
+ typer.secho(
158
+ f"✗ {mod_key} ({title}): 未获取到版本信息", fg=typer.colors.YELLOW
159
+ )
160
+ continue
161
+ old_version = mod_info.get("version")
162
+ if old_version != new_version:
163
+ mod_info["version"] = new_version
164
+ updated_count += 1
165
+ typer.secho(
166
+ f"✓ {mod_key} ({title}): {old_version} -> {new_version}",
167
+ fg=typer.colors.GREEN,
168
+ )
169
+ else:
170
+ typer.secho(
171
+ f"{mod_key} ({title}): 已是最新版本 {old_version}",
172
+ fg=typer.colors.WHITE,
173
+ )
174
+
175
+ try:
176
+ if updated_count > 0:
177
+ write_lua_file(file_path, data)
178
+ typer.secho(f"💾 已保存到: {file_path}", fg=typer.colors.CYAN)
179
+ if output:
180
+ for p in output:
181
+ write_lua_file(p, data)
182
+ typer.secho(f"💾 已保存到: {p}", fg=typer.colors.CYAN)
183
+ except Exception as e:
184
+ typer.secho(f"✗ 写入文件失败: {e}", fg=typer.colors.RED)
185
+ raise typer.Exit(code=1)
186
+
187
+ if updated_count > 0:
188
+ typer.secho(f"🎉 成功更新 {updated_count} 个 mod 版本", fg=typer.colors.GREEN)
189
+ else:
190
+ typer.secho("✓ 所有MOD已是最新版本", fg=typer.colors.YELLOW)
191
+
192
+
193
+ @app.command()
194
+ def mod_setup(
195
+ file_path: str = typer.Argument(
196
+ "d:/github/meme2046/docker/dst/modoverrides.lua",
197
+ help="modoverrides.lua 文件路径",
198
+ ),
199
+ mod_id: str = typer.Option(
200
+ "3486375086",
201
+ "--mod-id",
202
+ "-m",
203
+ help="模组: 客户端Mod转为服务器Mod<3486375086>的ID, 固定值",
204
+ ),
205
+ output: List[str] = typer.Option(
206
+ ["dedicated_server_mods_setup.lua"],
207
+ "--output",
208
+ "-o",
209
+ help="mods_setup输出路径,可多次指定",
210
+ ),
211
+ ):
212
+ """
213
+ 列出所有 mod, 并以<dedicated_server_mods_setup>格式保存到指定文件
214
+ """
215
+ result = get_client_mods(file_path, mod_id)
216
+ if result is None:
217
+ raise typer.Exit(code=1)
218
+ data, client_mods = result
219
+
220
+ client_ids = []
221
+ for key in client_mods.keys():
222
+ wid = extract_workshop_id(key)
223
+ if wid:
224
+ client_ids.append(wid)
225
+
226
+ server_ids = []
227
+ for key in data.keys():
228
+ wid = extract_workshop_id(key)
229
+ if wid:
230
+ server_ids.append(wid)
231
+
232
+ versions = get_mod_versions(server_ids + client_ids)
233
+
234
+ if not versions:
235
+ typer.secho("✗ 未能获取steam版本信息", fg=typer.colors.RED)
236
+ raise typer.Exit(code=1)
237
+
238
+ setup_list = []
239
+
240
+ typer.secho(
241
+ f"📢 server_mods({len(server_ids)}):",
242
+ fg=typer.colors.GREEN,
243
+ )
244
+
245
+ for item in server_ids:
246
+ mod_data = versions.get(item)
247
+
248
+ if mod_data is None:
249
+ typer.secho(f"✗ {item}: 未获取到版本信息", fg=typer.colors.YELLOW)
250
+ raise typer.Exit(code=1)
251
+
252
+ setup_list.append(f'ServerModSetup("{item}")')
253
+
254
+ new_version = mod_data.get("version")
255
+ title = mod_data.get("title", "")
256
+
257
+ typer.secho(
258
+ f"{item} ({title}): {new_version}",
259
+ fg=typer.colors.WHITE,
260
+ )
261
+
262
+ typer.secho(
263
+ f"📢 client_mods({len(client_ids)}):",
264
+ fg=typer.colors.GREEN,
265
+ )
266
+
267
+ for item in client_ids:
268
+ mod_data = versions.get(item)
269
+
270
+ if mod_data is None:
271
+ typer.secho(f"✗ {item}: 未获取到版本信息", fg=typer.colors.YELLOW)
272
+ raise typer.Exit(code=1)
273
+
274
+ setup_list.append(f'ServerModSetup("{item}")')
275
+
276
+ new_version = mod_data.get("version")
277
+ title = mod_data.get("title", "")
278
+
279
+ typer.secho(
280
+ f"{item} ({title}): {new_version}",
281
+ fg=typer.colors.WHITE,
282
+ )
283
+
284
+ for p in output:
285
+ with open(p, "w", encoding="utf-8") as f:
286
+ f.write("\n".join(setup_list))
287
+ typer.secho(f"💾 <setup_list>已保存到: {p}", fg=typer.colors.CYAN)
288
+
289
+
290
+ if __name__ == "__main__":
291
+ # cmu("d:/github/meme2046/docker/dst/modoverrides.lua", "3486375086")
292
+ convert_update("d:/github/meme2046/docker/dst/modoverrides.lua", "3486375086")
@@ -68,12 +68,8 @@ def emoji():
68
68
  emoji_list = [
69
69
  "『",
70
70
  "』",
71
- "✔",
72
- "✘",
73
- "❗",
74
- "⭕",
75
- "❓",
76
- "❌",
71
+ "✓",
72
+ "✗",
77
73
  "⤴︎",
78
74
  "⤵︎",
79
75
  "⇡",
@@ -147,7 +143,7 @@ def say():
147
143
  @app.command()
148
144
  def ipv6():
149
145
  """
150
- 获取本地 IPV6 稳定地址
146
+ 获取本地 IPv6 稳定地址
151
147
  """
152
148
  ips = []
153
149
  adapters = ifaddr.get_adapters(include_unconfigured=False)
@@ -178,7 +174,7 @@ def ipv6():
178
174
  print("未找到稳定 IPV6 地址")
179
175
  return ips
180
176
 
181
- print(f"IPV6 稳定地址: {ips}")
177
+ print(f"IPv6 稳定地址: {ips}")
182
178
 
183
179
  r = get_redis_client_sync()
184
180
  r.set("local.IPv6", ips[0])
@@ -19,7 +19,7 @@ class Settings(BaseSettings):
19
19
  REDIS_PASSWORD: str = ""
20
20
 
21
21
  PROJECT_DESCRIPTION: str = (
22
- f"{metadata['Summary']}, FastAPI提供: clash订阅转换、finance.baidu api"
22
+ f"{metadata['Summary']}, FastAPI提供: clash订阅转换、redis API"
23
23
  )
24
24
  PROJECT_VERSION: str = metadata["Version"]
25
25
 
@@ -0,0 +1,156 @@
1
+ external-controller: ""
2
+ ipv6: true
3
+ mode: rule
4
+ mixed-port: 7897
5
+ socks-port: 0
6
+ port: 0
7
+ redir-port: 0
8
+ tproxy-port: 0
9
+ allow-lan: true
10
+ unified-delay: true
11
+ tcp-concurrent: false
12
+ log-level: info
13
+ find-process-mode: strict
14
+ bind-address: "*"
15
+ lan-allowed-ips:
16
+ - 0.0.0.0/0
17
+ - ::/0
18
+ lan-disallowed-ips: []
19
+ authentication: []
20
+ skip-auth-prefixes:
21
+ - 127.0.0.1/32
22
+ - ::1/128
23
+ tun:
24
+ enable: false
25
+ stack: mixed
26
+ auto-route: true
27
+ auto-redirect: false
28
+ auto-detect-interface: true
29
+ dns-hijack:
30
+ - any:53
31
+ route-exclude-address: []
32
+ mtu: 1500
33
+ device: Mihomo
34
+ dns:
35
+ enable: true
36
+ ipv6: true
37
+ enhanced-mode: fake-ip
38
+ fake-ip-range: 198.18.0.1/16
39
+ fake-ip-filter:
40
+ - "*"
41
+ - +.lan
42
+ - +.local
43
+ - time.*.com
44
+ - ntp.*.com
45
+ - +.market.xiaomi.com
46
+ use-hosts: false
47
+ use-system-hosts: false
48
+ respect-rules: true
49
+ default-nameserver:
50
+ - https://223.5.5.5/dns-query
51
+ - https://223.6.6.6/dns-query
52
+ - https://8.8.8.8/dns-query
53
+ - https://1.1.1.1/dns-query
54
+ - 223.5.5.5
55
+ nameserver:
56
+ - https://dns.alidns.com/dns-query
57
+ - https://doh.pub/dns-query
58
+ proxy-server-nameserver:
59
+ - https://cloudflare-dns.com/dns-query
60
+ - https://dns.google/dns-query
61
+ - https://dns.alidns.com/dns-query
62
+ - https://doh.pub/dns-query
63
+ direct-nameserver:
64
+ - https://dns.alidns.com/dns-query
65
+ - https://doh.pub/dns-query
66
+ fallback:
67
+ - https://cloudflare-dns.com/dns-query
68
+ - https://dns.google/dns-query
69
+ fallback-filter:
70
+ geoip: true
71
+ geoip-code: CN
72
+ ipcidr:
73
+ - 240.0.0.0/4
74
+ - 0.0.0.0/32
75
+ - 127.0.0.0/8
76
+ - 10.0.0.0/8
77
+ - 192.168.0.0/16
78
+ domain:
79
+ - +.google.com
80
+ - +.facebook.com
81
+ - +.youtube.com
82
+ direct-nameserver-follow-policy: true
83
+ prefer-h3: false
84
+ nameserver-policy:
85
+ geosite:cn:
86
+ - https://dns.alidns.com/dns-query
87
+ - https://doh.pub/dns-query
88
+ geosite:geolocation-!cn:
89
+ - https://cloudflare-dns.com/dns-query
90
+ - https://dns.google/dns-query
91
+ +.jsdelivr.net:
92
+ - https://cloudflare-dns.com/dns-query
93
+ - https://dns.google/dns-query
94
+ +.github.com:
95
+ - https://cloudflare-dns.com/dns-query
96
+ - https://dns.google/dns-query
97
+ +.githubusercontent.com:
98
+ - https://cloudflare-dns.com/dns-query
99
+ - https://dns.google/dns-query
100
+ +.githubassets.com:
101
+ - https://cloudflare-dns.com/dns-query
102
+ - https://dns.google/dns-query
103
+ +.fastly.net:
104
+ - https://cloudflare-dns.com/dns-query
105
+ - https://dns.google/dns-query
106
+ sniffer:
107
+ enable: true
108
+ parse-pure-ip: true
109
+ force-dns-mapping: true
110
+ override-destination: true
111
+ sniff:
112
+ HTTP:
113
+ ports:
114
+ - "80"
115
+ - 8080-8880
116
+ override-destination: true
117
+ TLS:
118
+ ports:
119
+ - "443"
120
+ - "8443"
121
+ QUIC:
122
+ ports:
123
+ - "443"
124
+ - "8443"
125
+ - "4433"
126
+ skip-domain:
127
+ - +.push.apple.com
128
+ skip-dst-address:
129
+ - 91.105.192.0/23
130
+ - 91.108.4.0/22
131
+ - 91.108.8.0/21
132
+ - 91.108.16.0/21
133
+ - 91.108.56.0/22
134
+ - 95.161.64.0/20
135
+ - 149.154.160.0/20
136
+ - 185.76.151.0/24
137
+ - 2001:67c:4e8::/48
138
+ - 2001:b28:f23c::/47
139
+ - 2001:b28:f23f::/48
140
+ - 2a0a:f280:203::/48
141
+ profile:
142
+ store-selected: true
143
+ store-fake-ip: true
144
+ geo-auto-update: false
145
+ geo-update-interval: 24
146
+ geodata-mode: true
147
+ geox-url:
148
+ geoip: https://fastly.jsdelivr.net/gh/Loyalsoldier/geoip@release/geoip.dat
149
+ mmdb: https://fastly.jsdelivr.net/gh/Loyalsoldier/geoip@release/Country.mmdb
150
+ asn: https://fastly.jsdelivr.net/gh/Loyalsoldier/geoip@release/GeoLite2-ASN.mmdb
151
+ geosite: https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geosite.dat
152
+ proxies: []
153
+ proxy-providers: {}
154
+ proxy-groups: []
155
+ rule-providers: {}
156
+ rules: []
@@ -35,14 +35,14 @@
35
35
  name = "pymecli"
36
36
  readme = "README.md"
37
37
  requires-python = ">=3.10"
38
- version = "0.5.0"
38
+ version = "0.5.2"
39
39
 
40
40
  [project.urls]
41
41
  Homepage = "https://pypi.org/project/pymecli"
42
42
  Repository = "https://github.com/meme2046/pymecli"
43
43
 
44
44
  [project.scripts]
45
- baidu = "cli.baidu:app"
45
+ # baidu = "cli.baidu:app"
46
46
  bitget = "cli.bitget:app"
47
47
  dst = "cli.dst:app"
48
48
  example = "cli.example:app"
@@ -1,93 +0,0 @@
1
- import json
2
-
3
- import typer
4
- from selenium import webdriver
5
- from selenium.webdriver.chrome.options import Options
6
- from selenium.webdriver.chrome.service import Service
7
- from webdriver_manager.chrome import ChromeDriverManager
8
-
9
- from utils.pyredis import get_redis_client_sync
10
-
11
- app = typer.Typer()
12
-
13
- r = get_redis_client_sync()
14
-
15
-
16
- @app.command()
17
- def acsToken(
18
- url: str = typer.Argument(
19
- "https://finance.baidu.com/index/ab-000001?name=%25E4%25B8%258A%25E8%25AF%2581%25E6%258C%2587%25E6%2595%25B0",
20
- help="request url",
21
- ),
22
- timeout: int = typer.Option(
23
- 10,
24
- "--timeout",
25
- "-t",
26
- help="timeout内如果没有请求完整,会强制中断,然后从已有的请求中获取数据",
27
- ),
28
- ):
29
- """通过selenium获取百度股事通Acs-Token
30
-
31
- Args:
32
- url (str, optional): 请求的『URL』地址. Defaults to typer.Argument( "https://finance.baidu.com/", help="request url", ).
33
- timeout (int, optional): _description_. Defaults to typer.Option( 3, "--timeout", "-t", help="timeout内如果没有请求完整,会强制中断,然后从已有的请求中获取数据", ).
34
-
35
- Returns:
36
- str: 『Acs-Token』字符串
37
- """
38
- # 配置 Chrome 浏览器以启用 Performance API
39
- chrome_options = Options()
40
- chrome_options.add_argument("--enable-logging")
41
- chrome_options.add_argument("--v=1")
42
- chrome_options.add_argument("--headless") # 启用无头模式
43
- chrome_options.add_argument("--disable-gpu") # 禁用 GPU 加速
44
- chrome_options.add_argument("--window-size=1920,1080") # 设置窗口大小
45
- chrome_options.add_argument("--no-sandbox") # 禁用沙盒模式
46
- chrome_options.add_argument("--disable-dev-shm-usage") # 禁用 /dev/shm 使用
47
- chrome_options.set_capability("goog:loggingPrefs", {"performance": "ALL"})
48
-
49
- driver = webdriver.Chrome(
50
- service=Service(ChromeDriverManager().install()), options=chrome_options
51
- )
52
- driver.set_page_load_timeout(timeout)
53
-
54
- try:
55
- driver.get(url)
56
- except Exception:
57
- driver.execute_script("window.stop();")
58
-
59
- # 从网络请求中提取 Acs-Token
60
- acs_token = get_acs_token_from_requests(driver)
61
-
62
- driver.quit()
63
-
64
- if acs_token:
65
- print(f"Acs-Token:『{acs_token}』")
66
- r.set("baidu.Acs-Token", acs_token)
67
-
68
- return acs_token
69
-
70
-
71
- def get_acs_token_from_requests(driver):
72
- """从网络请求中提取 Acs-Token"""
73
- # 获取性能日志
74
-
75
- logs = driver.get_log("performance")
76
-
77
- for entry in logs:
78
- log = json.loads(entry["message"])["message"]
79
-
80
- # 检查是否是网络请求
81
- if log["method"] == "Network.requestWillBeSent":
82
- request = log["params"].get("request", {})
83
- headers = request.get("headers", {})
84
-
85
- # 检查请求头中是否包含 Acs-Token
86
- if "Acs-Token" in headers:
87
- return headers["Acs-Token"]
88
-
89
- return ""
90
-
91
-
92
- if __name__ == "__main__":
93
- app()
pymecli-0.5.0/cli/dst.py DELETED
@@ -1,222 +0,0 @@
1
- import re
2
- from typing import Dict, List, Optional
3
-
4
- import requests
5
- import typer
6
- from slpp import slpp as lua
7
-
8
- app = typer.Typer()
9
-
10
- STEAM_API_URL = (
11
- "https://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v0001"
12
- )
13
-
14
-
15
- def get_mod_versions(workshop_ids: List[str]) -> Dict[str, Dict[str, Optional[str]]]:
16
- if not workshop_ids:
17
- return {}
18
-
19
- data = {"itemcount": str(len(workshop_ids))}
20
- for i, wid in enumerate(workshop_ids):
21
- data[f"publishedfileids[{i}]"] = wid
22
-
23
- try:
24
- response = requests.post(STEAM_API_URL, data=data, timeout=15)
25
- response.raise_for_status()
26
- result = response.json()
27
- except Exception as e:
28
- typer.secho(f"❌ Steam API 请求失败: {e}", fg=typer.colors.RED)
29
- return {}
30
-
31
- versions: Dict[str, Dict[str, Optional[str]]] = {}
32
- for detail in result["response"].get("publishedfiledetails", []):
33
- publishedfileid = detail.get("publishedfileid")
34
- title = detail.get("title")
35
- tags = detail.get("tags", [])
36
- version = None
37
- for tag in tags:
38
- tag_str = tag.get("tag", "")
39
- if tag_str.startswith("version:"):
40
- version = tag_str.replace("version:", "")
41
- break
42
- versions[publishedfileid] = {"version": version, "title": title}
43
-
44
- return versions
45
-
46
-
47
- def extract_workshop_id(key: str) -> Optional[str]:
48
- match = re.match(r"workshop-(\d+)", key)
49
- return match.group(1) if match else None
50
-
51
-
52
- def parse_lua_file(file_path: str) -> dict:
53
- with open(file_path, "r", encoding="utf-8") as f:
54
- source = f.read()
55
- source = source.strip()
56
- if source.startswith("return"):
57
- source = source[6:].strip()
58
- result = lua.decode(source)
59
- assert isinstance(result, dict)
60
- return result
61
-
62
-
63
- def write_lua_file(file_path: str, data: dict):
64
- with open(file_path, "w", encoding="utf-8") as f:
65
- f.write("return " + lua.encode(data) + "\n")
66
-
67
-
68
- def get_client_mods_list(file_path: str, mod_id: str) -> Optional[tuple[dict, dict]]:
69
- """
70
- 解析 lua 文件,返回 (data, client_mods_list)。
71
- 失败返回 None(错误信息已由函数自行打印)。
72
- """
73
- try:
74
- data = parse_lua_file(file_path)
75
- except FileNotFoundError:
76
- typer.secho(f"❌ 文件不存在: {file_path}", fg=typer.colors.RED)
77
- return None
78
- except Exception as e:
79
- typer.secho(f"❌ 解析 Lua 文件失败: {e}", fg=typer.colors.RED)
80
- return None
81
-
82
- main_mod_key = f"workshop-{mod_id}"
83
- main_mod = data.get(main_mod_key)
84
- if not main_mod:
85
- typer.secho(f"❌ 未找到 {main_mod_key}", fg=typer.colors.RED)
86
- return None
87
-
88
- client_mods_list = main_mod.get("configuration_options", {}).get(
89
- "client_mods_list", {}
90
- )
91
- if not client_mods_list:
92
- typer.secho("❌ 未找到 client_mods_list", fg=typer.colors.RED)
93
- return None
94
-
95
- return data, client_mods_list
96
-
97
-
98
- # update_client_mods
99
- @app.command()
100
- def cmu(
101
- file_path: str = typer.Argument(
102
- "d:/.backups/dontstarvetogether/modoverrides.lua",
103
- help="modoverrides.lua 文件路径",
104
- ),
105
- mod_id: str = typer.Option(
106
- "3486375086",
107
- "--mod-id",
108
- "-m",
109
- help="主 mod 的 workshop ID",
110
- ),
111
- output: List[str] = typer.Option(
112
- None,
113
- "--output",
114
- "-o",
115
- help="额外的输出路径,可多次指定",
116
- ),
117
- ):
118
- result = get_client_mods_list(file_path, mod_id)
119
- if result is None:
120
- raise typer.Exit(code=1)
121
- data, client_mods_list = result
122
-
123
- workshop_ids = []
124
- for key in client_mods_list.keys():
125
- wid = extract_workshop_id(key)
126
- if wid:
127
- workshop_ids.append(wid)
128
-
129
- if not workshop_ids:
130
- typer.secho("❌ 未找到任何 client_mods", fg=typer.colors.RED)
131
- raise typer.Exit(code=1)
132
-
133
- typer.secho(
134
- f"🔍 发现 {len(workshop_ids)} 个 client mods,正在查询 Steam API...",
135
- fg=typer.colors.BLUE,
136
- )
137
- versions = get_mod_versions(workshop_ids)
138
-
139
- if not versions:
140
- typer.secho("❌ 未能获取版本信息", fg=typer.colors.RED)
141
- raise typer.Exit(code=1)
142
-
143
- typer.secho("📝 正在更新版本号...", fg=typer.colors.BLUE)
144
- updated_count = 0
145
- for mod_key, mod_info in client_mods_list.items():
146
- wid = extract_workshop_id(mod_key)
147
- if not wid:
148
- continue
149
- mod_data = versions.get(wid)
150
- if mod_data is None:
151
- typer.secho(f"⚠️ {mod_key}: 未获取到版本信息", fg=typer.colors.YELLOW)
152
- continue
153
- new_version = mod_data.get("version")
154
- title = mod_data.get("title", "")
155
- if new_version is None:
156
- typer.secho(
157
- f"⚠️ {mod_key} ({title}): 未获取到版本信息", fg=typer.colors.YELLOW
158
- )
159
- continue
160
- old_version = mod_info.get("version")
161
- if old_version != new_version:
162
- mod_info["version"] = new_version
163
- updated_count += 1
164
- typer.secho(
165
- f"✅ {mod_key} ({title}): {old_version} -> {new_version}",
166
- fg=typer.colors.GREEN,
167
- )
168
- else:
169
- typer.secho(
170
- f"ℹ️ {mod_key} ({title}): 已是最新版本 {old_version}",
171
- fg=typer.colors.WHITE,
172
- )
173
-
174
- try:
175
- if updated_count > 0:
176
- write_lua_file(file_path, data)
177
- typer.secho(f"💾 已保存到: {file_path}", fg=typer.colors.CYAN)
178
- if output:
179
- for p in output:
180
- write_lua_file(p, data)
181
- typer.secho(f"💾 已保存到: {p}", fg=typer.colors.CYAN)
182
- except Exception as e:
183
- typer.secho(f"❌ 写入文件失败: {e}", fg=typer.colors.RED)
184
- raise typer.Exit(code=1)
185
-
186
- if updated_count > 0:
187
- typer.secho(f"\n🎉 成功更新 {updated_count} 个 mod 版本", fg=typer.colors.GREEN)
188
- else:
189
- typer.secho("\nℹ️ 所有 mod 已是最新版本", fg=typer.colors.YELLOW)
190
-
191
-
192
- # list_client_mods
193
- @app.command()
194
- def cml(
195
- file_path: str = typer.Argument(
196
- "d:/.backups/dontstarvetogether/modoverrides.lua",
197
- help="modoverrides.lua 文件路径",
198
- ),
199
- mod_id: str = typer.Option(
200
- "3486375086",
201
- "--mod-id",
202
- "-m",
203
- help="主 mod 的 workshop ID",
204
- ),
205
- ):
206
- """
207
- 列出 client_mods_list 中所有 mod 的当前版本
208
- """
209
- result = get_client_mods_list(file_path, mod_id)
210
- if result is None:
211
- raise typer.Exit(code=1)
212
- _, client_mods_list = result
213
-
214
- typer.secho("\n📋 client_mods_list 中的 mod 列表:", fg=typer.colors.BLUE)
215
- for i, (mod_key, mod_info) in enumerate(client_mods_list.items(), 1):
216
- version = mod_info.get("version", "unknown")
217
- typer.secho(f"{i}. {mod_key}: {version}", fg=typer.colors.WHITE)
218
-
219
-
220
- if __name__ == "__main__":
221
- # cmu("d:/.backups/dontstarvetogether/modoverrides.lua", "3486375086")
222
- cml("d:/.backups/dontstarvetogether/modoverrides.lua", "3486375086")
@@ -1,70 +0,0 @@
1
- mixed-port: 7897
2
- allow-lan: true
3
- mode: rule
4
- log-level: warning
5
- external-controller: "127.0.0.1:9097"
6
- client-fingerprint: chrome
7
- unified-delay: true
8
- ipv6: true
9
- profile:
10
- store-selected: true
11
- tun:
12
- mtu: 1500
13
- dns:
14
- enable: true
15
- listen: ":53"
16
- enhanced-mode: "fake-ip"
17
- fake-ip-range: "198.18.0.1/16"
18
- fake-ip-filter-mode: "blacklist"
19
- prefer-h3: false
20
- respect-rules: false
21
- use-hosts: false
22
- use-system-hosts: false
23
- ipv6: true
24
- fake-ip-filter:
25
- - "*.lan"
26
- - "*.local"
27
- - "*.arpa"
28
- - "time.*.com"
29
- - "ntp.*.com"
30
- - "time.*.com"
31
- - "+.market.xiaomi.com"
32
- - "localhost.ptlogin2.qq.com"
33
- - "*.msftncsi.com"
34
- - "www.msftconnecttest.com"
35
- default-nameserver:
36
- - "system"
37
- - "223.6.6.6"
38
- - "8.8.8.8"
39
- - "2400:3200::1"
40
- - "2001:4860:4860::8888"
41
- nameserver:
42
- - "8.8.8.8"
43
- - "https://doh.pub/dns-query"
44
- - "https://dns.alidns.com/dns-query"
45
- direct-nameserver-follow-policy: false
46
- fallback-filter:
47
- geoip: true
48
- geoip-code: "CN"
49
- ipcidr:
50
- - "240.0.0.0/4"
51
- - "0.0.0.0/32"
52
- domain:
53
- - "+.google.com"
54
- - "+.facebook.com"
55
- - "+.youtube.com"
56
- fallback: []
57
- proxy-server-nameserver:
58
- - "https://doh.pub/dns-query"
59
- - "https://dns.alidns.com/dns-query"
60
- - "tls://223.5.5.5"
61
- direct-nameserver: []
62
- external-controller-cors:
63
- allow-private-network: true
64
- allow-origins:
65
- - "*"
66
- proxies: []
67
- proxy-providers: {}
68
- proxy-groups: []
69
- rule-providers: {}
70
- rules: []
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes