@leg3ndy/otto-bridge 0.8.2 → 0.8.3
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.
|
@@ -7,6 +7,7 @@ import { JobCancelledError } from "./shared.js";
|
|
|
7
7
|
import { loadManagedBridgeExtensionState, saveManagedBridgeExtensionState, } from "../extensions.js";
|
|
8
8
|
import { postDeviceJson, uploadDeviceJobArtifact } from "../http.js";
|
|
9
9
|
import { WHATSAPP_WEB_URL, WhatsAppBackgroundBrowser, } from "../whatsapp_background.js";
|
|
10
|
+
import { verifyExpectedWhatsAppMessage } from "../whatsapp_verification.js";
|
|
10
11
|
const KNOWN_APPS = [
|
|
11
12
|
{ canonical: "Safari", patterns: [/\bsafari\b/i] },
|
|
12
13
|
{ canonical: "Google Chrome", patterns: [/\bgoogle chrome\b/i, /\bchrome\b/i] },
|
|
@@ -1344,10 +1345,6 @@ export class NativeMacOSJobExecutor {
|
|
|
1344
1345
|
await reporter.progress(progressPercent, `Enviando a mensagem para ${action.contact} no WhatsApp`);
|
|
1345
1346
|
await this.sendWhatsAppMessage(action.text);
|
|
1346
1347
|
await delay(900);
|
|
1347
|
-
const verification = await this.verifyWhatsAppLastMessageAgainstBaseline(action.text, beforeSend.messages);
|
|
1348
|
-
if (!verification.ok) {
|
|
1349
|
-
throw new Error(verification.reason || `Nao consegui confirmar o envio da mensagem para ${action.contact} no WhatsApp.`);
|
|
1350
|
-
}
|
|
1351
1348
|
const afterSend = await this.readWhatsAppVisibleConversation(action.contact, Math.max(12, beforeSend.messages.length + 4)).catch(() => null);
|
|
1352
1349
|
resultPayload.whatsapp = {
|
|
1353
1350
|
action: "send_message",
|
|
@@ -1356,6 +1353,12 @@ export class NativeMacOSJobExecutor {
|
|
|
1356
1353
|
messages: afterSend?.messages || [],
|
|
1357
1354
|
summary: afterSend?.summary || "",
|
|
1358
1355
|
};
|
|
1356
|
+
const verification = await this.verifyWhatsAppLastMessageAgainstBaseline(action.text, beforeSend.messages);
|
|
1357
|
+
if (!verification.ok) {
|
|
1358
|
+
resultPayload.summary = verification.reason || `Nao consegui confirmar o envio da mensagem para ${action.contact} no WhatsApp.`;
|
|
1359
|
+
await reporter.failed(verification.reason || `Nao consegui confirmar o envio da mensagem para ${action.contact} no WhatsApp.`, resultPayload);
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1359
1362
|
completionNotes.push(`Enviei no WhatsApp para ${action.contact}: ${clipText(action.text, 180)}`);
|
|
1360
1363
|
continue;
|
|
1361
1364
|
}
|
|
@@ -3164,42 +3167,7 @@ return { messages: messages.slice(-maxMessages) };
|
|
|
3164
3167
|
}
|
|
3165
3168
|
const baseline = Array.isArray(previousMessages) ? previousMessages : [];
|
|
3166
3169
|
const chat = await this.readWhatsAppVisibleConversation("Contato", Math.max(8, baseline.length + 2));
|
|
3167
|
-
|
|
3168
|
-
return {
|
|
3169
|
-
ok: false,
|
|
3170
|
-
reason: "Nao consegui ler as mensagens visiveis apos o envio no WhatsApp.",
|
|
3171
|
-
};
|
|
3172
|
-
}
|
|
3173
|
-
const normalizedExpected = normalizeText(expectedText).slice(0, 120);
|
|
3174
|
-
const normalizeMessage = (item) => `${normalizeText(item.author)}|${normalizeText(item.text)}`;
|
|
3175
|
-
const beforeSignature = baseline.map(normalizeMessage).join("\n");
|
|
3176
|
-
const afterSignature = chat.messages.map(normalizeMessage).join("\n");
|
|
3177
|
-
const changed = beforeSignature !== afterSignature;
|
|
3178
|
-
const beforeMatches = baseline.filter((item) => normalizeText(item.text).includes(normalizedExpected)).length;
|
|
3179
|
-
const afterMatches = chat.messages.filter((item) => normalizeText(item.text).includes(normalizedExpected)).length;
|
|
3180
|
-
const latest = chat.messages[chat.messages.length - 1] || null;
|
|
3181
|
-
const latestAuthor = normalizeText(latest?.author || "");
|
|
3182
|
-
const latestText = normalizeText(latest?.text || "");
|
|
3183
|
-
const latestMatches = latestText.includes(normalizedExpected) && (latestAuthor === "voce" || latestAuthor === "você");
|
|
3184
|
-
if ((changed && latestMatches) || (changed && afterMatches > beforeMatches)) {
|
|
3185
|
-
return { ok: true, reason: "" };
|
|
3186
|
-
}
|
|
3187
|
-
if (!changed) {
|
|
3188
|
-
return {
|
|
3189
|
-
ok: false,
|
|
3190
|
-
reason: "O WhatsApp nao mostrou mudanca visivel na conversa depois da tentativa de envio.",
|
|
3191
|
-
};
|
|
3192
|
-
}
|
|
3193
|
-
if (afterMatches <= beforeMatches) {
|
|
3194
|
-
return {
|
|
3195
|
-
ok: false,
|
|
3196
|
-
reason: "A conversa mudou, mas nao apareceu uma nova mensagem com o texto esperado no WhatsApp.",
|
|
3197
|
-
};
|
|
3198
|
-
}
|
|
3199
|
-
return {
|
|
3200
|
-
ok: false,
|
|
3201
|
-
reason: "Nao consegui confirmar visualmente a nova mensagem enviada no WhatsApp.",
|
|
3202
|
-
};
|
|
3170
|
+
return verifyExpectedWhatsAppMessage(expectedText, baseline, chat.messages);
|
|
3203
3171
|
}
|
|
3204
3172
|
async takeScreenshot(targetPath) {
|
|
3205
3173
|
const artifactsDir = path.join(os.homedir(), ".otto-bridge", "artifacts");
|
|
@@ -5,16 +5,10 @@ import path from "node:path";
|
|
|
5
5
|
import process from "node:process";
|
|
6
6
|
import { getBridgeHomeDir } from "./config.js";
|
|
7
7
|
import { MACOS_WHATSAPP_HELPER_SWIFT_SOURCE } from "./macos_whatsapp_helper_source.js";
|
|
8
|
+
import { verifyExpectedWhatsAppMessage } from "./whatsapp_verification.js";
|
|
8
9
|
function delay(ms) {
|
|
9
10
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
10
11
|
}
|
|
11
|
-
function normalizeText(value) {
|
|
12
|
-
return String(value || "")
|
|
13
|
-
.normalize("NFD")
|
|
14
|
-
.replace(/[\u0300-\u036f]/g, "")
|
|
15
|
-
.toLowerCase()
|
|
16
|
-
.trim();
|
|
17
|
-
}
|
|
18
12
|
async function runHelperCommand(command, args) {
|
|
19
13
|
return await new Promise((resolve, reject) => {
|
|
20
14
|
const child = spawn(command, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -743,42 +737,7 @@ export class MacOSWhatsAppHelperRuntime {
|
|
|
743
737
|
async verifyLastMessage(expectedText, previousMessages) {
|
|
744
738
|
const baseline = Array.isArray(previousMessages) ? previousMessages : [];
|
|
745
739
|
const chat = await this.readVisibleConversation(Math.max(8, baseline.length + 2));
|
|
746
|
-
|
|
747
|
-
return {
|
|
748
|
-
ok: false,
|
|
749
|
-
reason: "Nao consegui ler as mensagens visiveis apos o envio no WhatsApp.",
|
|
750
|
-
};
|
|
751
|
-
}
|
|
752
|
-
const normalizedExpected = normalizeText(expectedText).slice(0, 120);
|
|
753
|
-
const normalizeMessage = (item) => `${normalizeText(item.author)}|${normalizeText(item.text)}`;
|
|
754
|
-
const beforeSignature = baseline.map(normalizeMessage).join("\n");
|
|
755
|
-
const afterSignature = chat.messages.map(normalizeMessage).join("\n");
|
|
756
|
-
const changed = beforeSignature !== afterSignature;
|
|
757
|
-
const beforeMatches = baseline.filter((item) => normalizeText(item.text).includes(normalizedExpected)).length;
|
|
758
|
-
const afterMatches = chat.messages.filter((item) => normalizeText(item.text).includes(normalizedExpected)).length;
|
|
759
|
-
const latest = chat.messages[chat.messages.length - 1] || null;
|
|
760
|
-
const latestAuthor = normalizeText(latest?.author || "");
|
|
761
|
-
const latestText = normalizeText(latest?.text || "");
|
|
762
|
-
const latestMatches = latestText.includes(normalizedExpected) && (latestAuthor === "voce" || latestAuthor === "você");
|
|
763
|
-
if ((changed && latestMatches) || (changed && afterMatches > beforeMatches)) {
|
|
764
|
-
return { ok: true, reason: "" };
|
|
765
|
-
}
|
|
766
|
-
if (!changed) {
|
|
767
|
-
return {
|
|
768
|
-
ok: false,
|
|
769
|
-
reason: "O WhatsApp nao mostrou mudanca visivel na conversa depois da tentativa de envio.",
|
|
770
|
-
};
|
|
771
|
-
}
|
|
772
|
-
if (afterMatches <= beforeMatches) {
|
|
773
|
-
return {
|
|
774
|
-
ok: false,
|
|
775
|
-
reason: "A conversa mudou, mas nao apareceu uma nova mensagem com o texto esperado no WhatsApp.",
|
|
776
|
-
};
|
|
777
|
-
}
|
|
778
|
-
return {
|
|
779
|
-
ok: false,
|
|
780
|
-
reason: "Nao consegui confirmar na conversa do WhatsApp se a nova mensagem foi enviada.",
|
|
781
|
-
};
|
|
740
|
+
return verifyExpectedWhatsAppMessage(expectedText, baseline, chat.messages);
|
|
782
741
|
}
|
|
783
742
|
handleStdout(chunk) {
|
|
784
743
|
this.stdoutBuffer += chunk;
|
package/dist/types.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const BRIDGE_CONFIG_VERSION = 1;
|
|
2
|
-
export const BRIDGE_VERSION = "0.8.
|
|
2
|
+
export const BRIDGE_VERSION = "0.8.3";
|
|
3
3
|
export const BRIDGE_PACKAGE_NAME = "@leg3ndy/otto-bridge";
|
|
4
4
|
export const DEFAULT_API_BASE_URL = "http://localhost:8000";
|
|
5
5
|
export const DEFAULT_POLL_INTERVAL_MS = 3000;
|
|
@@ -5,6 +5,7 @@ import process from "node:process";
|
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
6
|
import { getBridgeHomeDir } from "./config.js";
|
|
7
7
|
import { checkMacOSWhatsAppHelperAvailability, MacOSWhatsAppHelperRuntime, } from "./macos_whatsapp_helper.js";
|
|
8
|
+
import { verifyExpectedWhatsAppMessage } from "./whatsapp_verification.js";
|
|
8
9
|
import { getConfiguredWhatsAppRuntimeProvider } from "./whatsapp_runtime_provider.js";
|
|
9
10
|
export const WHATSAPP_WEB_URL = "https://web.whatsapp.com";
|
|
10
11
|
const DEFAULT_SETUP_TIMEOUT_MS = 5 * 60 * 1000;
|
|
@@ -44,13 +45,6 @@ async function loadEmbeddedPlaywrightModule() {
|
|
|
44
45
|
}
|
|
45
46
|
throw new Error(`Playwright nao esta disponivel para o Otto Bridge. Reinstale o pacote \`@leg3ndy/otto-bridge\` para garantir o browser persistente do bridge. ${errors.length ? `Detalhes: ${errors.join(" | ")}` : ""}`.trim());
|
|
46
47
|
}
|
|
47
|
-
function normalizeText(value) {
|
|
48
|
-
return String(value || "")
|
|
49
|
-
.normalize("NFD")
|
|
50
|
-
.replace(/[\u0300-\u036f]/g, "")
|
|
51
|
-
.toLowerCase()
|
|
52
|
-
.trim();
|
|
53
|
-
}
|
|
54
48
|
export function getWhatsAppBrowserUserDataDir() {
|
|
55
49
|
return path.join(getBridgeHomeDir(), "extensions", "whatsappweb-profile");
|
|
56
50
|
}
|
|
@@ -806,42 +800,7 @@ export class WhatsAppBackgroundBrowser {
|
|
|
806
800
|
}
|
|
807
801
|
const baseline = Array.isArray(previousMessages) ? previousMessages : [];
|
|
808
802
|
const chat = await this.readVisibleConversation(Math.max(8, baseline.length + 2));
|
|
809
|
-
|
|
810
|
-
return {
|
|
811
|
-
ok: false,
|
|
812
|
-
reason: "Nao consegui ler as mensagens visiveis apos o envio no WhatsApp.",
|
|
813
|
-
};
|
|
814
|
-
}
|
|
815
|
-
const normalizedExpected = normalizeText(expectedText).slice(0, 120);
|
|
816
|
-
const normalizeMessage = (item) => `${normalizeText(item.author)}|${normalizeText(item.text)}`;
|
|
817
|
-
const beforeSignature = baseline.map(normalizeMessage).join("\n");
|
|
818
|
-
const afterSignature = chat.messages.map(normalizeMessage).join("\n");
|
|
819
|
-
const changed = beforeSignature !== afterSignature;
|
|
820
|
-
const beforeMatches = baseline.filter((item) => normalizeText(item.text).includes(normalizedExpected)).length;
|
|
821
|
-
const afterMatches = chat.messages.filter((item) => normalizeText(item.text).includes(normalizedExpected)).length;
|
|
822
|
-
const latest = chat.messages[chat.messages.length - 1] || null;
|
|
823
|
-
const latestAuthor = normalizeText(latest?.author || "");
|
|
824
|
-
const latestText = normalizeText(latest?.text || "");
|
|
825
|
-
const latestMatches = latestText.includes(normalizedExpected) && (latestAuthor === "voce" || latestAuthor === "você");
|
|
826
|
-
if ((changed && latestMatches) || (changed && afterMatches > beforeMatches)) {
|
|
827
|
-
return { ok: true, reason: "" };
|
|
828
|
-
}
|
|
829
|
-
if (!changed) {
|
|
830
|
-
return {
|
|
831
|
-
ok: false,
|
|
832
|
-
reason: "O WhatsApp nao mostrou mudanca visivel na conversa depois da tentativa de envio.",
|
|
833
|
-
};
|
|
834
|
-
}
|
|
835
|
-
if (afterMatches <= beforeMatches) {
|
|
836
|
-
return {
|
|
837
|
-
ok: false,
|
|
838
|
-
reason: "A conversa mudou, mas nao apareceu uma nova mensagem com o texto esperado no WhatsApp.",
|
|
839
|
-
};
|
|
840
|
-
}
|
|
841
|
-
return {
|
|
842
|
-
ok: false,
|
|
843
|
-
reason: "Nao consegui confirmar na conversa do WhatsApp se a nova mensagem foi enviada.",
|
|
844
|
-
};
|
|
803
|
+
return verifyExpectedWhatsAppMessage(expectedText, baseline, chat.messages);
|
|
845
804
|
}
|
|
846
805
|
async ensureWhatsAppPage() {
|
|
847
806
|
const page = this.page;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
function normalizeVerificationText(value) {
|
|
2
|
+
return String(value || "")
|
|
3
|
+
.normalize("NFD")
|
|
4
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
5
|
+
.replace(/\s+/g, " ")
|
|
6
|
+
.toLowerCase()
|
|
7
|
+
.trim();
|
|
8
|
+
}
|
|
9
|
+
function isOutboundAuthor(author) {
|
|
10
|
+
const normalized = normalizeVerificationText(author);
|
|
11
|
+
return normalized === "voce" || normalized === "you";
|
|
12
|
+
}
|
|
13
|
+
function messageSignature(messages) {
|
|
14
|
+
return messages
|
|
15
|
+
.map((item) => `${normalizeVerificationText(item.author)}|${normalizeVerificationText(item.text)}`)
|
|
16
|
+
.join("\n");
|
|
17
|
+
}
|
|
18
|
+
function countMatches(messages, normalizedExpected) {
|
|
19
|
+
return messages.filter((item) => normalizeVerificationText(item.text).includes(normalizedExpected)).length;
|
|
20
|
+
}
|
|
21
|
+
function countOutboundMatches(messages, normalizedExpected) {
|
|
22
|
+
return messages.filter((item) => {
|
|
23
|
+
if (!isOutboundAuthor(item.author)) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
return normalizeVerificationText(item.text).includes(normalizedExpected);
|
|
27
|
+
}).length;
|
|
28
|
+
}
|
|
29
|
+
export function verifyExpectedWhatsAppMessage(expectedText, previousMessages, observedMessages) {
|
|
30
|
+
if (!observedMessages.length) {
|
|
31
|
+
return {
|
|
32
|
+
ok: false,
|
|
33
|
+
reason: "Nao consegui ler as mensagens visiveis apos o envio no WhatsApp.",
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const baseline = Array.isArray(previousMessages) ? previousMessages : [];
|
|
37
|
+
const normalizedExpected = normalizeVerificationText(expectedText).slice(0, 200);
|
|
38
|
+
const changed = messageSignature(baseline) !== messageSignature(observedMessages);
|
|
39
|
+
const beforeMatches = countMatches(baseline, normalizedExpected);
|
|
40
|
+
const afterMatches = countMatches(observedMessages, normalizedExpected);
|
|
41
|
+
const beforeOutboundMatches = countOutboundMatches(baseline, normalizedExpected);
|
|
42
|
+
const afterOutboundMatches = countOutboundMatches(observedMessages, normalizedExpected);
|
|
43
|
+
const latestOutbound = [...observedMessages].reverse().find((item) => isOutboundAuthor(item.author)) || null;
|
|
44
|
+
const latestOutboundMatches = latestOutbound
|
|
45
|
+
? normalizeVerificationText(latestOutbound.text).includes(normalizedExpected)
|
|
46
|
+
: false;
|
|
47
|
+
if ((changed && latestOutboundMatches) || (changed && afterOutboundMatches > beforeOutboundMatches) || (changed && afterMatches > beforeMatches)) {
|
|
48
|
+
return { ok: true, reason: "" };
|
|
49
|
+
}
|
|
50
|
+
if (!changed) {
|
|
51
|
+
return {
|
|
52
|
+
ok: false,
|
|
53
|
+
reason: "O WhatsApp nao mostrou mudanca visivel na conversa depois da tentativa de envio.",
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (afterMatches <= beforeMatches) {
|
|
57
|
+
return {
|
|
58
|
+
ok: false,
|
|
59
|
+
reason: "A conversa mudou, mas nao apareceu uma nova mensagem com o texto esperado no WhatsApp.",
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
ok: false,
|
|
64
|
+
reason: "Nao consegui confirmar na conversa do WhatsApp se a nova mensagem foi enviada.",
|
|
65
|
+
};
|
|
66
|
+
}
|