autoreg-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
94
skills/telegram-mass-join/references/bot-ref-checkin-flow.md
Normal file
94
skills/telegram-mass-join/references/bot-ref-checkin-flow.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# Mass Bot Ref + Check-in Flow (session pool)
|
||||
|
||||
Recipe for running a points-bot flow (start with owner's ref → language → daily check-in) across the whole CLEAN_ALIVE_SESSIONS pool. Proven end-to-end on @gemini_plusv_bot over 362 sessions.
|
||||
|
||||
## Flow discovery (ALWAYS first)
|
||||
|
||||
Probe 1-3 accounts through the full path and capture reply texts + button types before writing the mass runner:
|
||||
|
||||
```python
|
||||
client.send_message(BOT, '/start ' + REF)
|
||||
msgs = await client.get_messages(BOT, limit=6)
|
||||
for m in reversed(list(msgs)):
|
||||
print(m.id, m.out, m.text)
|
||||
for r in (m.reply_markup.rows if m.reply_markup else []):
|
||||
for b in r.buttons:
|
||||
print(type(b).__name__, b.text, getattr(b,'data',None), getattr(b,'url',None))
|
||||
```
|
||||
|
||||
Key checks during probe:
|
||||
- Does a language screen appear? (fresh vs returning user marker)
|
||||
- Is there a channel-subscribe / forced-join gate anywhere in the flow? Many points bots have NONE at first — but gates can be SWITCHED ON DAYS LATER by the bot owner. Re-probe 1 account before every re-run; classify any unmatched reply text before assuming it's a timeout.
|
||||
- Are menu buttons reply-keyboard (`KeyboardButton`) or inline? Reply buttons are driven by `send_message(bot, '<exact button text>')`, not clicks.
|
||||
|
||||
## Channel gate handling (join → Verify → retry)
|
||||
|
||||
When check-in replies 'Please join our channel first!' with Join Channel + Verify inline buttons:
|
||||
|
||||
1. Extract the channel URL by clicking the join button — the callback answer RETURNS the URL as a string (data/url fields are None):
|
||||
```python
|
||||
res = await msg.click(text='📢 Join Channel')
|
||||
url = res if isinstance(res, str) and res.startswith('http') else None
|
||||
```
|
||||
2. `await client(JoinChannelRequest(await client.get_entity(url)))`
|
||||
3. Re-fetch messages, click the `✅ Verify` inline button, wait 2-3s
|
||||
4. Send the Check-in text AGAIN — gate clears and check-in succeeds
|
||||
|
||||
**ChannelsTooMuchError**: farmed accounts sit at the 500-channel cap. Free a slot by leaving 1-2 unprotected channels, then retry the join in a loop (max ~4 attempts):
|
||||
```python
|
||||
dialogs = await client.get_dialogs()
|
||||
# candidates: broadcast/megagroup entities whose username/title does NOT match KEEP_SUBSTR
|
||||
# KEEP_SUBSTR = target bot channel + owner's own channels ('ghsghs', 'alstack', 'reform', ...)
|
||||
await client(LeaveChannelRequest(entity))
|
||||
```
|
||||
Never leave the target channel or the owner's channels — match a keep-list before leaving.
|
||||
|
||||
**Import pitfall**: runner scripts that call `asyncio.run(main())` at module level execute on import — importing one to reuse `proc()` in a probe launches the whole mass run. Guard with `if __name__ == '__main__':` or probe with a standalone copy.
|
||||
|
||||
## Mass runner parameters (proven)
|
||||
|
||||
- Pool: `tmp/tg_session_ops/inventory.json`, filter `status=='LIVE'` + file exists → ~360 sessions from `Desktop/CLEAN_ALIVE_SESSIONS`
|
||||
- API creds: `API_ID=2040, API_HASH='[REDACTED-KEY]'` (same file as tg_session_ops/clean_accounts.py)
|
||||
- `asyncio.Semaphore(8)`, chunks of 40, 2-4s pause between chunks
|
||||
- `asyncio.wait_for(client.connect(), timeout=22)` — ~10% of pool hits connect TimeoutError (bad DC), non-retryable, skip
|
||||
- Progress dict keyed by session filename, saved after EVERY account → crash-safe resume
|
||||
- Random 1.5-3s sleeps between bot steps; 0 flood, 0 bans observed at this rate
|
||||
|
||||
## Per-account step sequence
|
||||
|
||||
1. `send_message(BOT, '/start ' + REF)` — ref registers on every start, even for already-known users
|
||||
2. If latest bot msg has 'English' button → `msg.click(text='🇬🇧 English')` (inline, data=None — click works)
|
||||
3. `send_message(BOT, '📋 Check-in')` (reply keyboard — send text, not click)
|
||||
4. Read last 3 bot msgs; classify by substring: 'Check-in Successful' → ok (+5 pts, regex `Total Points:\s*`?(\d+)`?`), 'Already Checked In' → done (counts)
|
||||
|
||||
## Result classes and second pass
|
||||
|
||||
- `unauth` (~10%): is_user_authorized() False — dead session
|
||||
- `err: No user has "X" as username` (1-2%): account can't resolve the bot (frozen/blocked) — skip
|
||||
- `checkin=None` (~15% first pass): reply not matched within wait window — delete these progress entries and re-run; most resolve to 'done' on pass 2. If a batch of Nones shares one reply text, it's a NEW GATE, not noise — read the actual reply before retrying.
|
||||
- Corrupt session files raise `ValueError: not enough values to unpack` in TelegramClient init — catch per-account, mark unauth, continue.
|
||||
- Final on 362 sessions: 315 ok / 310 check-ins counted; 5 accounts hit the channel gate, 4 resolved via join→Verify→retry
|
||||
|
||||
## Daily cron automation (check-in resets 00:00 UTC)
|
||||
|
||||
Turn the mass runner into a no_agent cron job:
|
||||
|
||||
1. **Wrapper script** (separate from the runner): load progress JSON → clear `checkin`/`points`/`joined_channel` fields on every record (keep `ref=True` so the runner skips re-start) → `subprocess.run([python, runner], timeout=3600, capture_output)` → print a compact summary (status counter, ok/done/none, points sum, last `DONE.` line). The summary IS the delivery — no_agent cron posts stdout verbatim.
|
||||
2. **Script location rule**: Hermes cron `script=` rejects absolute paths — the file MUST live in `~/AppData/Local/hermes/scripts/` and be passed as a bare filename. Copy the wrapper there; keep the mass runner itself anywhere (wrapper calls it by absolute path).
|
||||
3. **Schedule**: fire shortly after the bot's UTC reset (e.g. `10 3 * * *` MSK = 00:10 UTC). `no_agent=true`, `deliver='telegram:<owner DM id>'`, `enabled_toolsets` irrelevant for no_agent.
|
||||
4. **Smoke-test before trusting**: run the wrapper manually once in background (`> smoke.log 2>&1; echo EXIT=$? >> smoke.log`) and verify `EXIT=0` + summary lines. Same-day re-run is safe — bot answers 'Already Checked In' (counts as done), no double-claim exists.
|
||||
5. Wrapper must NOT import the runner module — runners call `asyncio.run(main())` at module level (see import pitfall); subprocess isolation is the fix.
|
||||
|
||||
Proven artifacts: wrapper `AppData/Local/hermes/scripts/gemini_daily_checkin.py`, job `gemini-bot-daily-checkin` (~7 min full cycle, 362 sessions).
|
||||
|
||||
## Bot economy notes (@gemini_plusv_bot)
|
||||
|
||||
- 25 pts on start, +5/day check-in, +10/ref to the ref owner, 50 pts = 1 credit, 1 credit = Gemini Plus 12mo, 4 = Pro 18mo
|
||||
- Owner's ref link accumulates points; 20 refs = Pro 18mo
|
||||
- Daily cron over the same pool is set up — see section above
|
||||
|
||||
## Files
|
||||
|
||||
- Runner v2 (with channel-gate handling + slot freeing): `C:/Users/User/tmp/gemini_ref_mass2.py` (parameterize BOT/REF for other bots); v1 without gate: `gemini_ref_mass.py`
|
||||
- Progress: `tmp/gemini_refprog.json`, log: `tmp/gemini_ref_log.jsonl`, run logs: `tmp/gemini_ref_run*.log`
|
||||
- Gate channel seen on @gemini_plusv_bot: `@ghsghs_5` (may rotate — always extract from the Join Channel callback, never hardcode)
|
||||
Reference in New Issue
Block a user