@leg3ndy/otto-bridge 0.6.7 → 0.6.9
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/README.md +4 -4
- package/dist/types.js +1 -1
- package/dist/whatsapp_background.js +219 -166
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,10 +33,10 @@ Enquanto o pacote nao estiver publicado, voce pode gerar um tarball local:
|
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
35
|
npm pack
|
|
36
|
-
npm install -g ./leg3ndy-otto-bridge-0.6.
|
|
36
|
+
npm install -g ./leg3ndy-otto-bridge-0.6.9.tgz
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
No `0.6.
|
|
39
|
+
No `0.6.9`, `playwright` deixa de ser opcional no `otto-bridge`. O primeiro `npm install -g @leg3ndy/otto-bridge` pode demorar mais porque instala o browser persistente usado pelo WhatsApp Web e pelos fluxos web em background do bridge.
|
|
40
40
|
|
|
41
41
|
## Publicacao
|
|
42
42
|
|
|
@@ -106,7 +106,7 @@ otto-bridge run --executor clawd-cursor --clawd-url http://127.0.0.1:3847
|
|
|
106
106
|
|
|
107
107
|
### WhatsApp Web em background
|
|
108
108
|
|
|
109
|
-
Fluxo recomendado no `0.6.
|
|
109
|
+
Fluxo recomendado no `0.6.9`:
|
|
110
110
|
|
|
111
111
|
```bash
|
|
112
112
|
otto-bridge extensions --install whatsappweb
|
|
@@ -116,7 +116,7 @@ otto-bridge extensions --status whatsappweb
|
|
|
116
116
|
|
|
117
117
|
O setup agora abre o login do WhatsApp Web em um browser persistente do proprio bridge. Depois do QR code, o Otto usa a sessao local em background, sem depender de aba visivel no Safari.
|
|
118
118
|
|
|
119
|
-
Contrato do `0.6.
|
|
119
|
+
Contrato do `0.6.9`:
|
|
120
120
|
|
|
121
121
|
- `otto-bridge extensions --setup whatsappweb`: autentica a sessao uma vez
|
|
122
122
|
- `otto-bridge run`: mantem o browser persistente do WhatsApp vivo em background enquanto o runtime estiver ativo, sem depender de uma aba aberta no Safari
|
package/dist/types.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const BRIDGE_CONFIG_VERSION = 1;
|
|
2
|
-
export const BRIDGE_VERSION = "0.6.
|
|
2
|
+
export const BRIDGE_VERSION = "0.6.9";
|
|
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;
|
|
@@ -115,8 +115,7 @@ export class WhatsAppBackgroundBrowser {
|
|
|
115
115
|
this.page = pages[0] || await this.context.newPage();
|
|
116
116
|
await this.ensureWhatsAppPage();
|
|
117
117
|
if (this.options.background) {
|
|
118
|
-
await this.
|
|
119
|
-
await this.hideAppFromDock();
|
|
118
|
+
await this.ensureBackgroundPlacement();
|
|
120
119
|
}
|
|
121
120
|
}
|
|
122
121
|
async close() {
|
|
@@ -131,6 +130,13 @@ export class WhatsAppBackgroundBrowser {
|
|
|
131
130
|
async waitForTimeout(timeoutMs) {
|
|
132
131
|
await this.page?.waitForTimeout(Math.max(0, Number(timeoutMs || 0)));
|
|
133
132
|
}
|
|
133
|
+
async ensureBackgroundPlacement() {
|
|
134
|
+
if (!this.options.background) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
await this.moveWindowOffscreen();
|
|
138
|
+
await this.hideAppFromDock();
|
|
139
|
+
}
|
|
134
140
|
async moveWindowOffscreen() {
|
|
135
141
|
const context = this.context;
|
|
136
142
|
const page = this.page;
|
|
@@ -164,15 +170,26 @@ export class WhatsAppBackgroundBrowser {
|
|
|
164
170
|
return;
|
|
165
171
|
}
|
|
166
172
|
const browserPid = await this.findBrowserProcessId();
|
|
167
|
-
if (
|
|
168
|
-
|
|
173
|
+
if (browserPid) {
|
|
174
|
+
const script = [
|
|
175
|
+
'tell application "System Events"',
|
|
176
|
+
`set visible of (first application process whose unix id is ${browserPid}) to false`,
|
|
177
|
+
"end tell",
|
|
178
|
+
].join("\n");
|
|
179
|
+
const hidden = await runCommand("osascript", ["-e", script]).then(() => true).catch(() => false);
|
|
180
|
+
if (hidden) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
169
183
|
}
|
|
170
|
-
const
|
|
184
|
+
const fallbackScript = [
|
|
171
185
|
'tell application "System Events"',
|
|
172
|
-
|
|
186
|
+
'set chromeLikeProcesses to (application processes whose frontmost is true and (name contains "Chrom" or name contains "Chrome"))',
|
|
187
|
+
'if (count of chromeLikeProcesses) > 0 then',
|
|
188
|
+
'set visible of item 1 of chromeLikeProcesses to false',
|
|
189
|
+
"end if",
|
|
173
190
|
"end tell",
|
|
174
191
|
].join("\n");
|
|
175
|
-
await runCommand("osascript", ["-e",
|
|
192
|
+
await runCommand("osascript", ["-e", fallbackScript]).catch(() => undefined);
|
|
176
193
|
}
|
|
177
194
|
async findBrowserProcessId() {
|
|
178
195
|
if (process.platform !== "darwin") {
|
|
@@ -305,177 +322,208 @@ export class WhatsAppBackgroundBrowser {
|
|
|
305
322
|
}
|
|
306
323
|
async selectConversation(contact) {
|
|
307
324
|
await this.ensureReady();
|
|
308
|
-
const prepared = await this.withPage((page) =>
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
function focusAndReplaceContent(element, value) {
|
|
322
|
-
element.focus();
|
|
323
|
-
const selection = window.getSelection();
|
|
324
|
-
const range = document.createRange();
|
|
325
|
-
range.selectNodeContents(element);
|
|
326
|
-
selection?.removeAllRanges();
|
|
327
|
-
selection?.addRange(range);
|
|
328
|
-
document.execCommand("selectAll", false);
|
|
329
|
-
document.execCommand("delete", false);
|
|
330
|
-
document.execCommand("insertText", false, value);
|
|
331
|
-
if ((element.innerText || "").trim() !== value.trim()) {
|
|
332
|
-
element.textContent = value;
|
|
325
|
+
const prepared = await this.withPage(async (page) => {
|
|
326
|
+
const focusResult = await page.evaluate(() => {
|
|
327
|
+
const normalize = (value) => String(value || "").normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim();
|
|
328
|
+
function isVisible(element) {
|
|
329
|
+
if (!(element instanceof HTMLElement))
|
|
330
|
+
return false;
|
|
331
|
+
const rect = element.getBoundingClientRect();
|
|
332
|
+
if (rect.width < 4 || rect.height < 4)
|
|
333
|
+
return false;
|
|
334
|
+
const style = window.getComputedStyle(element);
|
|
335
|
+
if (style.visibility === "hidden" || style.display === "none" || Number(style.opacity || "1") === 0)
|
|
336
|
+
return false;
|
|
337
|
+
return rect.bottom >= 0 && rect.right >= 0 && rect.top <= window.innerHeight && rect.left <= window.innerWidth;
|
|
333
338
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
339
|
+
const candidates = Array.from(document.querySelectorAll('div[contenteditable="true"][role="textbox"], input[role="textbox"], textarea, div[contenteditable="true"][data-tab], [data-testid="chat-list-search"] [contenteditable="true"]'))
|
|
340
|
+
.filter((node) => node instanceof HTMLElement)
|
|
341
|
+
.filter((node) => isVisible(node))
|
|
342
|
+
.map((node) => {
|
|
343
|
+
const rect = node.getBoundingClientRect();
|
|
344
|
+
const label = normalize(node.getAttribute("aria-label") || node.getAttribute("data-testid") || node.textContent || "");
|
|
345
|
+
let score = 0;
|
|
346
|
+
if (rect.left < window.innerWidth * 0.45)
|
|
347
|
+
score += 30;
|
|
348
|
+
if (rect.top < 240)
|
|
349
|
+
score += 30;
|
|
350
|
+
if (label.includes("search") || label.includes("pesquisar") || label.includes("procure") || label.includes("chat list"))
|
|
351
|
+
score += 80;
|
|
352
|
+
if (node.closest('[data-testid="chat-list-search"], header'))
|
|
353
|
+
score += 25;
|
|
354
|
+
return { node, score };
|
|
355
|
+
})
|
|
356
|
+
.sort((left, right) => right.score - left.score);
|
|
357
|
+
if (!candidates.length) {
|
|
358
|
+
return { ok: false, reason: "Nao achei o campo de busca do WhatsApp Web." };
|
|
359
|
+
}
|
|
360
|
+
const searchBox = candidates[0].node;
|
|
361
|
+
searchBox.focus();
|
|
362
|
+
searchBox.click();
|
|
363
|
+
return { ok: true };
|
|
364
|
+
});
|
|
365
|
+
if (!focusResult.ok) {
|
|
366
|
+
return focusResult;
|
|
356
367
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
368
|
+
await page.keyboard?.press("Meta+A").catch(() => undefined);
|
|
369
|
+
await page.keyboard?.press("Backspace").catch(() => undefined);
|
|
370
|
+
await page.keyboard?.type(String(contact || ""), { delay: 25 }).catch(() => undefined);
|
|
371
|
+
const typedValue = await page.evaluate(() => {
|
|
372
|
+
function isVisible(element) {
|
|
373
|
+
if (!(element instanceof HTMLElement))
|
|
374
|
+
return false;
|
|
375
|
+
const rect = element.getBoundingClientRect();
|
|
376
|
+
if (rect.width < 4 || rect.height < 4)
|
|
377
|
+
return false;
|
|
378
|
+
const style = window.getComputedStyle(element);
|
|
379
|
+
if (style.visibility === "hidden" || style.display === "none" || Number(style.opacity || "1") === 0)
|
|
380
|
+
return false;
|
|
381
|
+
return rect.bottom >= 0 && rect.right >= 0 && rect.top <= window.innerHeight && rect.left <= window.innerWidth;
|
|
382
|
+
}
|
|
383
|
+
const candidates = Array.from(document.querySelectorAll('div[contenteditable="true"][role="textbox"], input[role="textbox"], textarea, div[contenteditable="true"][data-tab], [data-testid="chat-list-search"] [contenteditable="true"]'))
|
|
384
|
+
.filter((node) => node instanceof HTMLElement)
|
|
385
|
+
.filter((node) => isVisible(node));
|
|
386
|
+
const field = candidates[0];
|
|
387
|
+
if (!field) {
|
|
388
|
+
return "";
|
|
389
|
+
}
|
|
390
|
+
if (field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement) {
|
|
391
|
+
return field.value || "";
|
|
392
|
+
}
|
|
393
|
+
return field.innerText || field.textContent || "";
|
|
394
|
+
});
|
|
395
|
+
return { ok: true, typedValue: String(typedValue || "") };
|
|
396
|
+
});
|
|
360
397
|
if (!prepared.ok) {
|
|
361
398
|
return false;
|
|
362
399
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
.
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
score
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
target.click
|
|
400
|
+
const deadline = Date.now() + 5_000;
|
|
401
|
+
while (Date.now() < deadline) {
|
|
402
|
+
await this.page?.waitForTimeout(650);
|
|
403
|
+
const result = await this.withPage((page) => page.evaluate((query) => {
|
|
404
|
+
const normalize = (value) => String(value || "").normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim();
|
|
405
|
+
const normalizedQuery = normalize(query);
|
|
406
|
+
function isVisible(element) {
|
|
407
|
+
if (!(element instanceof HTMLElement))
|
|
408
|
+
return false;
|
|
409
|
+
const rect = element.getBoundingClientRect();
|
|
410
|
+
if (rect.width < 6 || rect.height < 6)
|
|
411
|
+
return false;
|
|
412
|
+
const style = window.getComputedStyle(element);
|
|
413
|
+
if (style.visibility === "hidden" || style.display === "none" || Number(style.opacity || "1") === 0)
|
|
414
|
+
return false;
|
|
415
|
+
return rect.bottom >= 0 && rect.right >= 0 && rect.top <= window.innerHeight && rect.left <= window.innerWidth;
|
|
416
|
+
}
|
|
417
|
+
const titleNodes = Array.from(document.querySelectorAll('span[title], div[title]'))
|
|
418
|
+
.filter((node) => node instanceof HTMLElement)
|
|
419
|
+
.filter((node) => isVisible(node))
|
|
420
|
+
.map((node) => {
|
|
421
|
+
const text = normalize(node.getAttribute("title") || node.textContent || "");
|
|
422
|
+
let score = 0;
|
|
423
|
+
if (text === normalizedQuery)
|
|
424
|
+
score += 160;
|
|
425
|
+
if (text.includes(normalizedQuery))
|
|
426
|
+
score += 100;
|
|
427
|
+
if (normalizedQuery.includes(text) && text.length >= 3)
|
|
428
|
+
score += 50;
|
|
429
|
+
const container = node.closest('[data-testid="cell-frame-container"], [role="listitem"], [role="gridcell"], div[tabindex]');
|
|
430
|
+
if (container instanceof HTMLElement && isVisible(container))
|
|
431
|
+
score += 20;
|
|
432
|
+
return { node, container, score };
|
|
433
|
+
})
|
|
434
|
+
.filter((item) => item.score > 0)
|
|
435
|
+
.sort((left, right) => right.score - left.score);
|
|
436
|
+
if (!titleNodes.length) {
|
|
437
|
+
return { clicked: false, reason: "Nao achei uma conversa visivel com esse nome." };
|
|
438
|
+
}
|
|
439
|
+
const winner = titleNodes[0];
|
|
440
|
+
const target = winner.container instanceof HTMLElement ? winner.container : winner.node;
|
|
441
|
+
target.scrollIntoView({ block: "center", inline: "center", behavior: "auto" });
|
|
442
|
+
target.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, cancelable: true, view: window }));
|
|
443
|
+
target.dispatchEvent(new MouseEvent("mouseup", { bubbles: true, cancelable: true, view: window }));
|
|
444
|
+
target.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true, view: window }));
|
|
445
|
+
if (typeof target.click === "function") {
|
|
446
|
+
target.click();
|
|
447
|
+
}
|
|
448
|
+
return { clicked: true };
|
|
449
|
+
}, contact));
|
|
450
|
+
if (result.clicked === true) {
|
|
451
|
+
return true;
|
|
408
452
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
return result.clicked === true;
|
|
453
|
+
}
|
|
454
|
+
return false;
|
|
412
455
|
}
|
|
413
456
|
async sendMessage(text) {
|
|
414
457
|
await this.ensureReady();
|
|
415
|
-
const result = await this.withPage((page) =>
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
function clearAndFillComposer(element, nextValue) {
|
|
428
|
-
element.focus();
|
|
429
|
-
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
|
|
430
|
-
element.value = "";
|
|
431
|
-
element.dispatchEvent(new InputEvent("input", { bubbles: true, inputType: "deleteContentBackward", data: null }));
|
|
432
|
-
element.value = nextValue;
|
|
433
|
-
element.dispatchEvent(new InputEvent("input", { bubbles: true, inputType: "insertText", data: nextValue }));
|
|
434
|
-
return;
|
|
458
|
+
const result = await this.withPage(async (page) => {
|
|
459
|
+
const focusedComposer = await page.evaluate(() => {
|
|
460
|
+
function isVisible(element) {
|
|
461
|
+
if (!(element instanceof HTMLElement))
|
|
462
|
+
return false;
|
|
463
|
+
const rect = element.getBoundingClientRect();
|
|
464
|
+
if (rect.width < 6 || rect.height < 6)
|
|
465
|
+
return false;
|
|
466
|
+
const style = window.getComputedStyle(element);
|
|
467
|
+
if (style.visibility === "hidden" || style.display === "none" || Number(style.opacity || "1") === 0)
|
|
468
|
+
return false;
|
|
469
|
+
return rect.bottom >= 0 && rect.right >= 0 && rect.top <= window.innerHeight && rect.left <= window.innerWidth;
|
|
435
470
|
}
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
document.execCommand("delete", false);
|
|
443
|
-
document.execCommand("insertText", false, nextValue);
|
|
444
|
-
if ((element.innerText || "").trim() !== nextValue.trim()) {
|
|
445
|
-
element.textContent = nextValue;
|
|
471
|
+
const candidates = Array.from(document.querySelectorAll('footer div[contenteditable="true"], [data-testid="conversation-compose-box-input"], main footer [contenteditable="true"], footer textarea'))
|
|
472
|
+
.filter((node) => node instanceof HTMLElement)
|
|
473
|
+
.filter((node) => isVisible(node))
|
|
474
|
+
.sort((left, right) => right.getBoundingClientRect().top - left.getBoundingClientRect().top);
|
|
475
|
+
if (!candidates.length) {
|
|
476
|
+
return { ok: false, reason: "Nao achei o campo de mensagem do WhatsApp Web." };
|
|
446
477
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
return { sent: false, reason: "Nao achei o campo de mensagem do WhatsApp Web." };
|
|
478
|
+
const composer = candidates[0];
|
|
479
|
+
composer.focus();
|
|
480
|
+
composer.click();
|
|
481
|
+
return { ok: true };
|
|
482
|
+
});
|
|
483
|
+
if (!focusedComposer.ok) {
|
|
484
|
+
return { sent: false, reason: String(focusedComposer.reason || "Nao achei o campo de mensagem do WhatsApp Web.") };
|
|
455
485
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
486
|
+
await page.keyboard?.press("Meta+A").catch(() => undefined);
|
|
487
|
+
await page.keyboard?.press("Backspace").catch(() => undefined);
|
|
488
|
+
await page.keyboard?.type(String(text || ""), { delay: 20 }).catch(() => undefined);
|
|
489
|
+
return page.evaluate((value) => {
|
|
490
|
+
function isVisible(element) {
|
|
491
|
+
if (!(element instanceof HTMLElement))
|
|
492
|
+
return false;
|
|
493
|
+
const rect = element.getBoundingClientRect();
|
|
494
|
+
if (rect.width < 6 || rect.height < 6)
|
|
495
|
+
return false;
|
|
496
|
+
const style = window.getComputedStyle(element);
|
|
497
|
+
if (style.visibility === "hidden" || style.display === "none" || Number(style.opacity || "1") === 0)
|
|
498
|
+
return false;
|
|
499
|
+
return rect.bottom >= 0 && rect.right >= 0 && rect.top <= window.innerHeight && rect.left <= window.innerWidth;
|
|
500
|
+
}
|
|
501
|
+
const sendCandidates = Array.from(document.querySelectorAll('[data-testid="compose-btn-send"], button[aria-label*="Send"], button[aria-label*="Enviar"], span[data-icon="send"], div[role="button"][aria-label*="Send"], div[role="button"][aria-label*="Enviar"]'))
|
|
502
|
+
.map((node) => node instanceof HTMLElement ? (node.closest('button, div[role="button"]') || node) : null)
|
|
503
|
+
.filter((node) => node instanceof HTMLElement)
|
|
504
|
+
.filter((node) => isVisible(node));
|
|
505
|
+
const sendButton = sendCandidates[0];
|
|
506
|
+
if (sendButton instanceof HTMLElement) {
|
|
507
|
+
sendButton.scrollIntoView({ block: "center", inline: "center", behavior: "auto" });
|
|
508
|
+
sendButton.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, cancelable: true, view: window }));
|
|
509
|
+
sendButton.dispatchEvent(new MouseEvent("mouseup", { bubbles: true, cancelable: true, view: window }));
|
|
510
|
+
sendButton.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true, view: window }));
|
|
511
|
+
if (typeof sendButton.click === "function") {
|
|
512
|
+
sendButton.click();
|
|
513
|
+
}
|
|
514
|
+
return { sent: true };
|
|
471
515
|
}
|
|
516
|
+
const composerCandidates = Array.from(document.querySelectorAll('footer div[contenteditable="true"], [data-testid="conversation-compose-box-input"], main footer [contenteditable="true"], footer textarea'))
|
|
517
|
+
.filter((node) => node instanceof HTMLElement)
|
|
518
|
+
.filter((node) => isVisible(node))
|
|
519
|
+
.sort((left, right) => right.getBoundingClientRect().top - left.getBoundingClientRect().top);
|
|
520
|
+
const composer = composerCandidates[0];
|
|
521
|
+
composer?.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", code: "Enter", keyCode: 13, which: 13, bubbles: true }));
|
|
522
|
+
composer?.dispatchEvent(new KeyboardEvent("keypress", { key: "Enter", code: "Enter", keyCode: 13, which: 13, bubbles: true }));
|
|
523
|
+
composer?.dispatchEvent(new KeyboardEvent("keyup", { key: "Enter", code: "Enter", keyCode: 13, which: 13, bubbles: true }));
|
|
472
524
|
return { sent: true };
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
composer.dispatchEvent(new KeyboardEvent("keypress", { key: "Enter", code: "Enter", keyCode: 13, which: 13, bubbles: true }));
|
|
476
|
-
composer.dispatchEvent(new KeyboardEvent("keyup", { key: "Enter", code: "Enter", keyCode: 13, which: 13, bubbles: true }));
|
|
477
|
-
return { sent: true };
|
|
478
|
-
}, text));
|
|
525
|
+
}, text);
|
|
526
|
+
});
|
|
479
527
|
if (!result.sent) {
|
|
480
528
|
throw new Error(String(result.reason || "Nao consegui enviar a mensagem no WhatsApp Web."));
|
|
481
529
|
}
|
|
@@ -535,7 +583,7 @@ export class WhatsAppBackgroundBrowser {
|
|
|
535
583
|
if (!matched) {
|
|
536
584
|
return {
|
|
537
585
|
ok: false,
|
|
538
|
-
reason: "Nao consegui confirmar
|
|
586
|
+
reason: "Nao consegui confirmar na conversa do WhatsApp se a mensagem foi enviada.",
|
|
539
587
|
};
|
|
540
588
|
}
|
|
541
589
|
return { ok: true, reason: "" };
|
|
@@ -568,7 +616,12 @@ export class WhatsAppBackgroundBrowser {
|
|
|
568
616
|
if (!this.page) {
|
|
569
617
|
throw new Error("WhatsApp background browser nao conseguiu abrir a pagina.");
|
|
570
618
|
}
|
|
571
|
-
|
|
619
|
+
try {
|
|
620
|
+
return await handler(this.page);
|
|
621
|
+
}
|
|
622
|
+
finally {
|
|
623
|
+
await this.ensureBackgroundPlacement().catch(() => undefined);
|
|
624
|
+
}
|
|
572
625
|
}
|
|
573
626
|
}
|
|
574
627
|
function clipText(text, maxLength) {
|