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:
Cameron
2026-02-03 13:24:35 -08:00
committed by GitHub
parent 12871128e1
commit 6d4bfdd63d
3 changed files with 257 additions and 3 deletions

210
docs/telegram-setup.md Normal file
View 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)

View File

@@ -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
};

View File

@@ -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
*/