tgcaptcha-solver 0.1.0__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 (49) hide show
  1. tgcaptcha_solver-0.1.0/LICENSE +21 -0
  2. tgcaptcha_solver-0.1.0/PKG-INFO +387 -0
  3. tgcaptcha_solver-0.1.0/README.md +324 -0
  4. tgcaptcha_solver-0.1.0/pyproject.toml +49 -0
  5. tgcaptcha_solver-0.1.0/setup.cfg +4 -0
  6. tgcaptcha_solver-0.1.0/tgcaptcha/__init__.py +29 -0
  7. tgcaptcha_solver-0.1.0/tgcaptcha/adapters/__init__.py +25 -0
  8. tgcaptcha_solver-0.1.0/tgcaptcha/adapters/pyrogram.py +508 -0
  9. tgcaptcha_solver-0.1.0/tgcaptcha/adapters/tdlib.py +684 -0
  10. tgcaptcha_solver-0.1.0/tgcaptcha/client.py +87 -0
  11. tgcaptcha_solver-0.1.0/tgcaptcha/engine.py +122 -0
  12. tgcaptcha_solver-0.1.0/tgcaptcha/executor.py +453 -0
  13. tgcaptcha_solver-0.1.0/tgcaptcha/hands.py +123 -0
  14. tgcaptcha_solver-0.1.0/tgcaptcha/model.py +245 -0
  15. tgcaptcha_solver-0.1.0/tgcaptcha/policy.py +254 -0
  16. tgcaptcha_solver-0.1.0/tgcaptcha/replay.py +156 -0
  17. tgcaptcha_solver-0.1.0/tgcaptcha/rules.py +184 -0
  18. tgcaptcha_solver-0.1.0/tgcaptcha/runner.py +312 -0
  19. tgcaptcha_solver-0.1.0/tgcaptcha/server.py +188 -0
  20. tgcaptcha_solver-0.1.0/tgcaptcha/solvers/__init__.py +50 -0
  21. tgcaptcha_solver-0.1.0/tgcaptcha/solvers/bio.py +180 -0
  22. tgcaptcha_solver-0.1.0/tgcaptcha/solvers/builtin.py +162 -0
  23. tgcaptcha_solver-0.1.0/tgcaptcha/solvers/dm.py +171 -0
  24. tgcaptcha_solver-0.1.0/tgcaptcha/solvers/emoji.py +119 -0
  25. tgcaptcha_solver-0.1.0/tgcaptcha/solvers/gates.py +287 -0
  26. tgcaptcha_solver-0.1.0/tgcaptcha/solvers/image.py +267 -0
  27. tgcaptcha_solver-0.1.0/tgcaptcha/solvers/image_digits.py +216 -0
  28. tgcaptcha_solver-0.1.0/tgcaptcha/solvers/image_text_pick.py +379 -0
  29. tgcaptcha_solver-0.1.0/tgcaptcha/solvers/ocr.py +153 -0
  30. tgcaptcha_solver-0.1.0/tgcaptcha/solvers/qa.py +127 -0
  31. tgcaptcha_solver-0.1.0/tgcaptcha/solvers/rose.py +334 -0
  32. tgcaptcha_solver-0.1.0/tgcaptcha/solvers/success.py +70 -0
  33. tgcaptcha_solver-0.1.0/tgcaptcha/solvers/video.py +204 -0
  34. tgcaptcha_solver-0.1.0/tgcaptcha/tests/__init__.py +0 -0
  35. tgcaptcha_solver-0.1.0/tgcaptcha/tests/test_bio.py +431 -0
  36. tgcaptcha_solver-0.1.0/tgcaptcha/tests/test_core.py +961 -0
  37. tgcaptcha_solver-0.1.0/tgcaptcha/tests/test_image_digits.py +263 -0
  38. tgcaptcha_solver-0.1.0/tgcaptcha/tests/test_image_pick.py +353 -0
  39. tgcaptcha_solver-0.1.0/tgcaptcha/tests/test_pyro_adapter.py +498 -0
  40. tgcaptcha_solver-0.1.0/tgcaptcha/tests/test_runner.py +187 -0
  41. tgcaptcha_solver-0.1.0/tgcaptcha/tests/test_success.py +117 -0
  42. tgcaptcha_solver-0.1.0/tgcaptcha/tests/test_tdlib_adapter.py +737 -0
  43. tgcaptcha_solver-0.1.0/tgcaptcha/tests/test_video.py +127 -0
  44. tgcaptcha_solver-0.1.0/tgcaptcha/text.py +398 -0
  45. tgcaptcha_solver-0.1.0/tgcaptcha_solver.egg-info/PKG-INFO +387 -0
  46. tgcaptcha_solver-0.1.0/tgcaptcha_solver.egg-info/SOURCES.txt +47 -0
  47. tgcaptcha_solver-0.1.0/tgcaptcha_solver.egg-info/dependency_links.txt +1 -0
  48. tgcaptcha_solver-0.1.0/tgcaptcha_solver.egg-info/requires.txt +47 -0
  49. tgcaptcha_solver-0.1.0/tgcaptcha_solver.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 drshta0
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,387 @@
1
+ Metadata-Version: 2.4
2
+ Name: tgcaptcha-solver
3
+ Version: 0.1.0
4
+ Summary: Telegram captcha solver for Rose, GroupHelp, Shieldy and SOSO group-join bots, with a strict safety policy.
5
+ Author: drshta0
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/drshta0/tgcaptcha-solver
8
+ Project-URL: Repository, https://github.com/drshta0/tgcaptcha-solver
9
+ Project-URL: Issues, https://github.com/drshta0/tgcaptcha-solver/issues
10
+ Project-URL: Changelog, https://github.com/drshta0/tgcaptcha-solver/blob/main/CHANGELOG.md
11
+ Project-URL: Documentation, https://github.com/drshta0/tgcaptcha-solver/blob/main/docs/USAGE.md
12
+ Keywords: telegram,captcha,captcha-solver,telegram-captcha,telethon,userbot,missrose,rose-bot,grouphelp,shieldy,ocr,automation
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Communications :: Chat
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Provides-Extra: client
26
+ Requires-Dist: aiohttp>=3.9; extra == "client"
27
+ Provides-Extra: executor
28
+ Requires-Dist: telethon>=1.36; extra == "executor"
29
+ Requires-Dist: aiohttp>=3.9; extra == "executor"
30
+ Provides-Extra: kurigram
31
+ Requires-Dist: kurigram>=2.0; extra == "kurigram"
32
+ Requires-Dist: aiohttp>=3.9; extra == "kurigram"
33
+ Provides-Extra: telethon
34
+ Requires-Dist: telethon>=1.36; extra == "telethon"
35
+ Requires-Dist: aiohttp>=3.9; extra == "telethon"
36
+ Provides-Extra: hydrogram
37
+ Requires-Dist: hydrogram>=0.2; extra == "hydrogram"
38
+ Requires-Dist: aiohttp>=3.9; extra == "hydrogram"
39
+ Provides-Extra: tdlib
40
+ Requires-Dist: aiotdlib<0.28,>=0.27.6; extra == "tdlib"
41
+ Requires-Dist: aiohttp>=3.9; extra == "tdlib"
42
+ Provides-Extra: server
43
+ Requires-Dist: fastapi>=0.110; extra == "server"
44
+ Requires-Dist: uvicorn>=0.29; extra == "server"
45
+ Requires-Dist: PyYAML>=6.0; extra == "server"
46
+ Requires-Dist: Pillow>=10.0; extra == "server"
47
+ Provides-Extra: ocr
48
+ Requires-Dist: ddddocr>=1.4; extra == "ocr"
49
+ Provides-Extra: test
50
+ Requires-Dist: aiohttp>=3.9; extra == "test"
51
+ Requires-Dist: fastapi>=0.110; extra == "test"
52
+ Requires-Dist: uvicorn>=0.29; extra == "test"
53
+ Requires-Dist: PyYAML>=6.0; extra == "test"
54
+ Requires-Dist: Pillow>=10.0; extra == "test"
55
+ Provides-Extra: all
56
+ Requires-Dist: aiohttp>=3.9; extra == "all"
57
+ Requires-Dist: telethon>=1.36; extra == "all"
58
+ Requires-Dist: fastapi>=0.110; extra == "all"
59
+ Requires-Dist: uvicorn>=0.29; extra == "all"
60
+ Requires-Dist: PyYAML>=6.0; extra == "all"
61
+ Requires-Dist: Pillow>=10.0; extra == "all"
62
+ Dynamic: license-file
63
+
64
+ <p align="center">
65
+ <img src="https://raw.githubusercontent.com/drshta0/tgcaptcha-solver/main/docs/media/banner.svg" width="100%" alt="tgcaptcha: open-source Telegram captcha solver for Rose, GroupHelp, Shieldy and SOSO" />
66
+ </p>
67
+
68
+ <p align="center">
69
+ <a href="#install"><img src="https://img.shields.io/badge/python-3.10%2B-2aabee?style=flat-square" alt="Python 3.10+" /></a>
70
+ <a href="#install"><img src="https://img.shields.io/badge/core%20deps-0-3fb950?style=flat-square" alt="Zero core dependencies" /></a>
71
+ <a href="#tests"><img src="https://img.shields.io/badge/tests-384%20offline-3fb950?style=flat-square" alt="384 offline checks" /></a>
72
+ <a href="https://github.com/drshta0/tgcaptcha-solver/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-8b949e?style=flat-square" alt="MIT" /></a>
73
+ </p>
74
+
75
+ <p align="center">
76
+ <a href="#install">Install</a> &nbsp;·&nbsp;
77
+ <a href="#quickstart">Quickstart</a> &nbsp;·&nbsp;
78
+ <a href="#works-with">Works with</a> &nbsp;·&nbsp;
79
+ <a href="#all-solvers">Solvers</a> &nbsp;·&nbsp;
80
+ <a href="#safety">Safety</a> &nbsp;·&nbsp;
81
+ <a href="https://github.com/drshta0/tgcaptcha-solver/blob/main/docs/USAGE.md">Docs</a> &nbsp;·&nbsp;
82
+ <a href="https://github.com/drshta0/tgcaptcha-solver/blob/main/CONTRIBUTING.md">Contributing</a>
83
+ </p>
84
+
85
+ <br />
86
+
87
+ **tgcaptcha** is an open-source Telegram captcha solver. When your account
88
+ joins a group, bots like **Rose**, **GroupHelp**, **Shieldy** and **SOSO 守门**
89
+ post a captcha: press a button, solve a sum, read a picture. tgcaptcha reads
90
+ it, works out the answer, checks the action against a strict safety policy,
91
+ and replies after a human-length pause. It runs on any Telegram client
92
+ framework: adapters ship for Telethon, the Pyrogram family (pyrogram,
93
+ kurigram, pyrofork, hydrogram) and TDLib hosts via aiotdlib, and any
94
+ other framework needs about 60 lines — see
95
+ [docs/ADAPTERS.md](https://github.com/drshta0/tgcaptcha-solver/blob/main/docs/ADAPTERS.md).
96
+
97
+ <p align="center">
98
+ <img src="https://raw.githubusercontent.com/drshta0/tgcaptcha-solver/main/docs/media/demo.svg" width="100%" alt="Animated demo: tgcaptcha solves GroupHelp, SOSO and Rose captchas, then refuses to click a scam wallet gate" />
99
+ </p>
100
+
101
+ ## Works with
102
+
103
+ Replayed against 30 days of real challenges from each bot. "Handled" counts
104
+ solved challenges plus Rose welcome walls that are correctly skipped.
105
+
106
+ | Bot | What it asks | Handled | Real-account test |
107
+ |:--|:--|--:|:--|
108
+ | **Rose** `@MissRose_bot` | sum, word or letters in a distorted image, sent in DM | **99.8%** of 4,456 | ✅ all captcha modes |
109
+ | **GroupHelp** `@GroupHelpBot` | press "I'm not a bot" / "Unmute me" | **94.8%** of 2,525 | ✅ 3 of 3 passed |
110
+ | **Shieldy** `@shieldy_bot` | type the answer to a sum, or digits from an image | **97.3%** of 2,019 | |
111
+ | **SOSO 守门** and relatives | pick the answer to `6 + 5 = ?` | **99.6%** of 1,808 | |
112
+ | **Bio guards** `@group_protection_pro_bot` | remove the link from your bio, press Recheck | **100%** of 1,863 | ✅ end to end |
113
+ | **GroupHelp clones** (ghclone2 to 5, im_grouphelp) | same as GroupHelp | 80 to 100% | |
114
+ | **Rose clones** (missrose2, rose_rosebot) | same as Rose | 97 to 99.5% | |
115
+
116
+ The busiest gate bots in the data (nnai8688, nmnmfun, coolestbot) are all
117
+ above 99.5%. New clones usually work without changes, because solvers match
118
+ the shape of a challenge rather than the bot's name.
119
+
120
+ ## Results
121
+
122
+ Measured by replaying 30 days of real traffic (167,198 challenges from 1,502
123
+ gate bots) through the same engine that runs live. No network needed.
124
+
125
+ | Outcome | Share | Challenges | Notes |
126
+ |:--|--:|--:|:--|
127
+ | Solved | **86.9%** | 145,252 | 15 built-in solvers |
128
+ | Ignored on purpose | 9.5% | 15,847 | scam buttons and welcome walls, never clicked |
129
+ | Unsolved | 3.6% | 6,099 | video or sticker captchas, images that were never captured |
130
+
131
+ Answer timing matters. A correct answer about 2 s after the challenge still got
132
+ accounts kicked. Waiting 8 to 22 s turned most of those kicks into passes.
133
+
134
+ ## Install
135
+
136
+ Python 3.10 or newer. The distribution is `tgcaptcha-solver`; the import is
137
+ still `tgcaptcha`. The core (engine, rules, solvers, policy, replay) uses only
138
+ the standard library. Each side installs its own extras:
139
+
140
+ ```bash
141
+ pip install -e ".[kurigram]" # Pyrogram-family accounts (kurigram / pyrofork)
142
+ pip install -e ".[hydrogram]" # hydrogram accounts (auto-detected family)
143
+ pip install -e ".[tdlib]" # TDLib hosts via aiotdlib (Linux/macOS-arm wheels)
144
+ pip install -e ".[telethon]" # Telethon accounts
145
+ pip install -e ".[server]" # solver service: fastapi, uvicorn, yaml, pillow
146
+ pip install -e ".[client]" # talk to the service from the account host: aiohttp
147
+ pip install -e ".[executor]" # Telethon-side executor: telethon, aiohttp
148
+ pip install ddddocr # optional OCR for the image solvers
149
+ ```
150
+
151
+ `image_buttons` uses the `tesseract` binary. `rose_dm`, `image_text_pick` and
152
+ `image_digits_typed` use `ddddocr`. A solver skips itself when its OCR reader
153
+ is missing, so both are optional.
154
+
155
+ ## 60-second setup (one account, no server)
156
+
157
+ The engine runs locally on your own machine — nothing to host, no token, no
158
+ config file. Mount it on your client and it watches every group you are in
159
+ for a captcha challenge that names you, solves it, and touches nothing else.
160
+
161
+ **Pyrogram family** (pyrogram, kurigram, pyrofork, hydrogram):
162
+
163
+ ```python
164
+ from pyrogram import Client
165
+ from tgcaptcha.adapters import mount
166
+
167
+ app = Client("myaccount")
168
+ mount(app)
169
+ app.run()
170
+ ```
171
+
172
+ **Telethon:**
173
+
174
+ ```python
175
+ from telethon import TelegramClient
176
+ from tgcaptcha.executor import mount_telethon
177
+
178
+ client = TelegramClient("myaccount", api_id, api_hash)
179
+ mount_telethon(client)
180
+ await client.start()
181
+ ```
182
+
183
+ **TDLib (aiotdlib):**
184
+
185
+ ```python
186
+ from aiotdlib import Client
187
+ from tgcaptcha.adapters.tdlib import mount as mount_tdlib
188
+
189
+ client = Client(api_id, api_hash, database_encryption_key="...")
190
+ mount_tdlib(client)
191
+ await client.idle()
192
+ ```
193
+
194
+ Linux and macOS-arm64 wheels bundle libtdjson (nothing to compile);
195
+ elsewhere TDLib must be built first. Upstream aiotdlib is frozen at
196
+ TDLib 1.8.46 — see [docs/ADAPTERS.md](https://github.com/drshta0/tgcaptcha-solver/blob/main/docs/ADAPTERS.md) for the
197
+ caveats, including the URL-button lane degradation.
198
+
199
+ **Any other framework:** implement nine async methods (click, send text,
200
+ send a sticker, fetch an image, read/write your bio, open a bot DM, join
201
+ a channel, set a last name) and feed them to the shared loop — template in
202
+ [`examples/diy_adapter.py`](https://github.com/drshta0/tgcaptcha-solver/blob/main/examples/diy_adapter.py), contract in
203
+ [docs/ADAPTERS.md](https://github.com/drshta0/tgcaptcha-solver/blob/main/docs/ADAPTERS.md). The safety policy runs inside the
204
+ loop on every action, so no adapter can click a trap button.
205
+
206
+ **Node.js (gramjs):** the same loop, policy and wire model ship as
207
+ TypeScript in [`node/`](https://github.com/drshta0/tgcaptcha-solver/blob/main/node/) — `npm i` there, then
208
+ `mount(client)` from `tgcaptcha-node`. The brain talks to your solver
209
+ service over the same `/v1/decide` protocol; the safety policy runs
210
+ in-process on the Node host, exactly as in Python. Parity with the
211
+ Python engine is pinned by golden vectors (1,888 policy verdicts, 12
212
+ runner transcripts, zero diffs) — see [`node/README.md`](https://github.com/drshta0/tgcaptcha-solver/blob/main/node/README.md).
213
+
214
+ Optional: pass `delay_fn` for a human-length pause before answering
215
+ (answering in ~2 s gets correct answers kicked), or host callbacks for
216
+ joins and last-name edits. Running several accounts is when the
217
+ optional solver service starts to pay off — see below.
218
+
219
+ ## Quickstart
220
+
221
+ ```python
222
+ from tgcaptcha import Account, Button, Engine, Message, Session
223
+
224
+ s = Session(
225
+ session_id="demo",
226
+ account=Account(123, "me", first_name="Alex"),
227
+ challenge=Message(
228
+ msg_id=1,
229
+ text="Alex, **press the button** below within 1 Minute 👇🏻",
230
+ buttons=[[Button("CLICK 👉🏻 I'm NOT a BOT 🤖")]],
231
+ sender_id=42, sender_username="grouphelpbot",
232
+ ),
233
+ chat_title="Some Group",
234
+ names_account=True, # the challenge mentions this account
235
+ )
236
+
237
+ d = Engine().decide(s)
238
+ print(d.solver) # verify_button
239
+ print(d.actions) # [Click(label="CLICK 👉🏻 I'm NOT a BOT 🤖")]
240
+ ```
241
+
242
+ That runs locally with no service and no config. The full setup (service,
243
+ executor, Telethon) is in [docs/USAGE.md](https://github.com/drshta0/tgcaptcha-solver/blob/main/docs/USAGE.md).
244
+
245
+ ## All solvers
246
+
247
+ | Solver | Gate bots | Challenge | Tested |
248
+ |:--|:--|:--|:--|
249
+ | `math_buttons` | SOSO 守门 and relatives (64% of traffic), nmnmfun | `6 + 5 = ?` with answer buttons | unit, replay |
250
+ | `verify_button` | GroupHelp and 5 clones, Shieldy, long tail | one "I'm not a bot" button, 10 languages | unit, replay, live |
251
+ | `math_typed` | Shieldy, filesreply | `(10 + 1)`, type the answer | unit, replay, live |
252
+ | `rose_dm` | MissRose | image or text challenge in DM, OCR | unit, replay, live |
253
+ | `image_text_pick` | clean3robot family | sum in an image, pick the matching button | unit, live |
254
+ | `image_digits_typed` | MissRose typed, long tail | digits in an image, type them | unit, replay |
255
+ | `image_buttons` | misc image gates | image plus buttons (tesseract) | unit, replay |
256
+ | `bio_guard` | @group_protection_pro_bot | link in bio: clear, Recheck, restore | unit, replay, live |
257
+ | `profile_requirement` | profile-gate bots | set a last name, then unmute | unit, replay |
258
+ | `join_required` | join-gate bots | join the named channel, then verify | unit, replay |
259
+ | `qa_buttons` | Customize_Captcha_Bot, quiz gates | fixed question, pick the answer | unit, replay |
260
+ | `emoji_pick` | emigrants360 and similar | "click the button with 🍏" | unit, replay |
261
+ | `say_anything` | "write any message" gates | send a short plain message | unit, replay |
262
+ | `dm_flow` | deep-link verifiers | follow the link into the bot's DM | unit, replay |
263
+ | `rose_welcome_wall` | MissRose | welcome walls, ignored on purpose | unit, replay |
264
+
265
+ Each file in `tgcaptcha/solvers/` lists the gate bots it was built from.
266
+
267
+ ## Safety
268
+
269
+ Every action a solver proposes goes through the policy on the account's own
270
+ machine before anything happens. No solver is trusted, local or remote.
271
+
272
+ - **No trap buttons.** Kick, ban, casino and wallet labels are blocked, and that
273
+ check runs before any allow rule.
274
+ - **Only its own challenge.** It answers only challenges that mention this account.
275
+ - **Short plain text only.** No links, no mentions, no line breaks.
276
+ - **DMs only to the gate bot**, or to a bot the gate bot links to.
277
+ - **Joins only the channel the gate bot named.**
278
+ - **Profile edits only for bio-guard:** clear the bio once, then restore exactly
279
+ what was there.
280
+ - **Bounded.** Steps and actions are capped. After that it gives up.
281
+
282
+ ## How it works
283
+
284
+ ```
285
+ account host (Telethon) solver service
286
+ ┌───────────────────────────────┐ session ┌──────────────────────┐
287
+ │ Executor observe, act │ ────────▶ │ Engine │
288
+ │ Policy checks each action │ ◀──────── │ rules (YAML) │
289
+ │ Engine local fallback │ decision │ solvers (plugins) │
290
+ └───────────────────────────────┘ └──────────────────────┘
291
+ ```
292
+
293
+ - **Executor** (`executor.py`) turns a challenge into a `Session`, asks for a
294
+ `Decision`, runs each allowed action, then watches the gate bot for a few
295
+ seconds and repeats until done.
296
+ - **Policy** (`policy.py`) runs on the account's host and decides what is allowed.
297
+ - **Engine** (`engine.py`) tries rules first, then solvers, in order. It runs on
298
+ the service and also on the host as a fallback, so solving continues if the
299
+ service is down.
300
+
301
+ The executor can only do a fixed set of things (`model.py`): click, send text,
302
+ fetch image, fetch bio, open DM, join channel, set last name, set bio, wait,
303
+ done. New gate bots are handled by adding a rule or a solver.
304
+
305
+ ## Adding a gate bot
306
+
307
+ Most bots need only a rule. The service reloads the rules file when it changes.
308
+ See [`rules.example.yaml`](https://github.com/drshta0/tgcaptcha-solver/blob/main/rules.example.yaml).
309
+
310
+ ```yaml
311
+ - id: owlto-join
312
+ match: {bots: [Owlto_BOT]}
313
+ do: {click_label: "click\\s+to\\s+join"}
314
+
315
+ - id: filesreply
316
+ match: {bots: [filesreply_bot], text: "直接发送答案"}
317
+ do: {solver: math_typed}
318
+
319
+ - id: not-a-captcha
320
+ match: {bots: [fun_message_scoring_bot]}
321
+ do: {ignore: "quiz results"}
322
+ ```
323
+
324
+ For harder cases (images, multi-step DM flows), write a solver: subclass
325
+ `tgcaptcha.solvers.Solver`, implement `decide(session)`, and put the file in the
326
+ service's `--plugins` folder (then `POST /v1/reload`). You can also publish it
327
+ under the `tgcaptcha.solvers` entry point. See
328
+ [`examples/custom_solver.py`](https://github.com/drshta0/tgcaptcha-solver/blob/main/examples/custom_solver.py).
329
+
330
+ ## Testing a change on real traffic
331
+
332
+ ```bash
333
+ python -m tgcaptcha.replay corpus.jsonl --rules new_rules.yaml --compare-to rules.yaml
334
+ ```
335
+
336
+ This replays a stored corpus (JSON lines of sessions, or flat
337
+ `{bot, text, buttons, mode}` rows) and prints coverage per solver and per bot,
338
+ the most common challenges nothing answers, and every decision that changed
339
+ compared to the baseline. A change ships only if nothing that was solved
340
+ before stops being solved.
341
+
342
+ ## Running the service
343
+
344
+ ```bash
345
+ TGCAPTCHA_TOKEN=... python -m tgcaptcha.server --port 8090 \
346
+ --rules rules.yaml --plugins plugins/ --report reports.jsonl
347
+ ```
348
+
349
+ Endpoints: `GET /v1/health`, `POST /v1/decide`, `POST /v1/report`,
350
+ `POST /v1/reload`, all behind a Bearer token. The service keeps no state; the
351
+ host sends the whole session on every step.
352
+
353
+ On the account's host:
354
+
355
+ ```python
356
+ from tgcaptcha import Account
357
+ from tgcaptcha.client import Brain
358
+ from tgcaptcha.executor import Executor
359
+
360
+ brain = Brain("http://solver:8090", token="...") # Brain() runs locally only
361
+ ex = Executor(client, brain, account=Account(me.id, me.username),
362
+ delay_fn=lambda text: random.uniform(8, 22)) # human pause
363
+ result = await ex.run(chat, challenge_msg, names_account=True)
364
+ ```
365
+
366
+ ## Tests
367
+
368
+ ```bash
369
+ python -m tgcaptcha.tests.test_core
370
+ python -m tgcaptcha.tests.test_bio
371
+ python -m tgcaptcha.tests.test_image_digits
372
+ python -m tgcaptcha.tests.test_image_pick
373
+ ```
374
+
375
+ 384 checks, all offline. CI runs them on Python 3.10 and 3.12, along with an
376
+ import check that keeps the package standalone.
377
+
378
+ ## Responsible use
379
+
380
+ For education and research. Automating Telegram accounts may break Telegram's
381
+ Terms of Service, and you are responsible for your own accounts. Don't use it
382
+ to spam, scrape, or abuse any platform. The policy never clicks casino, wallet
383
+ or ad buttons, and that won't change.
384
+
385
+ ## License
386
+
387
+ [MIT](https://github.com/drshta0/tgcaptcha-solver/blob/main/LICENSE)