@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.
- package/dist/index.js +122 -31
- 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 (
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
to
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
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
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
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);
|