yiekheng 3c4eedff03 feat(bot): tap-to-send test message from groups menu
Each entry in the groups list is now a button. Tapping shows a group detail
view with [📝 Send Test Text]. Operator replies with the message body and
the bot sends it to the selected WhatsApp group via the live Baileys session,
records the action in audit_log, and shows success/failure inline.

This is a small forerunner of the full reminder send pipeline that plan 2
will build out (with media, scheduling, retries). Useful right now to
validate the end-to-end Telegram-to-WhatsApp send path during pairing tests.
2026-05-09 16:46:22 +08:00

44 lines
1.5 KiB
TypeScript

// Per-user conversation state for menu-driven flows.
// Currently tracks: "operator clicked Pair New, waiting for them to type the label".
// In-memory only — fine for a single-instance bot. If we ever scale horizontally,
// move this to Postgres.
const PENDING_TTL_MS = 5 * 60 * 1000; // 5 minutes
const pendingPairLabel = new Map<number, number>(); // userId → expires_at_ms
export function setPendingPairLabel(userId: number): void {
pendingPairLabel.set(userId, Date.now() + PENDING_TTL_MS);
}
export function clearPendingPairLabel(userId: number): void {
pendingPairLabel.delete(userId);
}
export function consumePendingPairLabel(userId: number): boolean {
const expiresAt = pendingPairLabel.get(userId);
if (!expiresAt) return false;
pendingPairLabel.delete(userId);
return Date.now() < expiresAt;
}
// "Send a test message to this WhatsApp group" pending state.
type PendingSend = { groupId: string; expiresAt: number };
const pendingSendToGroup = new Map<number, PendingSend>();
export function setPendingSendToGroup(userId: number, groupId: string): void {
pendingSendToGroup.set(userId, { groupId, expiresAt: Date.now() + PENDING_TTL_MS });
}
export function clearPendingSendToGroup(userId: number): void {
pendingSendToGroup.delete(userId);
}
export function consumePendingSendToGroup(userId: number): string | null {
const pending = pendingSendToGroup.get(userId);
if (!pending) return null;
pendingSendToGroup.delete(userId);
if (Date.now() >= pending.expiresAt) return null;
return pending.groupId;
}