101 lines
3.1 KiB
Markdown
101 lines
3.1 KiB
Markdown
# 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 |