fix(whatsapp): suppress Signal Protocol crypto noise in logs (#102)
Baileys/libsignal logs "Closing open session in favor of incoming prekey bundle" and similar messages that are normal Signal Protocol key renegotiation - not errors. Changes: - Remove our own crypto error logging (line 810) - Add console filter to suppress Baileys crypto patterns: - prekey bundle messages - session renegotiation - bad mac errors - ratchet/key details These are harmless noise that confused users into thinking something was wrong. Addresses LET-7275 Written by Cameron ◯ Letta Code "Silence is golden." - Thomas Carlyle
This commit is contained in:
210
docs/telegram-setup.md
Normal file
210
docs/telegram-setup.md
Normal file
@@ -0,0 +1,210 @@
|
||||
# Telegram Setup for LettaBot
|
||||
|
||||
This guide walks you through setting up Telegram as a channel for LettaBot.
|
||||
|
||||
## Overview
|
||||
|
||||
LettaBot connects to Telegram using the **Bot API** with long-polling:
|
||||
- No public URL required (no webhook needed)
|
||||
- Works behind firewalls
|
||||
- Automatic reconnection handling
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A Telegram account
|
||||
- LettaBot installed and configured with at least `LETTA_API_KEY`
|
||||
|
||||
## Step 1: Create a Bot with BotFather
|
||||
|
||||
1. Open Telegram and search for **@BotFather**
|
||||
2. Start a chat and send `/newbot`
|
||||
3. Choose a **display name** for your bot (e.g., `LettaBot`)
|
||||
4. Choose a **username** ending in `bot` (e.g., `my_letta_bot`)
|
||||
5. BotFather will respond with your **bot token**
|
||||
|
||||
> **Important**: Copy this token immediately. It looks like `123456789:ABCdefGHIjklMNOpqrsTUVwxyz`
|
||||
|
||||
## Step 2: Configure LettaBot
|
||||
|
||||
Run the onboarding wizard and select Telegram:
|
||||
|
||||
```bash
|
||||
lettabot onboard
|
||||
```
|
||||
|
||||
Or add directly to your `lettabot.yaml`:
|
||||
|
||||
```yaml
|
||||
channels:
|
||||
telegram:
|
||||
enabled: true
|
||||
token: "123456789:ABCdefGHIjklMNOpqrsTUVwxyz"
|
||||
dmPolicy: pairing # or 'allowlist' or 'open'
|
||||
```
|
||||
|
||||
Or use environment variables:
|
||||
|
||||
```bash
|
||||
export TELEGRAM_BOT_TOKEN="123456789:ABCdefGHIjklMNOpqrsTUVwxyz"
|
||||
export TELEGRAM_DM_POLICY="pairing"
|
||||
```
|
||||
|
||||
## Step 3: Start LettaBot
|
||||
|
||||
```bash
|
||||
lettabot server
|
||||
```
|
||||
|
||||
You should see:
|
||||
```
|
||||
Registered channel: Telegram
|
||||
[Telegram] Bot started as @your_bot_username
|
||||
[Telegram] DM policy: pairing
|
||||
```
|
||||
|
||||
## Step 4: Test the Integration
|
||||
|
||||
1. Open Telegram and search for your bot's username
|
||||
2. Click **Start** or send `/start`
|
||||
3. If using pairing mode, you'll receive a pairing code
|
||||
4. Approve the code: `lettabot pairing approve telegram ABCD1234`
|
||||
5. Send a message: `Hello!`
|
||||
6. The bot should respond
|
||||
|
||||
## Access Control
|
||||
|
||||
LettaBot supports three DM policies for Telegram:
|
||||
|
||||
### Pairing (Default)
|
||||
```yaml
|
||||
dmPolicy: pairing
|
||||
```
|
||||
- New users receive a pairing code
|
||||
- Approve with: `lettabot pairing approve telegram <CODE>`
|
||||
- Most secure for personal use
|
||||
|
||||
### Allowlist
|
||||
```yaml
|
||||
dmPolicy: allowlist
|
||||
allowedUsers:
|
||||
- 123456789 # Telegram user IDs
|
||||
```
|
||||
- Only specified users can interact
|
||||
- Find your user ID: Message @userinfobot or check the bot logs
|
||||
|
||||
### Open
|
||||
```yaml
|
||||
dmPolicy: open
|
||||
```
|
||||
- Anyone can message the bot
|
||||
- Not recommended for personal bots
|
||||
|
||||
## Bot Commands
|
||||
|
||||
LettaBot responds to these Telegram commands:
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `/start` | Welcome message and help |
|
||||
| `/help` | Show available commands |
|
||||
| `/status` | Show current bot status |
|
||||
| `/heartbeat` | Trigger a heartbeat (silent) |
|
||||
|
||||
## Features
|
||||
|
||||
### Voice Messages
|
||||
|
||||
Send a voice message to have it transcribed and processed:
|
||||
|
||||
1. Requires `OPENAI_API_KEY` for transcription
|
||||
2. The bot transcribes and responds to the content
|
||||
3. Configure in `lettabot.yaml`:
|
||||
```yaml
|
||||
transcription:
|
||||
provider: openai
|
||||
apiKey: sk-... # Optional: uses OPENAI_API_KEY env var
|
||||
```
|
||||
|
||||
### Attachments
|
||||
|
||||
The bot can receive and process:
|
||||
- Photos
|
||||
- Documents
|
||||
- Videos
|
||||
- Audio files
|
||||
- Stickers
|
||||
- Animations (GIFs)
|
||||
|
||||
Attachments are downloaded to `/tmp/lettabot/attachments/telegram/` and the agent can view images using its Read tool.
|
||||
|
||||
### Reactions
|
||||
|
||||
LettaBot can react to messages using the `lettabot-react` CLI:
|
||||
|
||||
```bash
|
||||
# React to the most recent message
|
||||
lettabot-react add --emoji "eyes"
|
||||
|
||||
# React to a specific message
|
||||
lettabot-react add --emoji "thumbsup" --channel telegram --chat 123456789 --message 42
|
||||
```
|
||||
|
||||
Telegram supports a limited set of reaction emojis. Common ones:
|
||||
`thumbsup`, `heart`, `fire`, `eyes`, `clap`, `tada`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Bot not responding
|
||||
|
||||
1. **Check the token**: Make sure you copied the full token from BotFather
|
||||
2. **Check pairing**: If using pairing mode, approve new users with `lettabot pairing list telegram`
|
||||
3. **Check logs**: Look for errors in the console output
|
||||
|
||||
### "Unauthorized" error
|
||||
|
||||
Your bot token is invalid:
|
||||
1. Go back to @BotFather
|
||||
2. Send `/mybots` and select your bot
|
||||
3. Click **API Token** to see the current token
|
||||
4. If needed, use **Revoke current token** to generate a new one
|
||||
|
||||
### Bot responds slowly
|
||||
|
||||
This is normal - the bot needs to:
|
||||
1. Receive your message
|
||||
2. Send it to the Letta agent
|
||||
3. Wait for the agent to respond
|
||||
4. Send the response back
|
||||
|
||||
First responses may take longer as the agent "wakes up".
|
||||
|
||||
### Voice messages not working
|
||||
|
||||
1. Make sure `OPENAI_API_KEY` is set
|
||||
2. Check the logs for transcription errors
|
||||
3. Verify your OpenAI account has API access
|
||||
|
||||
### Rate limiting
|
||||
|
||||
If the bot stops responding temporarily, Telegram may be rate-limiting requests. Wait a few minutes and try again.
|
||||
|
||||
## Security Notes
|
||||
|
||||
- **Bot tokens** should be kept secret - never commit them to git
|
||||
- Use `dmPolicy: pairing` or `allowlist` in production
|
||||
- The bot can only see messages sent directly to it
|
||||
- Group functionality is not currently supported
|
||||
|
||||
## Cross-Channel Memory
|
||||
|
||||
Since LettaBot uses a single agent across all channels:
|
||||
- Messages you send on Telegram continue the same conversation as Slack/Discord
|
||||
- The agent remembers context from all channels
|
||||
- You can start a conversation on Telegram and continue it on another channel
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Slack Setup](./slack-setup.md)
|
||||
- [Discord Setup](./discord-setup.md)
|
||||
- [WhatsApp Setup](./whatsapp-setup.md)
|
||||
- [Signal Setup](./signal-setup.md)
|
||||
@@ -31,7 +31,7 @@ import type {
|
||||
import type { CredsSaveQueue } from "../../utils/creds-queue.js";
|
||||
|
||||
// Session management
|
||||
import { createWaSocket, type SocketResult } from "./session.js";
|
||||
import { createWaSocket, installConsoleFilters, type SocketResult } from "./session.js";
|
||||
|
||||
// Inbound message handling
|
||||
import { extractInboundMessage } from "./inbound/extract.js";
|
||||
@@ -242,6 +242,9 @@ export class WhatsAppAdapter implements ChannelAdapter {
|
||||
async start(): Promise<void> {
|
||||
if (this.running) return;
|
||||
|
||||
// Suppress Baileys crypto noise from console
|
||||
installConsoleFilters();
|
||||
|
||||
this.running = true;
|
||||
this.reconnectState.abortController = new AbortController();
|
||||
|
||||
@@ -805,9 +808,9 @@ export class WhatsAppAdapter implements ChannelAdapter {
|
||||
? reason.message
|
||||
: String(reason ?? "").slice(0, 200);
|
||||
|
||||
// Just log - these are normal Signal Protocol session renegotiations
|
||||
// Silently ignore - these are normal Signal Protocol session renegotiations
|
||||
// Forcing reconnect would destroy session keys and cause "Waiting for this message" errors
|
||||
console.log("[WhatsApp] Signal Protocol renegotiation (normal):", errorStr);
|
||||
// No logging needed - this is just crypto noise
|
||||
|
||||
// Don't call forceDisconnect() - let Baileys handle it internally
|
||||
};
|
||||
|
||||
@@ -20,6 +20,47 @@ const INITIAL_CONNECT_TIMEOUT_MS = 30000; // 30 seconds
|
||||
const QR_SCAN_TIMEOUT_MS = 120000; // 2 minutes
|
||||
const BROWSER_INFO: [string, string, string] = ["LettaBot", "Desktop", "1.0.0"];
|
||||
|
||||
// Patterns to filter from console output (crypto noise from Baileys/libsignal)
|
||||
const CONSOLE_FILTER_PATTERNS = [
|
||||
/closing.*session.*prekey/i,
|
||||
/closing open session/i,
|
||||
/prekey bundle/i,
|
||||
/bad mac/i,
|
||||
/session error/i,
|
||||
/sessionentry/i,
|
||||
/ratchet/i,
|
||||
/registrationid/i,
|
||||
/basekey/i,
|
||||
/remoteidentitykey/i,
|
||||
];
|
||||
|
||||
/**
|
||||
* Install console filters to suppress Baileys crypto noise.
|
||||
* Call this once when WhatsApp channel starts.
|
||||
*/
|
||||
export function installConsoleFilters(): void {
|
||||
const originalLog = console.log;
|
||||
const originalError = console.error;
|
||||
const originalWarn = console.warn;
|
||||
|
||||
const shouldFilter = (...args: any[]): boolean => {
|
||||
const str = args.map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' ');
|
||||
return CONSOLE_FILTER_PATTERNS.some(p => p.test(str));
|
||||
};
|
||||
|
||||
console.log = (...args: any[]) => {
|
||||
if (!shouldFilter(...args)) originalLog.apply(console, args);
|
||||
};
|
||||
|
||||
console.error = (...args: any[]) => {
|
||||
if (!shouldFilter(...args)) originalError.apply(console, args);
|
||||
};
|
||||
|
||||
console.warn = (...args: any[]) => {
|
||||
if (!shouldFilter(...args)) originalWarn.apply(console, args);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for creating a WhatsApp socket
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user