From 6d4bfdd63d3ef68ded8d4890b6f99ab41443e30e Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 3 Feb 2026 13:24:35 -0800 Subject: [PATCH] fix(whatsapp): suppress Signal Protocol crypto noise in logs (#102) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- docs/telegram-setup.md | 210 +++++++++++++++++++++++++++++++ src/channels/whatsapp/index.ts | 9 +- src/channels/whatsapp/session.ts | 41 ++++++ 3 files changed, 257 insertions(+), 3 deletions(-) create mode 100644 docs/telegram-setup.md diff --git a/docs/telegram-setup.md b/docs/telegram-setup.md new file mode 100644 index 0000000..d378a44 --- /dev/null +++ b/docs/telegram-setup.md @@ -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 ` +- 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) diff --git a/src/channels/whatsapp/index.ts b/src/channels/whatsapp/index.ts index 9fb8960..e5d6c0d 100644 --- a/src/channels/whatsapp/index.ts +++ b/src/channels/whatsapp/index.ts @@ -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 { 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 }; diff --git a/src/channels/whatsapp/session.ts b/src/channels/whatsapp/session.ts index 00c5547..4c6f3eb 100644 --- a/src/channels/whatsapp/session.ts +++ b/src/channels/whatsapp/session.ts @@ -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 */