@daolmedo/openclaw-twilio-whatsapp 1.1.22 → 1.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +122 -31
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -63,30 +63,63 @@ async function sendTwilioWhatsappMessage(opts) {
63
63
  const to = opts.to.startsWith("whatsapp:") ? opts.to : `whatsapp:${opts.to}`;
64
64
  const chunks = opts.body ? splitMessage(opts.body) : [void 0];
65
65
  let lastSid = "";
66
- for (const chunk of chunks) {
67
- const msg = await client.messages.create({
68
- from,
69
- to,
70
- ...chunk ? { body: chunk } : {},
71
- ...opts.mediaUrl ? { mediaUrl: [opts.mediaUrl] } : {}
72
- });
73
- lastSid = msg.sid;
66
+ for (let i = 0; i < chunks.length; i += 1) {
67
+ const chunk = chunks[i];
68
+ console.log(
69
+ `[twilio-whatsapp] Twilio create start from=${from} to=${to} chunk=${i + 1}/${chunks.length} bodyChars=${chunk?.length ?? 0} media=${opts.mediaUrl ? "yes" : "no"}`
70
+ );
71
+ try {
72
+ const msg = await client.messages.create({
73
+ from,
74
+ to,
75
+ ...chunk ? { body: chunk } : {},
76
+ ...opts.mediaUrl ? { mediaUrl: [opts.mediaUrl] } : {}
77
+ });
78
+ lastSid = msg.sid;
79
+ console.log(
80
+ `[twilio-whatsapp] Twilio create ok sid=${msg.sid} status=${msg.status} chunk=${i + 1}/${chunks.length}`
81
+ );
82
+ } catch (err) {
83
+ console.error(
84
+ `[twilio-whatsapp] Twilio create failed chunk=${i + 1}/${chunks.length}:`,
85
+ err
86
+ );
87
+ throw err;
88
+ }
74
89
  }
75
90
  return lastSid;
76
91
  }
77
92
 
78
93
  // src/outbound-adapter.ts
94
+ function summarizeText(text) {
95
+ if (!text) return "chars=0";
96
+ return `chars=${text.length} preview=${JSON.stringify(text.slice(0, 120))}`;
97
+ }
79
98
  async function deliver(ctx) {
80
99
  const account = resolveTwilioAccount(ctx.cfg, ctx.accountId);
81
- const sid = await sendTwilioWhatsappMessage({
82
- accountSid: account.accountSid,
83
- authToken: account.authToken,
84
- from: account.phoneNumber,
85
- to: ctx.to,
86
- body: ctx.text || void 0,
87
- mediaUrl: ctx.mediaUrl
88
- });
89
- return sid;
100
+ console.log(
101
+ `[twilio-whatsapp] Durable adapter deliver start accountId=${ctx.accountId ?? "default"} from=${account.phoneNumber} to=${ctx.to} ${summarizeText(ctx.text)} media=${ctx.mediaUrl ? "yes" : "no"}`
102
+ );
103
+ try {
104
+ const sid = await sendTwilioWhatsappMessage({
105
+ accountSid: account.accountSid,
106
+ authToken: account.authToken,
107
+ from: account.phoneNumber,
108
+ to: ctx.to,
109
+ body: ctx.text || void 0,
110
+ mediaUrl: ctx.mediaUrl
111
+ });
112
+ console.log(
113
+ `[twilio-whatsapp] Durable adapter deliver ok accountId=${ctx.accountId ?? "default"} outboundSid=${sid}`
114
+ );
115
+ return sid;
116
+ } catch (err) {
117
+ console.error(
118
+ `[twilio-whatsapp] Durable adapter deliver failed accountId=${ctx.accountId ?? "default"}:`,
119
+ err
120
+ );
121
+ throw err;
122
+ }
90
123
  }
91
124
  var twilioWhatsappOutbound = {
92
125
  deliveryMode: "direct",
@@ -278,6 +311,14 @@ function startTypingKeepalive(opts) {
278
311
 
279
312
  // src/http-routes.ts
280
313
  var MEDIA_MAX_BYTES = 16 * 1024 * 1024;
314
+ function resolveDeliveryMode() {
315
+ const value = process.env.TWILIO_WHATSAPP_DELIVERY_MODE?.trim().toLowerCase();
316
+ return value === "durable" ? "durable" : "direct";
317
+ }
318
+ function summarizeText2(text) {
319
+ if (!text) return "chars=0";
320
+ return `chars=${text.length} preview=${JSON.stringify(text.slice(0, 120))}`;
321
+ }
281
322
  async function readBody(req) {
282
323
  return new Promise((resolve, reject) => {
283
324
  const chunks = [];
@@ -333,6 +374,9 @@ function registerTwilioWhatsappHttpRoutes(api) {
333
374
  const webhookUrl = String(headers["x-twilio-webhook-url"] ?? "");
334
375
  const fields = parseTwilioWebhook(rawBody);
335
376
  const toPhone = normalizePhoneNumber(fields.to);
377
+ console.log(
378
+ `[twilio-whatsapp] Webhook received sid=${fields.messageSid || "unknown"} from=${fields.from} to=${fields.to} bodyChars=${fields.body.length} media=${fields.media.length}`
379
+ );
336
380
  const stored = getRuntimeForPhoneNumber(toPhone);
337
381
  if (!stored) {
338
382
  console.error(`[twilio-whatsapp] No runtime found for To=${fields.to}`);
@@ -371,6 +415,9 @@ function registerTwilioWhatsappHttpRoutes(api) {
371
415
  return true;
372
416
  }
373
417
  twimlOk(res);
418
+ console.log(
419
+ `[twilio-whatsapp] Webhook acknowledged sid=${fields.messageSid || "unknown"} accountId=${account.accountId}`
420
+ );
374
421
  const resolvedMedia = [];
375
422
  if (hasMedia) {
376
423
  const results = await Promise.all(
@@ -379,6 +426,9 @@ function registerTwilioWhatsappHttpRoutes(api) {
379
426
  for (const r of results) {
380
427
  if (r) resolvedMedia.push(r);
381
428
  }
429
+ console.log(
430
+ `[twilio-whatsapp] Media resolved sid=${fields.messageSid || "unknown"} requested=${fields.media.length} saved=${resolvedMedia.length}`
431
+ );
382
432
  }
383
433
  const mediaContext = resolvedMedia.length > 0 ? {
384
434
  MediaPath: resolvedMedia[0].path,
@@ -440,18 +490,25 @@ ${text}`;
440
490
  bodyForAgent = messageText || "<media:audio>";
441
491
  }
442
492
  }
443
- console.log("[twilio-whatsapp] Dispatching bodyForAgent:", bodyForAgent);
444
- const stopTypingKeepalive = fields.messageSid ? startTypingKeepalive({
445
- accountSid: account.accountSid,
446
- authToken: account.authToken,
447
- messageId: fields.messageSid
448
- }) : () => {
493
+ const deliveryMode = resolveDeliveryMode();
494
+ console.log(
495
+ `[twilio-whatsapp] Dispatching inbound sid=${fields.messageSid || "unknown"} deliveryMode=${deliveryMode} bodyForAgent=${summarizeText2(bodyForAgent)}`
496
+ );
497
+ const stopTypingKeepalive = fields.messageSid ? (() => {
498
+ console.log(`[twilio-whatsapp] Typing keepalive start sid=${fields.messageSid}`);
499
+ return startTypingKeepalive({
500
+ accountSid: account.accountSid,
501
+ authToken: account.authToken,
502
+ messageId: fields.messageSid
503
+ });
504
+ })() : () => {
449
505
  };
450
506
  let typingStopped = false;
451
507
  const stopTyping = () => {
452
508
  if (typingStopped) return;
453
509
  typingStopped = true;
454
510
  stopTypingKeepalive();
511
+ console.log(`[twilio-whatsapp] Typing keepalive stop sid=${fields.messageSid || "unknown"}`);
455
512
  };
456
513
  const { route, buildEnvelope } = resolveInboundRouteEnvelopeBuilderWithRuntime({
457
514
  cfg: stored.cfg,
@@ -491,6 +548,9 @@ ${text}`;
491
548
  OriginatingTo: senderAddress,
492
549
  ...mediaContext
493
550
  });
551
+ console.log(
552
+ `[twilio-whatsapp] Route resolved sid=${fields.messageSid || "unknown"} agentId=${route.agentId} sessionKey=${route.sessionKey} accountId=${route.accountId ?? account.accountId}`
553
+ );
494
554
  stored.channelRuntime.inbound.dispatchReply({
495
555
  cfg: stored.cfg,
496
556
  channel: "twilio-whatsapp",
@@ -502,26 +562,57 @@ ${text}`;
502
562
  recordInboundSession: stored.channelRuntime.session.recordInboundSession,
503
563
  dispatchReplyWithBufferedBlockDispatcher: stored.channelRuntime.reply.dispatchReplyWithBufferedBlockDispatcher,
504
564
  delivery: {
565
+ ...deliveryMode === "durable" ? {
566
+ durable: {
567
+ replyToMode: "first"
568
+ }
569
+ } : {},
505
570
  deliver: async (payload) => {
506
571
  const outbound = payload;
507
572
  const text = outbound.text ?? "";
508
573
  const mediaUrl = outbound.mediaUrl ?? outbound.mediaUrls?.[0];
574
+ console.log(
575
+ `[twilio-whatsapp] Direct fallback deliver invoked sid=${fields.messageSid || "unknown"} deliveryMode=${deliveryMode} ${summarizeText2(text)} media=${mediaUrl ? "yes" : "no"}`
576
+ );
509
577
  if (!text && !mediaUrl) {
578
+ console.log(
579
+ `[twilio-whatsapp] Direct fallback skipped empty payload sid=${fields.messageSid || "unknown"}`
580
+ );
510
581
  return { visibleReplySent: false };
511
582
  }
512
- const messageId = await sendTwilioWhatsappMessage({
513
- accountSid: account.accountSid,
514
- authToken: account.authToken,
515
- from: toPhone,
516
- to: fromPhone,
517
- body: text || void 0,
518
- mediaUrl
519
- });
583
+ let messageId;
584
+ try {
585
+ console.log(
586
+ `[twilio-whatsapp] Twilio direct send start sid=${fields.messageSid || "unknown"} from=${recipientAddress} to=${senderAddress} ${summarizeText2(text)} media=${mediaUrl ? "yes" : "no"}`
587
+ );
588
+ messageId = await sendTwilioWhatsappMessage({
589
+ accountSid: account.accountSid,
590
+ authToken: account.authToken,
591
+ from: toPhone,
592
+ to: fromPhone,
593
+ body: text || void 0,
594
+ mediaUrl
595
+ });
596
+ console.log(
597
+ `[twilio-whatsapp] Twilio direct send ok sid=${fields.messageSid || "unknown"} outboundSid=${messageId}`
598
+ );
599
+ } catch (err) {
600
+ console.error(
601
+ `[twilio-whatsapp] Twilio direct send failed sid=${fields.messageSid || "unknown"}:`,
602
+ err
603
+ );
604
+ throw err;
605
+ }
520
606
  return {
521
607
  messageIds: [messageId],
522
608
  visibleReplySent: true
523
609
  };
524
610
  },
611
+ onDelivered: (_payload, info, result) => {
612
+ console.log(
613
+ `[twilio-whatsapp] Delivery observer sid=${fields.messageSid || "unknown"} kind=${info.kind} result=${JSON.stringify(result)}`
614
+ );
615
+ },
525
616
  onError: (err, info) => {
526
617
  stopTyping();
527
618
  console.error(`[twilio-whatsapp] Dispatch error (${info.kind}):`, err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daolmedo/openclaw-twilio-whatsapp",
3
- "version": "1.1.22",
3
+ "version": "1.1.23",
4
4
  "description": "Twilio WhatsApp channel plugin for OpenClaw (Cody ISV)",
5
5
  "type": "module",
6
6
  "exports": {