wechat-cli 0.1.0__py3-none-any.whl
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.
- wechat_cli/__init__.py +3 -0
- wechat_cli/__main__.py +3 -0
- wechat_cli/assets/app.js +79 -0
- wechat_cli/assets/bridge.cpp +643 -0
- wechat_cli/assets/controls.swift +218 -0
- wechat_cli/assets/entitlements.plist +38 -0
- wechat_cli/assets/favicon.svg +1 -0
- wechat_cli/assets/index.html +10 -0
- wechat_cli/assets/native-agent.js +334 -0
- wechat_cli/assets/pdf-thumbnail.swift +13 -0
- wechat_cli/assets/profile.json +232 -0
- wechat_cli/assets/profiles/4.1.13-269631.json +182 -0
- wechat_cli/assets/profiles/4.1.13.34-269602.json +232 -0
- wechat_cli/assets/profiles/4.1.15-270135.json +194 -0
- wechat_cli/assets/startup-notice.js +78 -0
- wechat_cli/assets/style.css +1 -0
- wechat_cli/cli.py +238 -0
- wechat_cli/client.py +59 -0
- wechat_cli/codec.py +14 -0
- wechat_cli/config.py +84 -0
- wechat_cli/controls.py +223 -0
- wechat_cli/errors.py +7 -0
- wechat_cli/idempotency.py +82 -0
- wechat_cli/lifecycle.py +141 -0
- wechat_cli/media.py +341 -0
- wechat_cli/models.py +260 -0
- wechat_cli/native.py +229 -0
- wechat_cli/network.py +9 -0
- wechat_cli/server.py +518 -0
- wechat_cli/setup.py +342 -0
- wechat_cli/worker.py +227 -0
- wechat_cli-0.1.0.dist-info/METADATA +178 -0
- wechat_cli-0.1.0.dist-info/RECORD +36 -0
- wechat_cli-0.1.0.dist-info/WHEEL +4 -0
- wechat_cli-0.1.0.dist-info/entry_points.txt +2 -0
- wechat_cli-0.1.0.dist-info/licenses/LICENSE +7 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wechat-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Local Python CLI, REST API and web interface for macOS WeChat
|
|
5
|
+
License-Expression: ISC
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Environment :: Console
|
|
9
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Requires-Dist: cryptography<49,>=44
|
|
14
|
+
Requires-Dist: frida==17.8.0
|
|
15
|
+
Requires-Dist: pilk<0.3,>=0.2.4
|
|
16
|
+
Requires-Dist: pillow<13,>=11
|
|
17
|
+
Requires-Dist: truststore<1,>=0.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# wechat-cli
|
|
21
|
+
|
|
22
|
+
A Python CLI, authenticated local REST API, Python client and web interface for a signed-in macOS WeChat session. Node.js and npm are not required. `uvx` installs Python dependencies, including Frida and the SILK codec, into its own environment.
|
|
23
|
+
|
|
24
|
+
## Quick start
|
|
25
|
+
|
|
26
|
+
After publication to PyPI:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
uvx wechat-cli setup
|
|
30
|
+
uvx wechat-cli start
|
|
31
|
+
uvx wechat-cli chats
|
|
32
|
+
uvx wechat-cli web
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`setup` downloads WeChat 4.1.15 from Tencent and Frida Gadget 17.8.0 from the upstream Frida release. It verifies the Gadget archive's SHA-256, the downloaded app's Tencent signature and its supported payload fingerprint, mounts the installer read-only, and prepares a separate app in:
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
~/Library/Application Support/wechat-cli/apps/WeChatBridge.app
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
It adds Gadget, builds the bundled C++ helper, signs the copy, and records its location. `/Applications/WeChat.app` is not modified. No WeChat app, Gadget binary, credentials or account data are redistributed in the Python package.
|
|
42
|
+
|
|
43
|
+
Requires **Apple Silicon macOS**, **Python 3.11+** (managed by uv when necessary), **uv**, and **Xcode command-line tools** (`xcode-select --install`). The native helper and SILK codec require a compiler. Setup runs as your normal user; do not use `sudo uvx`.
|
|
44
|
+
|
|
45
|
+
On CLI startup, the bridge automatically accepts WeChat's exact **“opened from the installation package”** location notice with its **“Do not show this message again”** box selected. This policy is installed before the app resumes; setup itself does not need Accessibility permission to configure it.
|
|
46
|
+
|
|
47
|
+
Quit other WeChat copies before starting the prepared app. Sign in through its normal WeChat window if needed. `start` launches that app and a persistent Python service. Later commands call the service and reuse its session; they do not log in again. `web` also starts the service if needed, then opens its local page. `stop` stops the Python service and leaves WeChat running and signed in.
|
|
48
|
+
|
|
49
|
+
## Supported payloads
|
|
50
|
+
|
|
51
|
+
| WeChat version | Build | Installer |
|
|
52
|
+
| --- | --- | --- |
|
|
53
|
+
| 4.1.13.34 | 269602 | Supply an existing compatible app with `--source` or `--app` |
|
|
54
|
+
| 4.1.13 | 269631 | Tencent's current `WeChatMac_4.1.13.dmg` |
|
|
55
|
+
| 4.1.15 | 270135 | Tencent's current `WeChatMac_4.1.15.dmg` (default) |
|
|
56
|
+
|
|
57
|
+
Profiles are selected by the entire vendor payload's SHA-256, with native function signatures checked before use. Installer filenames alone do not establish compatibility. Tencent can replace a download without changing its filename. Future payloads require a new validated profile and package release; unknown payloads are rejected before patching. To get the newest package, use `uvx --refresh wechat-cli setup`.
|
|
58
|
+
|
|
59
|
+
To prepare the current 4.1.13 download:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
uvx wechat-cli setup \
|
|
63
|
+
--wechat-url https://dldir1v6.qq.com/weixin/Universal/Mac/WeChatMac_4.1.13.dmg \
|
|
64
|
+
--destination "$HOME/Applications/WeChatBridge-4.1.13.app"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
To use a local installer app or an already patched copy:
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
uvx wechat-cli setup --source /Applications/WeChat.app
|
|
71
|
+
uvx wechat-cli setup --app /absolute/path/to/WeChatBridge.app
|
|
72
|
+
uvx wechat-cli doctor
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`--source` still requires a supported payload. `--gadget /path/to/FridaGadget.dylib` uses a locally supplied Gadget instead of downloading it. `--destination` must be a new path outside the source app. Setup never overwrites an existing app; `--app` registers and rebuilds an existing patched copy. Repeated default setup reuses the previously prepared copy.
|
|
76
|
+
|
|
77
|
+
## Commands
|
|
78
|
+
|
|
79
|
+
All results are JSON on stdout. Errors and generated idempotency keys go to stderr. Exit status is 0 for success, 1 for operation errors, and 2 for invalid CLI syntax. Global `--data-dir` and `--port` options work before or after a subcommand.
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
uvx wechat-cli start # background service + patched app
|
|
83
|
+
uvx wechat-cli start --foreground # foreground service
|
|
84
|
+
uvx wechat-cli serve # same foreground behavior
|
|
85
|
+
uvx wechat-cli status
|
|
86
|
+
uvx wechat-cli chats
|
|
87
|
+
uvx wechat-cli messages filehelper --limit 20
|
|
88
|
+
uvx wechat-cli send filehelper 'Hello' --key demo-message-001
|
|
89
|
+
uvx wechat-cli send filehelper 'Validate only' --dry-run
|
|
90
|
+
uvx wechat-cli send-file filehelper ./report.pdf --key demo-file-001
|
|
91
|
+
uvx wechat-cli send-voice filehelper ./voice.wav --key demo-voice-001
|
|
92
|
+
uvx wechat-cli send-voice filehelper ./voice.silk --format silk
|
|
93
|
+
uvx wechat-cli send-voice filehelper ./voice.pcm --format pcm_s16le --sample-rate 24000
|
|
94
|
+
uvx wechat-cli media CHAT_ID MESSAGE_ID --output ./attachment.bin
|
|
95
|
+
uvx wechat-cli download CHAT_ID MESSAGE_ID # download through the active native chat
|
|
96
|
+
uvx wechat-cli ocr CHAT_ID MESSAGE_ID
|
|
97
|
+
uvx wechat-cli transcribe CHAT_ID MESSAGE_ID --key demo-transcription-001
|
|
98
|
+
uvx wechat-cli transcript CHAT_ID MESSAGE_ID
|
|
99
|
+
uvx wechat-cli web
|
|
100
|
+
uvx wechat-cli web --no-open
|
|
101
|
+
uvx wechat-cli stop
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Use the exact **string** message IDs returned by `messages`; some exceed JavaScript's precise integer range. History is limited to the latest 100 records per request. `media --output` refuses to overwrite an existing file. Voice input supports SILK, mono 16-bit PCM WAV and raw signed little-endian 16-bit PCM, lasting 1–60 seconds. CLI file/audio uploads are limited to 8 MiB. File sending uses WeChat's UI and requires Accessibility permission for the launching terminal, a uniquely named target chat open in WeChat, and an empty draft.
|
|
105
|
+
|
|
106
|
+
`send --dry-run` constructs and verifies a native message without queuing it. Real send receipts distinguish acceptance by the local client from server acknowledgement; a queued message is not a delivery guarantee.
|
|
107
|
+
|
|
108
|
+
Each send gets an idempotency key, printed before the API call. **Reuse the same `--key` and the same content when retrying.** The private SQLite ledger retains results for 24 hours, including uncertain outcomes, across service restarts. An interrupted attempt is not automatically resent. Inspect WeChat history before retrying an uncertain send with another key. Keys are scoped to the active account and operation.
|
|
109
|
+
|
|
110
|
+
OCR uses WeChat's local recognizer. `transcribe` explicitly initiates WeChat voice recognition, which can use WeChat's servers; `transcript` only reads existing recognition state. Downloading uncached stickers can contact supported Tencent CDNs. Native logout, independent QR login, legacy Web WeChat and static reverse-engineering catalogs are not part of this Python gateway.
|
|
111
|
+
|
|
112
|
+
## REST API and Python client
|
|
113
|
+
|
|
114
|
+
The default server is `http://127.0.0.1:8787`. It only binds to loopback. Every API request requires the token in:
|
|
115
|
+
|
|
116
|
+
```text
|
|
117
|
+
~/Library/Application Support/wechat-cli/api-token
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The browser obtains a local, HttpOnly, SameSite=Strict session after a same-origin check. Browser sessions never expose the API token. No permissive CORS headers are sent. Runtime files and tokens remain outside uv's disposable package cache.
|
|
121
|
+
|
|
122
|
+
| CLI command | HTTP API |
|
|
123
|
+
| --- | --- |
|
|
124
|
+
| `status` | `GET /api/native/status` |
|
|
125
|
+
| `chats` | `GET /api/native/chats` |
|
|
126
|
+
| `messages CHAT_ID` | `GET /api/native/messages?chatId=…&limit=50` |
|
|
127
|
+
| `send` | `POST /api/messages/send` |
|
|
128
|
+
| `send --dry-run` | `POST /api/messages/prepare` |
|
|
129
|
+
| `send-file` / `send-voice` | `POST /api/messages/file` / `/api/messages/voice` |
|
|
130
|
+
| `media` | `GET /api/native/media?chatId=…&id=…&variant=full` |
|
|
131
|
+
| `download` | `POST /api/native/attachments/download` |
|
|
132
|
+
| `ocr` | `POST /api/native/images/text` |
|
|
133
|
+
| `transcribe` / `transcript` | `POST /api/native/voice/transcribe` / `GET /api/native/voice/transcript` |
|
|
134
|
+
| `stop` | `POST /api/server/stop` |
|
|
135
|
+
|
|
136
|
+
The service also provides `/api/health`, `/api/capabilities`, `/api/openapi.json`, `/api/native/controls`, and the `/api/chats` and `/api/messages` aliases. The generic `api` command can call any route:
|
|
137
|
+
|
|
138
|
+
```sh
|
|
139
|
+
uvx wechat-cli api /api/health
|
|
140
|
+
uvx wechat-cli api /api/messages/send --method POST \
|
|
141
|
+
--json '{"chatId":"filehelper","text":"Hello"}' --key demo-message-002
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from wechat_cli.client import ApiClient
|
|
146
|
+
from wechat_cli.config import data_directory
|
|
147
|
+
|
|
148
|
+
client = ApiClient(data_directory())
|
|
149
|
+
chats = client.chats()
|
|
150
|
+
history = client.messages("filehelper", limit=20)
|
|
151
|
+
receipt = client.send("filehelper", "Hello", idempotency_key="python-demo-001")
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Select a separate service or port with `--data-dir /absolute/path --port 8788`. A service keeps running until `stop`, Ctrl-C in foreground mode, or system shutdown. Stop it before deleting uv caches or removing the installed package environment. Setup/doctor are local administration operations; chat/media commands go through HTTP.
|
|
155
|
+
|
|
156
|
+
## Development and release
|
|
157
|
+
|
|
158
|
+
From this folder:
|
|
159
|
+
|
|
160
|
+
```sh
|
|
161
|
+
uv sync --group dev
|
|
162
|
+
uv run pytest
|
|
163
|
+
uv run ruff check src tests
|
|
164
|
+
uv run ruff format --check src tests
|
|
165
|
+
uv build --no-sources
|
|
166
|
+
uv run twine check dist/*
|
|
167
|
+
uvx --from ./dist/wechat_cli-0.1.0-py3-none-any.whl wechat-cli --help
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Before publication, run the built wheel from outside the repository and execute the opt-in read-only live smoke check with a signed-in prepared app. See [validation/README.md](validation/README.md) for the recorded checks and limitations. Tests never send real messages.
|
|
171
|
+
|
|
172
|
+
Publish the reviewed artifacts with your PyPI credentials or a configured trusted publisher:
|
|
173
|
+
|
|
174
|
+
```sh
|
|
175
|
+
uv publish dist/wechat_cli-0.1.0-py3-none-any.whl dist/wechat_cli-0.1.0.tar.gz
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
The distribution name and console entry point are both **`wechat-cli`**; the import name is `wechat_cli`. Name availability is not a reservation. Publication is a separate step and is not performed by setup or the test suite. The Python server is independent of the existing `wechat-node` implementation.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
wechat_cli/__init__.py,sha256=-jcSAgUZzK0P4R9Hv_aWUh4e2HivD6_5UQX_h5-pJGQ,101
|
|
2
|
+
wechat_cli/__main__.py,sha256=k1ocEWawweo1qCJWNFAAvyxz3tcY13dzvCenHszij30,48
|
|
3
|
+
wechat_cli/cli.py,sha256=ml0evKYTkK8XRRCTjpokvXz6iTbCqslhEoBIO2pDTBA,10443
|
|
4
|
+
wechat_cli/client.py,sha256=ay0mlJwOXnnUjwLyp4iUuqujCAb6WYW7ksCR3gJ-7N8,2585
|
|
5
|
+
wechat_cli/codec.py,sha256=O-DApwf5lufdRjuIrFgD-Wmx7SzLIpy-bC06Xo43UD8,420
|
|
6
|
+
wechat_cli/config.py,sha256=JkPwZjjSzbyO_QGP8AkYuHWW2xlfgIJ_DTJYEN6pbNs,2533
|
|
7
|
+
wechat_cli/controls.py,sha256=Fo80-ucmIfL13NghXEHoSa0pIebBWgpur81GrIeiUUg,9801
|
|
8
|
+
wechat_cli/errors.py,sha256=CBAtE5RyfKCTc4L001c8Rvg5TVxcHR0Y9-_NnfsF28o,257
|
|
9
|
+
wechat_cli/idempotency.py,sha256=vcBJu4hxJ0Ji0fllXbH7JAtWzooMcpCra3vWGrPKdzM,3397
|
|
10
|
+
wechat_cli/lifecycle.py,sha256=efp6PelGUrVQijIR8HYUmfrm3EYrm0Oc4FjgjdssLXs,5052
|
|
11
|
+
wechat_cli/media.py,sha256=4iP_tSq_wOBdkHR6NBGg44qDQpFGQaL5-_R4tWWUAqE,13753
|
|
12
|
+
wechat_cli/models.py,sha256=byI415qn3EdC11gJigZD_5uWUlFSLC3KArpoN09hmGs,10128
|
|
13
|
+
wechat_cli/native.py,sha256=sQ10IU7DIKWB_UD-dN3BL_IJv5k-TmMiT2SJtZyDExc,9955
|
|
14
|
+
wechat_cli/network.py,sha256=R0FlGuX8ID5xewQqtrtEOpF6qckMZehzD5CuebeEcfg,203
|
|
15
|
+
wechat_cli/server.py,sha256=nXO4_C5uakfLxYGDJM3ndZdEGMnObfFeSmWWO00z6EA,23054
|
|
16
|
+
wechat_cli/setup.py,sha256=H4IVAiiFAZreBKpy8LbzHkRzcGjDaEwitkdw-Gn1ua8,14601
|
|
17
|
+
wechat_cli/worker.py,sha256=2ye4nJ2vAIp5LCd4k-BqeCuvnDog39IpjCgbYtjaxms,10373
|
|
18
|
+
wechat_cli/assets/app.js,sha256=mLmlWEjXKtSu550ZCZ_Sjm8A36xJ3cNMqrqpkrCp0KI,6836
|
|
19
|
+
wechat_cli/assets/bridge.cpp,sha256=TePKwRa-qr9im1fdz8ranRbF9HE03YqXZFQ1_zMWBIE,50147
|
|
20
|
+
wechat_cli/assets/controls.swift,sha256=TtVxhvWRiJNlZzzKl2fUtwi5POcXlYzlxTC3dWPo2zs,17217
|
|
21
|
+
wechat_cli/assets/entitlements.plist,sha256=9lNvCf9d_XM2_HvMXXm8u18hgdux3rvFCBeqs_YU8lY,1158
|
|
22
|
+
wechat_cli/assets/favicon.svg,sha256=f79mzhPUFZ0cu8171yvmScy0uxHPOLI4rU_KfYXjyDo,287
|
|
23
|
+
wechat_cli/assets/index.html,sha256=6-idVM3Vqr9HvPmp8pQbu7M-FlxBgO4uMJOTp9f1lr4,1743
|
|
24
|
+
wechat_cli/assets/native-agent.js,sha256=nY1IbQiSE2Of1kfjjLUOtvR-NT3-bdCdwEQ4PwJd01o,25796
|
|
25
|
+
wechat_cli/assets/pdf-thumbnail.swift,sha256=wP0q-dF6k_hLs16J997ghkReUnZUYALU7DIs1g2r6ew,773
|
|
26
|
+
wechat_cli/assets/profile.json,sha256=TAVjfd3Dj3m9-wYh2bhV0vfhKDC4qWZb0tvPOhwUo78,7833
|
|
27
|
+
wechat_cli/assets/startup-notice.js,sha256=aK9IDbiq0WmtNek2D9RO58d0UoXBU9T_LjicCQvP7rw,4669
|
|
28
|
+
wechat_cli/assets/style.css,sha256=zc3n3xBTS_9Y0l3tdGuMQ7r56l7J64hO4crLQktIZQY,2457
|
|
29
|
+
wechat_cli/assets/profiles/4.1.13-269631.json,sha256=vhNCdDP1GNO338tcXUW1DmUohfskVMx8r4sCaUj6Uik,7102
|
|
30
|
+
wechat_cli/assets/profiles/4.1.13.34-269602.json,sha256=TAVjfd3Dj3m9-wYh2bhV0vfhKDC4qWZb0tvPOhwUo78,7833
|
|
31
|
+
wechat_cli/assets/profiles/4.1.15-270135.json,sha256=p2iTZwbMwuriUaJek17gMx2IpB0tREiFXqlOIbgtp6g,7468
|
|
32
|
+
wechat_cli-0.1.0.dist-info/METADATA,sha256=YCBIRUuAsDEBS4QBpiLI6hVjShnNOP4X8nkn9jNvnts,10157
|
|
33
|
+
wechat_cli-0.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
34
|
+
wechat_cli-0.1.0.dist-info/entry_points.txt,sha256=NBm8ozvV3By3USA4UsOVb1kmAK349gpYFgU1tnttDys,51
|
|
35
|
+
wechat_cli-0.1.0.dist-info/licenses/LICENSE,sha256=2ax9i1_wyMBQFDkeDoVYspHJt6_8XONeSzLPMRWvqxs,754
|
|
36
|
+
wechat_cli-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 wechat-cli contributors
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|