diff --git a/src/channels/types.ts b/src/channels/types.ts index c75fcc3..b4ee877 100644 --- a/src/channels/types.ts +++ b/src/channels/types.ts @@ -22,6 +22,7 @@ export interface ChannelAdapter { sendMessage(msg: OutboundMessage): Promise<{ messageId: string }>; editMessage(chatId: string, messageId: string, text: string): Promise; sendTypingIndicator(chatId: string): Promise; + stopTypingIndicator?(chatId: string): Promise; // Capabilities (optional) supportsEditing?(): boolean; diff --git a/src/channels/whatsapp/index.ts b/src/channels/whatsapp/index.ts index 4c37a85..7fd85f9 100644 --- a/src/channels/whatsapp/index.ts +++ b/src/channels/whatsapp/index.ts @@ -47,6 +47,7 @@ import { sendWhatsAppMessage, sendWhatsAppFile, sendTypingIndicator, + stopTypingIndicator, sendReadReceipt, type LidMapper, } from "./outbound.js"; @@ -1017,6 +1018,11 @@ export class WhatsAppAdapter implements ChannelAdapter { if (!this.sock) return; await sendTypingIndicator(this.sock, chatId); } + + async stopTypingIndicator(chatId: string): Promise { + if (!this.sock) return; + await stopTypingIndicator(this.sock, chatId); + } } // Export types and config diff --git a/src/channels/whatsapp/outbound.ts b/src/channels/whatsapp/outbound.ts index 83b02d6..8ce9f6d 100644 --- a/src/channels/whatsapp/outbound.ts +++ b/src/channels/whatsapp/outbound.ts @@ -168,6 +168,24 @@ export async function sendTypingIndicator( } } +/** + * Stop typing indicator for a chat. + * Sends "paused" presence to immediately clear the "typing..." indicator + * instead of waiting for WhatsApp's built-in timeout (~15-25s). + */ +export async function stopTypingIndicator( + sock: import("@whiskeysockets/baileys").WASocket, + chatId: string +): Promise { + if (!sock) return; + + try { + await sock.sendPresenceUpdate("paused", chatId); + } catch { + // Ignore presence errors + } +} + /** * Send read receipt for a message. * diff --git a/src/core/bot.ts b/src/core/bot.ts index d399099..dfd68a8 100644 --- a/src/core/bot.ts +++ b/src/core/bot.ts @@ -725,6 +725,7 @@ export class LettaBot implements AgentSession { } } finally { clearInterval(typingInterval); + adapter.stopTypingIndicator?.(msg.chatId)?.catch(() => {}); } // Handle no-reply marker