whatsapp-bot/src/bot/EscalationManager.ts
Martin Porwoll 8847358507 feat: initial WhatsApp Business Bot scaffold
Phase 1 implementation with all core modules:
- Fastify webhook server with Meta signature validation
- WhatsApp Cloud API client (send text/template/interactive, mark as read)
- LLM abstraction layer with Claude provider (Haiku for speed)
- BullMQ message processing pipeline (dedup, rate limiting)
- Bot routing (MessageRouter, ConversationManager, EscalationManager)
- Payload CMS integration (InteractionWriter via direct DB, RulesLoader, TemplateResolver)
- Healthcare-safe system prompt with medical keyword detection
- PM2 ecosystem config

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 10:58:51 +00:00

81 lines
2.3 KiB
TypeScript

import { getLogger } from '../lib/logger.js'
import type { ConversationManager } from './ConversationManager.js'
import type { WhatsAppClient } from '../whatsapp/WhatsAppClient.js'
const log = getLogger('escalation-manager')
export interface EscalationReason {
type: 'medical' | 'explicit_request' | 'rule_match' | 'sentiment' | 'repeated_question'
detail: string
}
export class EscalationManager {
constructor(
private conversationManager: ConversationManager,
private whatsappClient: WhatsAppClient,
) {}
async shouldEscalate(
_phone: string,
text: string,
isMedicalQuestion: boolean,
): Promise<EscalationReason | null> {
// Medical questions → always escalate
if (isMedicalQuestion) {
return { type: 'medical', detail: 'Medical content detected' }
}
// Explicit escalation requests
const escalationPhrases = [
'mitarbeiter',
'mensch',
'echte person',
'berater',
'agent',
'human',
'real person',
'sprechen',
'anrufen',
'rückruf',
'beschwerde',
'complaint',
]
const lower = text.toLowerCase()
const matchedPhrase = escalationPhrases.find((phrase) =>
lower.includes(phrase),
)
if (matchedPhrase) {
return {
type: 'explicit_request',
detail: `User requested human: "${matchedPhrase}"`,
}
}
return null
}
async escalate(
phone: string,
reason: EscalationReason,
): Promise<void> {
await this.conversationManager.escalate(phone)
let message: string
switch (reason.type) {
case 'medical':
message =
'Ich verstehe, dass Sie eine medizinische Frage haben. Ich leite Sie an einen unserer Mitarbeiter weiter, der Ihnen besser helfen kann. Bitte haben Sie einen Moment Geduld.\n\nBei Notfällen rufen Sie bitte den Notruf 112 an.'
break
case 'explicit_request':
message =
'Ich verbinde Sie gerne mit einem Mitarbeiter. Bitte haben Sie einen Moment Geduld — wir melden uns schnellstmöglich bei Ihnen.'
break
default:
message =
'Ich leite Ihre Anfrage an einen Mitarbeiter weiter. Bitte haben Sie einen Moment Geduld.'
}
await this.whatsappClient.sendTextMessage(phone, message)
log.info({ phone, reason }, 'Conversation escalated to human agent')
}
}