# Join Executor Patterns ## Script: `join_assigned.py` ### Key Constants ```python BATCH_SIZE = 5 # Accounts processed in parallel BATCH_PAUSE = 15 # Seconds between batches (was 30, lowered for speed) ACCOUNT_TIMEOUT = 120 # Hard timeout per account (was 60, 50×2.5s avg = 125s) ``` ### Error Classification ```python BAD_REASONS = { 'CHANNEL_PRIVATE', 'CHAT_WRITE_FORBIDDEN', 'CHANNEL_INVALID', 'USER_BANNED_IN_CHANNEL', 'INVITE_HASH_EXPIRED', 'INVITE_HASH_INVALID', 'CHAT_ID_INVALID', 'USERNAME_NOT_OCCUPIED', 'USERNAME_INVALID', 'CHAT_FORBIDDEN', 'CHAT_RESTRICTED', } ``` ### Join Result Types | Result | Meaning | |--------|---------| | `joined` | Successfully joined | | `already` | Already a member (UserAlreadyParticipantError) | | `flood_N` | Rate limited for N seconds (auto-waits) | | `bad_*` | Chat is private/deleted/invalid | | `err_*` | Unknown error (first 50 chars logged) | | `err_timeout` | Individual chat operation timed out | ### Account Status Types | Status | Meaning | |--------|---------| | `ok` | All chats processed | | `hard_timeout` | Account took >60s total | | `no_stringsession` | No StringSession found | | `timeout_connect` | connect() timed out | | `crash: ...` | Exception during processing | ### Async Flow ``` main() → for each batch of 5 accounts: → asyncio.gather(process_with_timeout(acc) for acc in batch) → process_with_timeout → asyncio.wait_for(process_account, timeout=60) → process_account: → client.connect() [10s timeout] → client.get_me() [5s timeout] → for each chat: → get_entity(username) [10s timeout] → JoinChannelRequest(entity) [10s timeout] → sleep(0.5-1.5s) → client.disconnect() ``` ### Account Lookup Accounts are identified by index in the sorted OK list: ```python ok_accounts.sort(key=lambda a: a.get("phone", "")) # ... global_idx = i + idx acc_key = f"account_{global_idx + 1}" acc_chats = assignments.get(acc_key, []) ``` ### StringSession Lookup Tries phone first, then name: ```python ss = string_sessions.get(phone) or string_sessions.get(name) ``` ### Progress Persistence ```python # Saved every batch: { "batch_num": 42, "total_joined": 352, "accounts_processed": 210 } # Joined chats saved every 10 batches: { "baraholllkamariupol": { "username": "baraholllkamariupol", "id": "1856532726", "title": "Барахолка...", "participants": 21513, "joined_by": "12894827641" } } ``` ## Performance Observations - 50 chats/account × 120s timeout: ~28-36 joins per 58-account process - ~25% of accounts succeed (ok), ~40% hard_timeout, ~20% timeout_connect, ~15% no_stringsession - 50 chats × 2.5s avg = 125s → 120s is tight. Some accounts with slow connections hit hard_timeout - timeout_connect accounts have network/DC issues — not recoverable per-run - 3 parallel processes × 58 accounts: ~81 new joins, ~17 min runtime - 0 flood, 0 bans — safe for mass operations - BATCH_PAUSE=15s (was 30s) — sufficient to avoid rate limits, 2x faster - Together with 3-way parallel: ~6x speedup vs original single 30s-pause process