@daolmedo/openclaw-twilio-whatsapp 1.1.20 → 1.1.22
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 +101 -36
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -38,6 +38,9 @@ function normalizePhoneNumber(phone) {
|
|
|
38
38
|
return phone.replace(/^whatsapp:/, "").replace(/\s+/g, "");
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// src/outbound-adapter.ts
|
|
42
|
+
import { createChannelMessageAdapterFromOutbound } from "openclaw/plugin-sdk/channel-outbound";
|
|
43
|
+
|
|
41
44
|
// src/send.ts
|
|
42
45
|
import twilio from "twilio";
|
|
43
46
|
var MAX_BODY_LENGTH = 1550;
|
|
@@ -55,7 +58,7 @@ function splitMessage(text) {
|
|
|
55
58
|
return chunks;
|
|
56
59
|
}
|
|
57
60
|
async function sendTwilioWhatsappMessage(opts) {
|
|
58
|
-
const client = twilio(opts.accountSid, opts.authToken);
|
|
61
|
+
const client = twilio(opts.accountSid, opts.authToken, { timeout: 3e4 });
|
|
59
62
|
const from = opts.from.startsWith("whatsapp:") ? opts.from : `whatsapp:${opts.from}`;
|
|
60
63
|
const to = opts.to.startsWith("whatsapp:") ? opts.to : `whatsapp:${opts.to}`;
|
|
61
64
|
const chunks = opts.body ? splitMessage(opts.body) : [void 0];
|
|
@@ -87,6 +90,13 @@ async function deliver(ctx) {
|
|
|
87
90
|
}
|
|
88
91
|
var twilioWhatsappOutbound = {
|
|
89
92
|
deliveryMode: "direct",
|
|
93
|
+
deliveryCapabilities: {
|
|
94
|
+
durableFinal: {
|
|
95
|
+
text: true,
|
|
96
|
+
media: true,
|
|
97
|
+
messageSendingHooks: true
|
|
98
|
+
}
|
|
99
|
+
},
|
|
90
100
|
async sendText(ctx) {
|
|
91
101
|
const messageId = await deliver(ctx);
|
|
92
102
|
return {
|
|
@@ -102,6 +112,10 @@ var twilioWhatsappOutbound = {
|
|
|
102
112
|
};
|
|
103
113
|
}
|
|
104
114
|
};
|
|
115
|
+
var twilioWhatsappMessageAdapter = createChannelMessageAdapterFromOutbound({
|
|
116
|
+
id: "twilio-whatsapp",
|
|
117
|
+
outbound: twilioWhatsappOutbound
|
|
118
|
+
});
|
|
105
119
|
|
|
106
120
|
// src/runtime-store.ts
|
|
107
121
|
var phoneToRuntime = /* @__PURE__ */ new Map();
|
|
@@ -152,6 +166,7 @@ var twilioWhatsappPlugin = {
|
|
|
152
166
|
}
|
|
153
167
|
},
|
|
154
168
|
outbound: twilioWhatsappOutbound,
|
|
169
|
+
message: twilioWhatsappMessageAdapter,
|
|
155
170
|
gateway: {
|
|
156
171
|
async startAccount(ctx) {
|
|
157
172
|
const account = ctx.account;
|
|
@@ -196,7 +211,7 @@ var twilioWhatsappPlugin = {
|
|
|
196
211
|
// src/http-routes.ts
|
|
197
212
|
import { readFile } from "node:fs/promises";
|
|
198
213
|
import path from "node:path";
|
|
199
|
-
import {
|
|
214
|
+
import { resolveInboundRouteEnvelopeBuilderWithRuntime } from "openclaw/plugin-sdk/inbound-envelope";
|
|
200
215
|
import { saveMediaBuffer } from "openclaw/plugin-sdk/media-runtime";
|
|
201
216
|
|
|
202
217
|
// src/webhook.ts
|
|
@@ -426,53 +441,103 @@ ${text}`;
|
|
|
426
441
|
}
|
|
427
442
|
}
|
|
428
443
|
console.log("[twilio-whatsapp] Dispatching bodyForAgent:", bodyForAgent);
|
|
429
|
-
const
|
|
444
|
+
const stopTypingKeepalive = fields.messageSid ? startTypingKeepalive({
|
|
430
445
|
accountSid: account.accountSid,
|
|
431
446
|
authToken: account.authToken,
|
|
432
447
|
messageId: fields.messageSid
|
|
433
448
|
}) : () => {
|
|
434
449
|
};
|
|
435
|
-
|
|
450
|
+
let typingStopped = false;
|
|
451
|
+
const stopTyping = () => {
|
|
452
|
+
if (typingStopped) return;
|
|
453
|
+
typingStopped = true;
|
|
454
|
+
stopTypingKeepalive();
|
|
455
|
+
};
|
|
456
|
+
const { route, buildEnvelope } = resolveInboundRouteEnvelopeBuilderWithRuntime({
|
|
436
457
|
cfg: stored.cfg,
|
|
437
|
-
runtime: { channel: stored.channelRuntime },
|
|
438
458
|
channel: "twilio-whatsapp",
|
|
439
|
-
channelLabel: "WhatsApp",
|
|
440
459
|
accountId: account.accountId,
|
|
441
460
|
peer: { kind: "direct", id: fromPhone },
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
461
|
+
runtime: stored.channelRuntime,
|
|
462
|
+
sessionStore: stored.cfg.session?.store
|
|
463
|
+
});
|
|
464
|
+
const timestamp = Date.now();
|
|
465
|
+
const rawBodyForContext = mediaContext.Transcript ? String(mediaContext.Transcript) : messageText;
|
|
466
|
+
const { storePath, body } = buildEnvelope({
|
|
467
|
+
channel: "WhatsApp",
|
|
468
|
+
from: conversationLabel,
|
|
469
|
+
body: rawBodyForContext,
|
|
470
|
+
timestamp
|
|
471
|
+
});
|
|
472
|
+
const ctxPayload = stored.channelRuntime.reply.finalizeInboundContext({
|
|
473
|
+
Body: body,
|
|
474
|
+
BodyForAgent: bodyForAgent ?? rawBodyForContext,
|
|
475
|
+
RawBody: rawBodyForContext,
|
|
476
|
+
CommandBody: rawBodyForContext,
|
|
477
|
+
From: senderAddress,
|
|
478
|
+
To: recipientAddress,
|
|
479
|
+
SessionKey: route.sessionKey,
|
|
480
|
+
AccountId: route.accountId ?? account.accountId,
|
|
481
|
+
ChatType: "direct",
|
|
482
|
+
ConversationLabel: conversationLabel,
|
|
483
|
+
SenderId: fromPhone,
|
|
484
|
+
Provider: "twilio-whatsapp",
|
|
485
|
+
Surface: "twilio-whatsapp",
|
|
486
|
+
MessageSid: fields.messageSid,
|
|
487
|
+
MessageSidFull: fields.messageSid,
|
|
488
|
+
Timestamp: timestamp,
|
|
489
|
+
CommandAuthorized: true,
|
|
490
|
+
OriginatingChannel: "twilio-whatsapp",
|
|
491
|
+
OriginatingTo: senderAddress,
|
|
492
|
+
...mediaContext
|
|
493
|
+
});
|
|
494
|
+
stored.channelRuntime.inbound.dispatchReply({
|
|
495
|
+
cfg: stored.cfg,
|
|
496
|
+
channel: "twilio-whatsapp",
|
|
497
|
+
accountId: route.accountId ?? account.accountId,
|
|
498
|
+
agentId: route.agentId,
|
|
499
|
+
routeSessionKey: route.sessionKey,
|
|
500
|
+
storePath,
|
|
501
|
+
ctxPayload,
|
|
502
|
+
recordInboundSession: stored.channelRuntime.session.recordInboundSession,
|
|
503
|
+
dispatchReplyWithBufferedBlockDispatcher: stored.channelRuntime.reply.dispatchReplyWithBufferedBlockDispatcher,
|
|
504
|
+
delivery: {
|
|
505
|
+
deliver: async (payload) => {
|
|
506
|
+
const outbound = payload;
|
|
507
|
+
const text = outbound.text ?? "";
|
|
508
|
+
const mediaUrl = outbound.mediaUrl ?? outbound.mediaUrls?.[0];
|
|
509
|
+
if (!text && !mediaUrl) {
|
|
510
|
+
return { visibleReplySent: false };
|
|
511
|
+
}
|
|
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
|
+
});
|
|
520
|
+
return {
|
|
521
|
+
messageIds: [messageId],
|
|
522
|
+
visibleReplySent: true
|
|
523
|
+
};
|
|
524
|
+
},
|
|
525
|
+
onError: (err, info) => {
|
|
526
|
+
stopTyping();
|
|
527
|
+
console.error(`[twilio-whatsapp] Dispatch error (${info.kind}):`, err);
|
|
528
|
+
}
|
|
465
529
|
},
|
|
466
|
-
|
|
467
|
-
|
|
530
|
+
replyPipeline: {},
|
|
531
|
+
record: {
|
|
532
|
+
onRecordError: (err) => {
|
|
533
|
+
console.error("[twilio-whatsapp] Session record error:", err);
|
|
534
|
+
}
|
|
468
535
|
},
|
|
469
|
-
|
|
470
|
-
stopTyping();
|
|
471
|
-
console.error(`[twilio-whatsapp] Dispatch error (${info.kind}):`, err);
|
|
472
|
-
}
|
|
536
|
+
messageId: fields.messageSid
|
|
473
537
|
}).catch((err) => {
|
|
474
|
-
stopTyping();
|
|
475
538
|
console.error("[twilio-whatsapp] Unhandled dispatch error:", err);
|
|
539
|
+
}).finally(() => {
|
|
540
|
+
stopTyping();
|
|
476
541
|
});
|
|
477
542
|
return true;
|
|
478
543
|
}
|