Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | 11x 1x 1x 2x 1x 10x 10x 10x 10x 12x 12x 12x 1x 1x 11x 10x 8x 10x 6x 10x 2x 2x | import { createDal } from "../../dal";
import { getDb } from "../../db";
import { CampaignService } from "../../services/whatsapp/campaign.service";
import { isWhatsAppSendable, requireWhatsAppClient } from "./client";
import type { WhatsAppQueueMessage } from "./types";
export async function handleWhatsAppQueue(
batch: MessageBatch<WhatsAppQueueMessage>,
env: CloudflareBindings,
) {
// `isWhatsAppSendable`, not the raw token: local dry-run stands in for a
// token, so a laptop exercises the real queue path instead of acking the
// batch two steps short of it and proving nothing.
if (!isWhatsAppSendable(env)) {
console.error("[WA_QUEUE] WhatsApp not configured, skipping batch");
for (const msg of batch.messages) {
msg.ack();
}
return;
}
// The same factory every other send seam uses: the dry-run client locally,
// the live one everywhere else. This was a hand-rolled
// `createWhatsAppClient` with neither `dryRun` nor a safety guard, so a
// campaign launched from dev or preview messaged its real recipient list.
//
// It is still only HALF of the guard, deliberately: whether a given message
// may reach Meta depends on ITS recipient, which is per-message, so the
// per-batch client cannot decide it. `CampaignService.processQueueMessage`
// applies `applySafetyGuard` and substitutes a dry-run client for this one
// when the rule says so — see the comment there for why that decision has
// to sit next to the send rather than up here.
const client = requireWhatsAppClient(env);
const db = getDb(env.DB);
const dal = createDal(db);
for (const msg of batch.messages) {
try {
const data = msg.body;
if (
data.type === "campaign_send" &&
data.campaignId &&
data.recipientId
) {
const campaignService = new CampaignService(dal);
await campaignService.processQueueMessage(data, client, env);
} else if (data.type === "reply") {
// NOTE: replies are sent to whoever wrote in, with no safety
// guard applied to `data.payload.to`. Locally that is covered by
// the dry-run client above; on dev/preview it is not, and the
// fix belongs where the reply payload is built
// (`routes/webhooks/whatsapp.routes.ts`), not here.
const result = await client.sendRaw(data.payload);
const wamid = result.messages[0]?.id;
if (data.conversationId && wamid) {
await dal.waMessages.updateStatus(wamid, "sent");
}
}
msg.ack();
} catch (error) {
console.error("[WA_QUEUE] Processing error:", error);
msg.retry();
}
}
}
|