@daolmedo/openclaw-twilio-whatsapp 1.1.21 → 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 +123 -35
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -58,35 +58,68 @@ function splitMessage(text) {
|
|
|
58
58
|
return chunks;
|
|
59
59
|
}
|
|
60
60
|
async function sendTwilioWhatsappMessage(opts) {
|
|
61
|
-
const client = twilio(opts.accountSid, opts.authToken);
|
|
61
|
+
const client = twilio(opts.accountSid, opts.authToken, { timeout: 3e4 });
|
|
62
62
|
const from = opts.from.startsWith("whatsapp:") ? opts.from : `whatsapp:${opts.from}`;
|
|
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,29 +562,57 @@ ${text}`;
|
|
|
502
562
|
recordInboundSession: stored.channelRuntime.session.recordInboundSession,
|
|
503
563
|
dispatchReplyWithBufferedBlockDispatcher: stored.channelRuntime.reply.dispatchReplyWithBufferedBlockDispatcher,
|
|
504
564
|
delivery: {
|
|
505
|
-
durable
|
|
506
|
-
|
|
507
|
-
|
|
565
|
+
...deliveryMode === "durable" ? {
|
|
566
|
+
durable: {
|
|
567
|
+
replyToMode: "first"
|
|
568
|
+
}
|
|
569
|
+
} : {},
|
|
508
570
|
deliver: async (payload) => {
|
|
509
571
|
const outbound = payload;
|
|
510
572
|
const text = outbound.text ?? "";
|
|
511
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
|
+
);
|
|
512
577
|
if (!text && !mediaUrl) {
|
|
578
|
+
console.log(
|
|
579
|
+
`[twilio-whatsapp] Direct fallback skipped empty payload sid=${fields.messageSid || "unknown"}`
|
|
580
|
+
);
|
|
513
581
|
return { visibleReplySent: false };
|
|
514
582
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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
|
+
}
|
|
523
606
|
return {
|
|
524
607
|
messageIds: [messageId],
|
|
525
608
|
visibleReplySent: true
|
|
526
609
|
};
|
|
527
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
|
+
},
|
|
528
616
|
onError: (err, info) => {
|
|
529
617
|
stopTyping();
|
|
530
618
|
console.error(`[twilio-whatsapp] Dispatch error (${info.kind}):`, err);
|