fix: typing indicator now shows on every message, not just the first

Two issues fixed:
- Use 'paused' instead of 'available' to stop typing. Baileys'
  sendPresenceUpdate('available') sends a global <presence> stanza and
  ignores the JID, so chatstate never left 'composing' and WhatsApp
  suppressed duplicate composing notifications per XEP-0085.
- Add setTyping call when piping messages to an already-running
  container. Previously only the first message (which spawns a new
  container) triggered the typing indicator.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Gavriel Cohen
2026-02-14 14:25:29 +02:00
parent 5c68deef76
commit 6f2e10f0c3
3 changed files with 7 additions and 3 deletions

View File

@@ -823,14 +823,14 @@ describe('WhatsAppChannel', () => {
expect(fakeSocket.sendPresenceUpdate).toHaveBeenCalledWith('composing', 'test@g.us'); expect(fakeSocket.sendPresenceUpdate).toHaveBeenCalledWith('composing', 'test@g.us');
}); });
it('sends available presence when stopping', async () => { it('sends paused presence when stopping', async () => {
const opts = createTestOpts(); const opts = createTestOpts();
const channel = new WhatsAppChannel(opts); const channel = new WhatsAppChannel(opts);
await connectChannel(channel); await connectChannel(channel);
await channel.setTyping('test@g.us', false); await channel.setTyping('test@g.us', false);
expect(fakeSocket.sendPresenceUpdate).toHaveBeenCalledWith('available', 'test@g.us'); expect(fakeSocket.sendPresenceUpdate).toHaveBeenCalledWith('paused', 'test@g.us');
}); });
it('handles typing indicator failure gracefully', async () => { it('handles typing indicator failure gracefully', async () => {

View File

@@ -218,7 +218,9 @@ export class WhatsAppChannel implements Channel {
async setTyping(jid: string, isTyping: boolean): Promise<void> { async setTyping(jid: string, isTyping: boolean): Promise<void> {
try { try {
await this.sock.sendPresenceUpdate(isTyping ? 'composing' : 'available', jid); const status = isTyping ? 'composing' : 'paused';
logger.debug({ jid, status }, 'Sending presence update');
await this.sock.sendPresenceUpdate(status, jid);
} catch (err) { } catch (err) {
logger.debug({ jid, err }, 'Failed to update typing status'); logger.debug({ jid, err }, 'Failed to update typing status');
} }

View File

@@ -360,6 +360,8 @@ async function startMessageLoop(): Promise<void> {
lastAgentTimestamp[chatJid] = lastAgentTimestamp[chatJid] =
messagesToSend[messagesToSend.length - 1].timestamp; messagesToSend[messagesToSend.length - 1].timestamp;
saveState(); saveState();
// Show typing indicator while the container processes the piped message
whatsapp.setTyping(chatJid, true);
} else { } else {
// No active container — enqueue for a new one // No active container — enqueue for a new one
queue.enqueueMessageCheck(chatJid); queue.enqueueMessageCheck(chatJid);