110 lines
4.9 KiB
Markdown
110 lines
4.9 KiB
Markdown
# AI Lead Funnel — Echogate LLM + Telethon
|
||
|
||
Full runnable pattern for Telegram accounts that auto-respond to DMs using LLM.
|
||
|
||
## When to use
|
||
|
||
- User wants a Telegram account to auto-respond to incoming messages
|
||
- Redirect leads to a target contact (@lordrax, etc.)
|
||
- Need a human-like operator that filters and redirects
|
||
|
||
## Key design decisions
|
||
|
||
1. **Stage-gated dialog, not free-form LLM** — LLM is too eager to share contact immediately or too chatty. Use 3 hardcoded stages with separate system prompts.
|
||
2. **Processing lock per user** — `asyncio.Event` or `set()` to prevent parallel handler invocations.
|
||
3. **Typing indicator before every message** — `SetTypingRequest(SendMessageTypingAction())` + `asyncio.sleep(3-10s)`.
|
||
4. **State persistence** — JSON file with `stage`, `history`, `done` per user. Survives restarts.
|
||
|
||
## LLM config
|
||
|
||
```python
|
||
from openai import AsyncOpenAI
|
||
|
||
llm = AsyncOpenAI(
|
||
base_url="https://api.echogate.one/v1",
|
||
api_key="[REDACTED]",
|
||
timeout=60,
|
||
)
|
||
|
||
# gpt-5.5: ~3s response — fast enough for real-time
|
||
# deepseek-v4-pro: ~21s — too slow, breaks the illusion
|
||
```
|
||
|
||
## Stage prompts
|
||
|
||
```python
|
||
STAGE_PROMPTS = {
|
||
0: """Ты — оператор. Строгий, деловой, немногословный.
|
||
Это ПЕРВОЕ сообщение в диалоге. Ответь коротко (1 предложение).
|
||
Поприветствуй и спроси что интересует / по какому вопросу.
|
||
НЕ давай контакты. НЕ упоминай @target. НЕ давай ссылки.
|
||
НЕ используй эмодзи. Стиль: спокойный, уверенный, без лишней вежливости.""",
|
||
|
||
1: """Ты — оператор. Строгий, деловой.
|
||
Это ВТОРОЕ сообщение. Ответь на вопрос пользователя коротко (1-2 предложения).
|
||
Упомяни что детали, цены и наличие — у партнёра @target.
|
||
НЕ давай прямую ссылку пока. НЕ используй эмодзи.
|
||
Стиль: уверенный, деловой. Не извиняйся, не будь подобострастным.""",
|
||
|
||
2: """Ты — оператор. Строгий, деловой.
|
||
Это ТРЕТЬЕ (финальное) сообщение. Дай прямую ссылку.
|
||
Скажи обратиться туда, это всё. Закругляйся.
|
||
1 предложение. НЕ продолжай диалог. НЕ используй эмодзи.""",
|
||
}
|
||
```
|
||
|
||
## Processing lock pattern
|
||
|
||
```python
|
||
processing = set() # Track user_ids currently being processed
|
||
|
||
@client.on(events.NewMessage(incoming=True))
|
||
async def handler(event):
|
||
uk = str(event.sender_id)
|
||
|
||
if uk in processing:
|
||
log(f"Already processing {uk}, skip")
|
||
return
|
||
|
||
processing.add(uk)
|
||
try:
|
||
# ... generate and send reply ...
|
||
finally:
|
||
processing.discard(uk)
|
||
```
|
||
|
||
## Race condition: why it happens
|
||
|
||
When two messages arrive within 2 seconds (e.g. user sends empty message then "привет"), the Telethon event handler fires twice in parallel. Both invocations see the same state (stage=0, our_count=0), both generate Stage 0 replies, and both send simultaneously. Result: two greeting messages back-to-back, no dialog.
|
||
|
||
The processing lock prevents this — the second invocation sees `uk in processing` and skips.
|
||
|
||
## Fallback messages
|
||
|
||
Always have hardcoded fallbacks per stage in case LLM times out:
|
||
|
||
```python
|
||
FALLBACKS = {
|
||
0: "Добрый. По какому вопросу?",
|
||
1: "По наличию и ценам — обратись к @target",
|
||
2: "Напиши @target: https://t.me/target",
|
||
}
|
||
```
|
||
|
||
## Full runnable script
|
||
|
||
See `C:\Users\User\Desktop\_PROJECTS\рассылка\lead_funnel.py` for the production version with:
|
||
- echogate.one LLM integration
|
||
- 3-stage dialog with processing lock
|
||
- Typing indicator + random delays
|
||
- JSON state persistence
|
||
- @Tomitotototo → @lordrax funnel
|
||
|
||
## Pitfalls learned 2026-08-11
|
||
|
||
1. **JSON account metadata is stale**: `sessions/*.json` files say `status: "Active"`, `spamblock: "none"` but 90%+ of accounts are actually RESTRICTED/BANNED. Always verify by connecting and sending a test message to "me".
|
||
2. **SpamBot "RESTRICTED" ≠ can't DM**: Account may be RESTRICTED for groups but perfectly fine for private messages. The real test is `client.send_message("me", "test")`.
|
||
3. **Free-form LLM fails**: Without stage-gating, LLM either shares contact immediately (no dialog) or never shares it (endless chat). Hardcode stages.
|
||
4. **No processing lock = duplicate messages**: Parallel handler invocations on rapid-fire messages produce duplicate greetings.
|
||
5. **deepseek-v4-pro is 21s**: Too slow for real-time. gpt-5.5 is 3s. Pick fast models.
|
||
6. **No skip phrases in lead funnel**: Respond to EVERYTHING — "ку", "привет", empty messages. No intent detection needed. |