@automagik/omni 2.260904.1 → 2.260906.1
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 +1 -1
- package/dist/server/index.js +225 -29
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -128298,7 +128298,7 @@ import { fileURLToPath } from "url";
|
|
|
128298
128298
|
// package.json
|
|
128299
128299
|
var package_default = {
|
|
128300
128300
|
name: "@automagik/omni",
|
|
128301
|
-
version: "2.
|
|
128301
|
+
version: "2.260906.1",
|
|
128302
128302
|
repository: {
|
|
128303
128303
|
type: "git",
|
|
128304
128304
|
url: "git+https://github.com/automagik-dev/omni.git",
|
package/dist/server/index.js
CHANGED
|
@@ -242910,7 +242910,7 @@ var init_sentry_scrub = __esm(() => {
|
|
|
242910
242910
|
var require_package7 = __commonJS((exports, module) => {
|
|
242911
242911
|
module.exports = {
|
|
242912
242912
|
name: "@omni/api",
|
|
242913
|
-
version: "2.
|
|
242913
|
+
version: "2.260906.1",
|
|
242914
242914
|
type: "module",
|
|
242915
242915
|
exports: {
|
|
242916
242916
|
".": {
|
|
@@ -344750,7 +344750,7 @@ async function sendTypingPresence(channel5, instanceId, chatId, state) {
|
|
|
344750
344750
|
log91.debug("Failed to send typing presence", { error: String(error3) });
|
|
344751
344751
|
}
|
|
344752
344752
|
}
|
|
344753
|
-
async function sendTextMessage5(channel5, instanceId, chatId, text3, messageFormatMode = "convert", replyTo, correlationId, senderAgentId, systemNotice) {
|
|
344753
|
+
async function sendTextMessage5(channel5, instanceId, chatId, text3, messageFormatMode = "convert", replyTo, correlationId, senderAgentId, systemNotice, part) {
|
|
344754
344754
|
const plugin11 = await getPlugin(channel5);
|
|
344755
344755
|
if (!plugin11)
|
|
344756
344756
|
throw new Error(`Channel plugin not found: ${channel5}`);
|
|
@@ -344761,7 +344761,13 @@ async function sendTextMessage5(channel5, instanceId, chatId, text3, messageForm
|
|
|
344761
344761
|
to: chatId,
|
|
344762
344762
|
content: { type: "text", text: sanitized },
|
|
344763
344763
|
replyTo,
|
|
344764
|
-
metadata: {
|
|
344764
|
+
metadata: {
|
|
344765
|
+
messageFormatMode,
|
|
344766
|
+
correlationId,
|
|
344767
|
+
senderAgentId,
|
|
344768
|
+
systemNotice,
|
|
344769
|
+
...part ? { partIndex: part.index, partCount: part.count } : {}
|
|
344770
|
+
}
|
|
344765
344771
|
});
|
|
344766
344772
|
}
|
|
344767
344773
|
function resolveDispatchErrorMessage(instanceErrorMessages, env2 = process.env, random = Math.random) {
|
|
@@ -345223,14 +345229,15 @@ function isSelfChat(chatId, ownerIdentifier) {
|
|
|
345223
345229
|
}
|
|
345224
345230
|
async function sendResponseParts(channel5, instanceId, chatId, parts, splitConfig, messageFormatMode = "convert", replyTo, correlationId, senderAgentId) {
|
|
345225
345231
|
const messageLimit = getMessageLimit(channel5);
|
|
345226
|
-
const
|
|
345232
|
+
const chunked = [];
|
|
345227
345233
|
for (const part of parts) {
|
|
345228
|
-
|
|
345234
|
+
chunked.push(...chunkText(part, messageLimit));
|
|
345229
345235
|
}
|
|
345236
|
+
const allChunks = chunked.map((chunk2) => sanitizeOutboundText(chunk2)).filter(Boolean);
|
|
345230
345237
|
const isSlack = channel5 === "slack";
|
|
345231
345238
|
for (const [index2, chunk2] of allChunks.entries()) {
|
|
345232
345239
|
const chunkReplyTo = isSlack || index2 === 0 ? replyTo : undefined;
|
|
345233
|
-
await sendTextMessage5(channel5, instanceId, chatId, chunk2, messageFormatMode, chunkReplyTo, correlationId, senderAgentId);
|
|
345240
|
+
await sendTextMessage5(channel5, instanceId, chatId, chunk2, messageFormatMode, chunkReplyTo, correlationId, senderAgentId, undefined, { index: index2, count: allChunks.length });
|
|
345234
345241
|
const isLastChunk = index2 === allChunks.length - 1;
|
|
345235
345242
|
if (!isLastChunk) {
|
|
345236
345243
|
const delay2 = calculateSplitDelay(splitConfig);
|
|
@@ -380150,11 +380157,28 @@ function decodeAscEmoji(text) {
|
|
|
380150
380157
|
}
|
|
380151
380158
|
});
|
|
380152
380159
|
}
|
|
380160
|
+
var TRANSLITERATIONS = [
|
|
380161
|
+
[/[\u2014\u2013\u2212]/g, "-"],
|
|
380162
|
+
[/\u2026/g, "..."],
|
|
380163
|
+
[/[\u201C\u201D\u201E]/g, '"'],
|
|
380164
|
+
[/[\u2018\u2019\u201A]/g, "'"],
|
|
380165
|
+
[/\u2192/g, "->"],
|
|
380166
|
+
[/\u2190/g, "<-"],
|
|
380167
|
+
[/\u2265/g, ">="],
|
|
380168
|
+
[/\u2264/g, "<="],
|
|
380169
|
+
[/\u00A0/g, " "],
|
|
380170
|
+
[/[\u2022\u00B7]/g, "-"]
|
|
380171
|
+
];
|
|
380172
|
+
function nonLatin1Left(text) {
|
|
380173
|
+
return [...new Set([...text].filter((ch) => (ch.codePointAt(0) ?? 0) > 255))];
|
|
380174
|
+
}
|
|
380153
380175
|
function encodeAscEmoji(text) {
|
|
380154
|
-
|
|
380176
|
+
const withMarkers = text.replace(EMOJI_RUN, (run) => `##${[...run].map((ch) => (ch.codePointAt(0) ?? 0).toString(16)).join("-")}##`);
|
|
380177
|
+
return TRANSLITERATIONS.reduce((acc, [pattern, ascii]) => acc.replace(pattern, ascii), withMarkers);
|
|
380155
380178
|
}
|
|
380156
380179
|
|
|
380157
380180
|
// ../channel-asc-flow/src/handlers/webhook.ts
|
|
380181
|
+
var holdTimeoutMs = () => Number(process.env.ASC_FLOW_HOLD_MS ?? 40000);
|
|
380158
380182
|
var MAX_BODY_BYTES = 64 * 1024;
|
|
380159
380183
|
function json(body, status = 200) {
|
|
380160
380184
|
return new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
|
|
@@ -380171,13 +380195,16 @@ function firstString(...values) {
|
|
|
380171
380195
|
}
|
|
380172
380196
|
function parseInboundTurn(body) {
|
|
380173
380197
|
const codAtendimento = firstString(body.codAtendimento, body.cod_atendimento);
|
|
380174
|
-
const
|
|
380198
|
+
const typed = decodeAscEmoji(firstString(body.chatInput));
|
|
380199
|
+
const fallback = decodeAscEmoji(firstString(body.message));
|
|
380200
|
+
const text = typed || fallback;
|
|
380175
380201
|
if (!codAtendimento || !text)
|
|
380176
380202
|
return null;
|
|
380177
380203
|
const messageId = firstString(body.messageId, body.idMensagem);
|
|
380178
380204
|
return {
|
|
380179
380205
|
codAtendimento,
|
|
380180
380206
|
text,
|
|
380207
|
+
fromFallback: !typed,
|
|
380181
380208
|
phone: firstString(body.phone, body.telefone),
|
|
380182
380209
|
...messageId ? { messageId } : {}
|
|
380183
380210
|
};
|
|
@@ -380225,12 +380252,26 @@ async function handleAscFlowWebhookRequest(request, plugin2, instanceId, verifyT
|
|
|
380225
380252
|
if (ready) {
|
|
380226
380253
|
return json(ready);
|
|
380227
380254
|
}
|
|
380255
|
+
if (turn.fromFallback && plugin2.hasSeenCod(instanceId, turn.codAtendimento)) {
|
|
380256
|
+
logger5.debug("[asc-flow] loop-back with no chatInput \u2014 treating as a poll", {
|
|
380257
|
+
instanceId,
|
|
380258
|
+
codAtendimento: turn.codAtendimento
|
|
380259
|
+
});
|
|
380260
|
+
return pending();
|
|
380261
|
+
}
|
|
380262
|
+
if (await plugin2.isStaleFlowReplay(instanceId, turn)) {
|
|
380263
|
+
return pending();
|
|
380264
|
+
}
|
|
380228
380265
|
const isRedelivery = turn.messageId ? dedupeCache.isDuplicate(instanceId, turn.messageId, "asc-flow", logger5) : plugin2.isRedeliveryOfTurnInFlight(instanceId, turn);
|
|
380229
380266
|
if (isRedelivery) {
|
|
380230
380267
|
return pending();
|
|
380231
380268
|
}
|
|
380232
380269
|
await plugin2.handleInboundTurn(instanceId, turn);
|
|
380233
|
-
|
|
380270
|
+
const holdMs = holdTimeoutMs();
|
|
380271
|
+
if (holdMs <= 0)
|
|
380272
|
+
return pending();
|
|
380273
|
+
const held = await plugin2.waitForTurn(instanceId, turn.codAtendimento, turn.text, holdMs);
|
|
380274
|
+
return held ? json(held) : pending();
|
|
380234
380275
|
}
|
|
380235
380276
|
|
|
380236
380277
|
// ../channel-asc-flow/src/utils/handoff.ts
|
|
@@ -380306,6 +380347,16 @@ function buildGenesysFields(read, logger5, mode) {
|
|
|
380306
380347
|
init_src2();
|
|
380307
380348
|
var MAX_OPTIONS = 10;
|
|
380308
380349
|
var MAX_BODY_TEXT = 1024;
|
|
380350
|
+
var MAX_BUTTONS = 3;
|
|
380351
|
+
var BUTTON_TITLE_MAX = 20;
|
|
380352
|
+
function shortenTitle(title) {
|
|
380353
|
+
const value = title.trim();
|
|
380354
|
+
if (value.length <= BUTTON_TITLE_MAX)
|
|
380355
|
+
return value;
|
|
380356
|
+
const head = value.slice(0, BUTTON_TITLE_MAX);
|
|
380357
|
+
const boundary = head.lastIndexOf(" ");
|
|
380358
|
+
return (boundary >= 8 ? head.slice(0, boundary) : head).trim();
|
|
380359
|
+
}
|
|
380309
380360
|
function foldTitle(value) {
|
|
380310
380361
|
return value.normalize("NFD").replace(/\p{Mn}/gu, "").toLocaleLowerCase().split(/\s+/).filter(Boolean).join(" ");
|
|
380311
380362
|
}
|
|
@@ -380331,14 +380382,19 @@ function buildUra(body, buttons, listOptions = {}) {
|
|
|
380331
380382
|
const type = plan.interactive.type;
|
|
380332
380383
|
if (type !== "button" && type !== "list")
|
|
380333
380384
|
return null;
|
|
380334
|
-
|
|
380385
|
+
let titles = titlesOf(plan.interactive);
|
|
380386
|
+
let asButtons = type === "button";
|
|
380387
|
+
if (!asButtons && replyButtons.length <= MAX_BUTTONS) {
|
|
380388
|
+
titles = replyButtons.map((b2) => shortenTitle(b2.text ?? ""));
|
|
380389
|
+
asButtons = true;
|
|
380390
|
+
}
|
|
380335
380391
|
if (titles.length !== replyButtons.length || titles.some((t) => !t))
|
|
380336
380392
|
return null;
|
|
380337
380393
|
if (new Set(titles.map(foldTitle)).size !== titles.length)
|
|
380338
380394
|
return null;
|
|
380339
380395
|
return {
|
|
380340
380396
|
ura_opcoes: Object.fromEntries(titles.map((title, i) => [String(i + 1), title])),
|
|
380341
|
-
forcar_botoes:
|
|
380397
|
+
forcar_botoes: asButtons
|
|
380342
380398
|
};
|
|
380343
380399
|
}
|
|
380344
380400
|
function splitBubbles(text) {
|
|
@@ -380557,6 +380613,47 @@ function buildReplyField(replyTo) {
|
|
|
380557
380613
|
return /^\d+$/.test(trimmed) ? { id_mensagem_resposta: Number(trimmed) } : {};
|
|
380558
380614
|
}
|
|
380559
380615
|
|
|
380616
|
+
// ../channel-asc-flow/src/utils/turn-freshness.ts
|
|
380617
|
+
async function latestInboundText(client, codAtendimento) {
|
|
380618
|
+
const { status, body } = await client.get("/atendimento", { codigo_atendimento: codAtendimento });
|
|
380619
|
+
if (status !== 200 || typeof body !== "object" || body === null)
|
|
380620
|
+
return null;
|
|
380621
|
+
const list = body.mensagens;
|
|
380622
|
+
if (!Array.isArray(list))
|
|
380623
|
+
return null;
|
|
380624
|
+
for (const raw of [...list].reverse()) {
|
|
380625
|
+
if (String(raw.boleano_entrante ?? "") !== "1")
|
|
380626
|
+
continue;
|
|
380627
|
+
const text = decodeAscEmoji(String(raw.descricao_msg ?? "").trim());
|
|
380628
|
+
if (text)
|
|
380629
|
+
return text;
|
|
380630
|
+
}
|
|
380631
|
+
return null;
|
|
380632
|
+
}
|
|
380633
|
+
async function isStaleFlowReplay(params) {
|
|
380634
|
+
const { client, instanceId, codAtendimento, text, logger: logger5 } = params;
|
|
380635
|
+
let latest;
|
|
380636
|
+
try {
|
|
380637
|
+
latest = await latestInboundText(client, codAtendimento);
|
|
380638
|
+
} catch (err2) {
|
|
380639
|
+
logger5.warn("[asc-flow] could not read the atendimento to date this turn \u2014 processing it", {
|
|
380640
|
+
instanceId,
|
|
380641
|
+
codAtendimento,
|
|
380642
|
+
err: String(err2)
|
|
380643
|
+
});
|
|
380644
|
+
return false;
|
|
380645
|
+
}
|
|
380646
|
+
if (latest === null)
|
|
380647
|
+
return false;
|
|
380648
|
+
if (latest === text.trim())
|
|
380649
|
+
return false;
|
|
380650
|
+
logger5.info("[asc-flow] flow restarted with a stale input variable \u2014 dropping the replay", {
|
|
380651
|
+
instanceId,
|
|
380652
|
+
codAtendimento
|
|
380653
|
+
});
|
|
380654
|
+
return true;
|
|
380655
|
+
}
|
|
380656
|
+
|
|
380560
380657
|
// ../channel-asc-flow/src/plugin.ts
|
|
380561
380658
|
var DEFAULT_ASC_FLOW_BASE_URL = "https://sac-notredame.ascbrazil.com.br";
|
|
380562
380659
|
var REST_PREFIX = "/rest/v2";
|
|
@@ -380566,11 +380663,36 @@ var UNDELIVERABLE_ERROR = "no ASC flow turn is polling this cod_atendimento \u20
|
|
|
380566
380663
|
var HANDOFF_REFUSED_ERROR = "handoff refused: the transfer never held, so the turn answers without it";
|
|
380567
380664
|
var IN_FLIGHT_SWEEP_MS = 60000;
|
|
380568
380665
|
var IN_FLIGHT_MAX_ENTRIES = 5000;
|
|
380569
|
-
function resolveOutboundText(message2) {
|
|
380666
|
+
function resolveOutboundText(message2, logger5) {
|
|
380570
380667
|
const formatMode = message2.metadata?.messageFormatMode ?? "convert";
|
|
380571
380668
|
const text = message2.content.text ?? message2.content.caption ?? "";
|
|
380572
380669
|
const formatted = formatMode === "passthrough" ? text : markdownToWhatsApp(text);
|
|
380573
|
-
|
|
380670
|
+
const encoded = encodeAscEmoji(formatted);
|
|
380671
|
+
const restante = nonLatin1Left(encoded);
|
|
380672
|
+
if (restante.length > 0) {
|
|
380673
|
+
logger5?.warn('[asc-flow] characters the platform cannot carry \u2014 they will arrive as "?"', {
|
|
380674
|
+
chars: restante.join(" "),
|
|
380675
|
+
codepoints: restante.map((c) => (c.codePointAt(0) ?? 0).toString(16)).join(" ")
|
|
380676
|
+
});
|
|
380677
|
+
}
|
|
380678
|
+
return encoded;
|
|
380679
|
+
}
|
|
380680
|
+
function collectTurnParts(message2, turn) {
|
|
380681
|
+
const meta = message2.metadata ?? {};
|
|
380682
|
+
const partCount = Number(meta.partCount ?? 1);
|
|
380683
|
+
const partIndex = Number(meta.partIndex ?? 0);
|
|
380684
|
+
if (!turn || message2.content.type !== "text" || !(partCount > 1) || !(partIndex >= 0))
|
|
380685
|
+
return message2;
|
|
380686
|
+
const part = message2.content.text ?? "";
|
|
380687
|
+
if (partIndex < partCount - 1) {
|
|
380688
|
+
turn.parts = [...turn.parts ?? [], part];
|
|
380689
|
+
return null;
|
|
380690
|
+
}
|
|
380691
|
+
const text = [...turn.parts ?? [], part].join(`
|
|
380692
|
+
|
|
380693
|
+
`);
|
|
380694
|
+
turn.parts = undefined;
|
|
380695
|
+
return { ...message2, content: { ...message2.content, text } };
|
|
380574
380696
|
}
|
|
380575
380697
|
function listOptionsOf(content) {
|
|
380576
380698
|
return {
|
|
@@ -380594,6 +380716,8 @@ function buildReadyBody(turn) {
|
|
|
380594
380716
|
resposta: turn.delivered ? "" : turn.lastBubble || OUTBOUND_MEDIA_FALLBACK_TEXT,
|
|
380595
380717
|
hand_off: turn.handoff ? "sim" : "nao",
|
|
380596
380718
|
bolhas: turn.bubbles,
|
|
380719
|
+
fila_vq: "",
|
|
380720
|
+
motivo_transf_vq: "",
|
|
380597
380721
|
...turn.handoff ?? {},
|
|
380598
380722
|
...turn.ura ?? {}
|
|
380599
380723
|
};
|
|
@@ -380646,7 +380770,9 @@ class AscFlowPlugin extends BaseChannelPlugin {
|
|
|
380646
380770
|
config: ascFlowConfig,
|
|
380647
380771
|
dedupeCache: createInboundDedupeCache(),
|
|
380648
380772
|
inFlight: new Map,
|
|
380649
|
-
lastSweepAt: Date.now()
|
|
380773
|
+
lastSweepAt: Date.now(),
|
|
380774
|
+
seenCods: new Set,
|
|
380775
|
+
lastAnswered: new Map
|
|
380650
380776
|
});
|
|
380651
380777
|
await this.updateInstanceStatus(instanceId, config2, {
|
|
380652
380778
|
state: "connected",
|
|
@@ -380688,9 +380814,8 @@ class AscFlowPlugin extends BaseChannelPlugin {
|
|
|
380688
380814
|
if (!state) {
|
|
380689
380815
|
return { success: false, error: "ASC Flow instance not connected", retryable: false, timestamp: Date.now() };
|
|
380690
380816
|
}
|
|
380691
|
-
const {
|
|
380817
|
+
const { to } = message2;
|
|
380692
380818
|
const meta = message2.metadata ?? {};
|
|
380693
|
-
const text = resolveOutboundText(message2);
|
|
380694
380819
|
const correlationId = meta.correlationId;
|
|
380695
380820
|
if (correlationId)
|
|
380696
380821
|
this.captureT10(correlationId);
|
|
@@ -380698,12 +380823,24 @@ class AscFlowPlugin extends BaseChannelPlugin {
|
|
|
380698
380823
|
const cod = toCodAtendimento(to);
|
|
380699
380824
|
const answering = state.inFlight.get(to.trim());
|
|
380700
380825
|
const polling = answering !== undefined && answering.text !== "";
|
|
380701
|
-
const
|
|
380826
|
+
const turnMessage = collectTurnParts(message2, polling ? answering : undefined);
|
|
380827
|
+
if (!turnMessage)
|
|
380828
|
+
return { success: true, timestamp: Date.now() };
|
|
380829
|
+
const content = turnMessage.content;
|
|
380830
|
+
const text = resolveOutboundText(turnMessage, this.logger);
|
|
380831
|
+
const { rich, bubbles } = await this.prepareTurn(turnMessage, text);
|
|
380702
380832
|
const lastBubble = bubbles[bubbles.length - 1] ?? "";
|
|
380703
380833
|
const ura = rich ? null : buildUra(lastBubble, content.buttons, listOptionsOf(content));
|
|
380704
|
-
|
|
380834
|
+
const { delivered } = await this.deliver(state, cod, {
|
|
380835
|
+
text,
|
|
380836
|
+
bubbles,
|
|
380837
|
+
lastBubble,
|
|
380838
|
+
rich,
|
|
380839
|
+
ura,
|
|
380840
|
+
message: turnMessage
|
|
380841
|
+
});
|
|
380842
|
+
if (!delivered && !polling)
|
|
380705
380843
|
return this.refuseUndeliverable(instanceId, to, content.type);
|
|
380706
|
-
const { delivered } = await this.deliver(state, cod, { text, bubbles, lastBubble, rich, ura, message: message2 });
|
|
380707
380844
|
const handoff = meta.isHandoff === true ? await this.runHandoff(state, instanceId, cod, meta, delivered ? "" : lastBubble) : null;
|
|
380708
380845
|
const handoffRefused = meta.isHandoff === true && handoff === null;
|
|
380709
380846
|
this.resolveTurn(state, to, buildReadyBody({ delivered, lastBubble, bubbles, handoff, ura }), answering, correlationId);
|
|
@@ -380712,7 +380849,7 @@ class AscFlowPlugin extends BaseChannelPlugin {
|
|
|
380712
380849
|
if (correlationId)
|
|
380713
380850
|
this.captureT11(correlationId);
|
|
380714
380851
|
const messageId = crypto.randomUUID();
|
|
380715
|
-
await this.emitTurnSent(instanceId,
|
|
380852
|
+
await this.emitTurnSent(instanceId, turnMessage, messageId, {
|
|
380716
380853
|
cod,
|
|
380717
380854
|
bubbles: bubbles.length,
|
|
380718
380855
|
ura,
|
|
@@ -380858,13 +380995,19 @@ class AscFlowPlugin extends BaseChannelPlugin {
|
|
|
380858
380995
|
async deliver(state, cod, turn) {
|
|
380859
380996
|
const reply = buildReplyField(turn.message.replyTo);
|
|
380860
380997
|
if (turn.rich) {
|
|
380861
|
-
|
|
380998
|
+
if (await this.sendMensagem(state, cod, turn.text, { ...turn.rich, ...reply }))
|
|
380999
|
+
return { delivered: true };
|
|
381000
|
+
const texto = turn.bubbles.length > 0 ? turn.bubbles : [OUTBOUND_MEDIA_FALLBACK_TEXT];
|
|
381001
|
+
return { delivered: await this.pushBubbles(state, cod, texto) > 0 };
|
|
380862
381002
|
}
|
|
380863
|
-
await this.pushLeadingBubbles(state, cod, turn.bubbles);
|
|
380864
381003
|
if (turn.ura) {
|
|
380865
|
-
|
|
381004
|
+
await this.pushBubbles(state, cod, turn.bubbles.slice(0, -1));
|
|
381005
|
+
if (await this.sendMensagem(state, cod, turn.lastBubble, { ...turn.ura, ...reply }))
|
|
381006
|
+
return { delivered: true };
|
|
381007
|
+
return { delivered: await this.pushBubbles(state, cod, [turn.lastBubble]) > 0 };
|
|
380866
381008
|
}
|
|
380867
|
-
|
|
381009
|
+
const pushed = await this.pushBubbles(state, cod, turn.bubbles);
|
|
381010
|
+
return { delivered: pushed > 0 };
|
|
380868
381011
|
}
|
|
380869
381012
|
async sendMensagem(state, cod, text, extra) {
|
|
380870
381013
|
try {
|
|
@@ -380885,8 +381028,9 @@ class AscFlowPlugin extends BaseChannelPlugin {
|
|
|
380885
381028
|
return false;
|
|
380886
381029
|
}
|
|
380887
381030
|
}
|
|
380888
|
-
async
|
|
380889
|
-
|
|
381031
|
+
async pushBubbles(state, cod, bubbles) {
|
|
381032
|
+
let enviadas = 0;
|
|
381033
|
+
for (const bubble of bubbles) {
|
|
380890
381034
|
try {
|
|
380891
381035
|
await state.client.call("/callbackFlowMsg", {
|
|
380892
381036
|
cod_atendimento: cod,
|
|
@@ -380894,15 +381038,21 @@ class AscFlowPlugin extends BaseChannelPlugin {
|
|
|
380894
381038
|
msg_usuario: bubble,
|
|
380895
381039
|
entrante: 0
|
|
380896
381040
|
});
|
|
380897
|
-
|
|
381041
|
+
enviadas++;
|
|
381042
|
+
if (enviadas < bubbles.length) {
|
|
381043
|
+
await state.client.call("/sendIndicador", { cod, tipo: 1 });
|
|
381044
|
+
}
|
|
380898
381045
|
} catch (err2) {
|
|
380899
|
-
this.logger.warn("[asc-flow]
|
|
381046
|
+
this.logger.warn("[asc-flow] bubble push failed \u2014 stopping so the rest do not arrive out of order", {
|
|
380900
381047
|
cod,
|
|
381048
|
+
enviadas,
|
|
381049
|
+
total: bubbles.length,
|
|
380901
381050
|
err: String(err2)
|
|
380902
381051
|
});
|
|
380903
|
-
return;
|
|
381052
|
+
return enviadas;
|
|
380904
381053
|
}
|
|
380905
381054
|
}
|
|
381055
|
+
return enviadas;
|
|
380906
381056
|
}
|
|
380907
381057
|
async handleWebhook(request) {
|
|
380908
381058
|
const url = new URL(request.url);
|
|
@@ -380922,6 +381072,12 @@ class AscFlowPlugin extends BaseChannelPlugin {
|
|
|
380922
381072
|
if (inboundState) {
|
|
380923
381073
|
this.sweepInFlight(instanceId, inboundState);
|
|
380924
381074
|
inboundState.inFlight.set(turn.codAtendimento, { text: turn.text, at: Date.now() });
|
|
381075
|
+
if (inboundState.seenCods.size >= IN_FLIGHT_MAX_ENTRIES) {
|
|
381076
|
+
const oldest = inboundState.seenCods.values().next().value;
|
|
381077
|
+
if (oldest !== undefined)
|
|
381078
|
+
inboundState.seenCods.delete(oldest);
|
|
381079
|
+
}
|
|
381080
|
+
inboundState.seenCods.add(turn.codAtendimento);
|
|
380925
381081
|
}
|
|
380926
381082
|
await this.sendTyping(instanceId, turn.codAtendimento);
|
|
380927
381083
|
const externalId = turn.messageId ?? crypto.randomUUID();
|
|
@@ -380969,6 +381125,9 @@ class AscFlowPlugin extends BaseChannelPlugin {
|
|
|
380969
381125
|
getLogger() {
|
|
380970
381126
|
return this.logger;
|
|
380971
381127
|
}
|
|
381128
|
+
hasSeenCod(instanceId, codAtendimento) {
|
|
381129
|
+
return this.ascFlowInstances.get(instanceId)?.seenCods.has(codAtendimento) ?? false;
|
|
381130
|
+
}
|
|
380972
381131
|
sweepInFlight(instanceId, state) {
|
|
380973
381132
|
const now = Date.now();
|
|
380974
381133
|
if (now - state.lastSweepAt < IN_FLIGHT_SWEEP_MS && state.inFlight.size < IN_FLIGHT_MAX_ENTRIES)
|
|
@@ -381026,8 +381185,45 @@ class AscFlowPlugin extends BaseChannelPlugin {
|
|
|
381026
381185
|
ageMs
|
|
381027
381186
|
});
|
|
381028
381187
|
}
|
|
381188
|
+
if (state.lastAnswered.size >= IN_FLIGHT_MAX_ENTRIES) {
|
|
381189
|
+
const oldest = state.lastAnswered.keys().next().value;
|
|
381190
|
+
if (oldest !== undefined)
|
|
381191
|
+
state.lastAnswered.delete(oldest);
|
|
381192
|
+
}
|
|
381193
|
+
state.lastAnswered.set(codAtendimento, text.trim());
|
|
381029
381194
|
return entry.ready;
|
|
381030
381195
|
}
|
|
381196
|
+
async isStaleFlowReplay(instanceId, turn) {
|
|
381197
|
+
const state = this.ascFlowInstances.get(instanceId);
|
|
381198
|
+
if (!state)
|
|
381199
|
+
return false;
|
|
381200
|
+
if (state.lastAnswered.get(turn.codAtendimento) !== turn.text.trim())
|
|
381201
|
+
return false;
|
|
381202
|
+
return isStaleFlowReplay({
|
|
381203
|
+
client: state.client,
|
|
381204
|
+
instanceId,
|
|
381205
|
+
codAtendimento: turn.codAtendimento,
|
|
381206
|
+
text: turn.text,
|
|
381207
|
+
logger: this.logger
|
|
381208
|
+
});
|
|
381209
|
+
}
|
|
381210
|
+
async waitForTurn(instanceId, codAtendimento, text, timeoutMs) {
|
|
381211
|
+
const deadline = Date.now() + timeoutMs;
|
|
381212
|
+
while (Date.now() < deadline) {
|
|
381213
|
+
await new Promise((resolve2) => setTimeout(resolve2, 250));
|
|
381214
|
+
if (!this.ascFlowInstances.has(instanceId))
|
|
381215
|
+
return null;
|
|
381216
|
+
const ready = this.takeReadyTurn(instanceId, codAtendimento, text);
|
|
381217
|
+
if (ready)
|
|
381218
|
+
return ready;
|
|
381219
|
+
}
|
|
381220
|
+
this.logger.warn("[asc-flow] held the request to its deadline with no agent answer", {
|
|
381221
|
+
instanceId,
|
|
381222
|
+
codAtendimento,
|
|
381223
|
+
timeoutMs
|
|
381224
|
+
});
|
|
381225
|
+
return null;
|
|
381226
|
+
}
|
|
381031
381227
|
isRedeliveryOfTurnInFlight(instanceId, turn) {
|
|
381032
381228
|
const state = this.ascFlowInstances.get(instanceId);
|
|
381033
381229
|
const entry = state?.inFlight.get(turn.codAtendimento);
|