whatsapp-bot/src/payload/PayloadClient.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

72 lines
1.9 KiB
TypeScript

import { getLogger } from '../lib/logger.js'
import { getConfig } from '../config.js'
const log = getLogger('payload-client')
export class PayloadClient {
private baseUrl: string
private apiKey?: string
constructor() {
const config = getConfig()
this.baseUrl = config.PAYLOAD_API_URL
this.apiKey = config.PAYLOAD_API_KEY
}
async find<T>(
collection: string,
query: Record<string, unknown> = {},
): Promise<{ docs: T[]; totalDocs: number }> {
const params = new URLSearchParams()
for (const [key, value] of Object.entries(query)) {
if (value !== undefined) {
params.set(key, String(value))
}
}
const url = `${this.baseUrl}/${collection}?${params.toString()}`
const response = await this.request<{ docs: T[]; totalDocs: number }>(url)
return response
}
async findByID<T>(collection: string, id: number | string): Promise<T> {
const url = `${this.baseUrl}/${collection}/${id}`
return this.request<T>(url)
}
async create<T>(
collection: string,
data: Record<string, unknown>,
): Promise<T> {
const url = `${this.baseUrl}/${collection}`
return this.request<T>(url, {
method: 'POST',
body: JSON.stringify(data),
})
}
private async request<T>(url: string, init?: RequestInit): Promise<T> {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
}
if (this.apiKey) {
headers['Authorization'] = `Bearer ${this.apiKey}`
}
const response = await fetch(url, {
...init,
headers: { ...headers, ...init?.headers },
})
if (!response.ok) {
const body = await response.text().catch(() => '')
log.error(
{ url, status: response.status, body: body.slice(0, 200) },
'Payload API error',
)
throw new Error(`Payload API error: ${response.status}`)
}
return response.json() as Promise<T>
}
}