@rubytech/create-maxy 1.0.801 → 1.0.802
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/package.json +1 -1
- package/payload/platform/plugins/whatsapp/PLUGIN.md +4 -2
- package/payload/server/chunk-3SCIVS2T.js +9910 -0
- package/payload/server/chunk-5ABJJQ5K.js +3465 -0
- package/payload/server/client-pool-S4UZCYDJ.js +29 -0
- package/payload/server/maxy-edge.js +2 -2
- package/payload/server/server.js +376 -176
package/payload/server/server.js
CHANGED
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
vncLog,
|
|
51
51
|
waitForExit,
|
|
52
52
|
writeChromiumWrapper
|
|
53
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-3SCIVS2T.js";
|
|
54
54
|
import {
|
|
55
55
|
ACCOUNTS_DIR,
|
|
56
56
|
GREETING_DIRECTIVE,
|
|
@@ -113,7 +113,7 @@ import {
|
|
|
113
113
|
verifyAndGetConversationUpdatedAt,
|
|
114
114
|
verifyConversationOwnership,
|
|
115
115
|
writeAdminUserAndPerson
|
|
116
|
-
} from "./chunk-
|
|
116
|
+
} from "./chunk-5ABJJQ5K.js";
|
|
117
117
|
import {
|
|
118
118
|
__commonJS,
|
|
119
119
|
__toESM
|
|
@@ -3552,6 +3552,172 @@ async function sendMediaMessage(sock, to, media, opts) {
|
|
|
3552
3552
|
}
|
|
3553
3553
|
}
|
|
3554
3554
|
|
|
3555
|
+
// app/lib/whatsapp/persist-message.ts
|
|
3556
|
+
var TAG7 = "[whatsapp-persist]";
|
|
3557
|
+
var sessionWriteLocks = /* @__PURE__ */ new Map();
|
|
3558
|
+
async function persistWhatsAppMessage(input) {
|
|
3559
|
+
if (!input.body || !input.msgKeyId) return null;
|
|
3560
|
+
const messageId = `whatsapp-live:${input.accountId}:${input.remoteJid}:${input.msgKeyId}`;
|
|
3561
|
+
const senderTelephone = input.fromMe && input.selfPhone ? input.selfPhone : input.senderPhone;
|
|
3562
|
+
const dateSentIso = new Date(input.timestamp * 1e3).toISOString();
|
|
3563
|
+
const scope = deriveScope(input.sessionKey);
|
|
3564
|
+
const { givenName, familyName } = splitName(input.pushName);
|
|
3565
|
+
const prev = sessionWriteLocks.get(input.sessionKey);
|
|
3566
|
+
let release;
|
|
3567
|
+
const mine = new Promise((resolve25) => {
|
|
3568
|
+
release = resolve25;
|
|
3569
|
+
});
|
|
3570
|
+
const chained = (prev ?? Promise.resolve()).then(() => mine);
|
|
3571
|
+
sessionWriteLocks.set(input.sessionKey, chained);
|
|
3572
|
+
await prev;
|
|
3573
|
+
const t0 = Date.now();
|
|
3574
|
+
let session = null;
|
|
3575
|
+
try {
|
|
3576
|
+
session = getSession();
|
|
3577
|
+
const cypher = `
|
|
3578
|
+
MATCH (c:Conversation {sessionKey: $sessionKey})
|
|
3579
|
+
OPTIONAL MATCH (existingS:Person {telephone: $senderTelephone})
|
|
3580
|
+
OPTIONAL MATCH (existingM:Message {messageId: $messageId})
|
|
3581
|
+
WITH c,
|
|
3582
|
+
existingS IS NOT NULL AS senderReused,
|
|
3583
|
+
existingM IS NOT NULL AS messageExisted
|
|
3584
|
+
MERGE (s:Person {telephone: $senderTelephone})
|
|
3585
|
+
ON CREATE SET
|
|
3586
|
+
s.accountId = $accountId,
|
|
3587
|
+
s.givenName = $givenName,
|
|
3588
|
+
s.familyName = $familyName,
|
|
3589
|
+
s.source = 'whatsapp-live',
|
|
3590
|
+
s.scope = $scope,
|
|
3591
|
+
s.createdByAgent = 'whatsapp-live',
|
|
3592
|
+
s.createdBySource = 'whatsapp-live',
|
|
3593
|
+
s.createdAt = datetime()
|
|
3594
|
+
WITH c, senderReused, messageExisted, s
|
|
3595
|
+
MERGE (m:Message {messageId: $messageId})
|
|
3596
|
+
ON CREATE SET
|
|
3597
|
+
m:WhatsAppMessage,
|
|
3598
|
+
m.accountId = $accountId,
|
|
3599
|
+
m.conversationId = c.conversationId,
|
|
3600
|
+
m.source = 'whatsapp',
|
|
3601
|
+
m.createdByAgent = 'whatsapp-live',
|
|
3602
|
+
m.createdBySource = 'whatsapp-live',
|
|
3603
|
+
m.createdAt = datetime(),
|
|
3604
|
+
m.dateSent = datetime($dateSentIso),
|
|
3605
|
+
m.body = $body,
|
|
3606
|
+
m.fromMe = $fromMe,
|
|
3607
|
+
m.senderTelephone = $senderTelephone,
|
|
3608
|
+
m.senderName = $senderName,
|
|
3609
|
+
m.remoteJid = $remoteJid,
|
|
3610
|
+
m.msgKeyId = $msgKeyId,
|
|
3611
|
+
m.quotedId = $quotedId,
|
|
3612
|
+
m.quotedSender = $quotedSender,
|
|
3613
|
+
m.scope = $scope
|
|
3614
|
+
MERGE (m)-[:PART_OF]->(c)
|
|
3615
|
+
MERGE (s)-[sentRel:SENT]->(m)
|
|
3616
|
+
ON CREATE SET
|
|
3617
|
+
sentRel.source = 'whatsapp-live',
|
|
3618
|
+
sentRel.createdAt = datetime(),
|
|
3619
|
+
sentRel.selfSent = $fromMe
|
|
3620
|
+
WITH c, senderReused, messageExisted, m, s
|
|
3621
|
+
OPTIONAL MATCH (prior:Message:WhatsAppMessage)-[:PART_OF]->(c)
|
|
3622
|
+
WHERE prior.dateSent <= datetime($dateSentIso)
|
|
3623
|
+
AND prior.messageId <> $messageId
|
|
3624
|
+
WITH c, senderReused, messageExisted, m, s, prior
|
|
3625
|
+
ORDER BY prior.dateSent DESC, prior.messageId DESC
|
|
3626
|
+
LIMIT 1
|
|
3627
|
+
FOREACH (_ IN CASE WHEN prior IS NULL OR messageExisted THEN [] ELSE [1] END |
|
|
3628
|
+
MERGE (prior)-[nextRel:NEXT]->(m)
|
|
3629
|
+
ON CREATE SET
|
|
3630
|
+
nextRel.source = 'whatsapp-live',
|
|
3631
|
+
nextRel.createdAt = datetime()
|
|
3632
|
+
)
|
|
3633
|
+
RETURN
|
|
3634
|
+
m.messageId AS messageId,
|
|
3635
|
+
messageExisted AS existed,
|
|
3636
|
+
prior.messageId AS prevMessageId,
|
|
3637
|
+
elementId(s) AS senderElementId,
|
|
3638
|
+
'Person' AS senderKind,
|
|
3639
|
+
senderReused AS senderReused
|
|
3640
|
+
`;
|
|
3641
|
+
const params = {
|
|
3642
|
+
sessionKey: input.sessionKey,
|
|
3643
|
+
messageId,
|
|
3644
|
+
accountId: input.accountId,
|
|
3645
|
+
senderTelephone,
|
|
3646
|
+
senderName: input.pushName ?? null,
|
|
3647
|
+
givenName,
|
|
3648
|
+
familyName,
|
|
3649
|
+
remoteJid: input.remoteJid,
|
|
3650
|
+
msgKeyId: input.msgKeyId,
|
|
3651
|
+
fromMe: input.fromMe,
|
|
3652
|
+
body: input.body,
|
|
3653
|
+
dateSentIso,
|
|
3654
|
+
quotedId: input.quoted?.id ?? null,
|
|
3655
|
+
quotedSender: input.quoted?.sender ?? null,
|
|
3656
|
+
scope
|
|
3657
|
+
};
|
|
3658
|
+
const result = await session.run(cypher, params);
|
|
3659
|
+
const ms = Date.now() - t0;
|
|
3660
|
+
if (result.records.length === 0) {
|
|
3661
|
+
console.error(`${TAG7} skip reason=conversation-not-found accountId=${input.accountId} sessionKey=${input.sessionKey} messageId=${messageId}`);
|
|
3662
|
+
return null;
|
|
3663
|
+
}
|
|
3664
|
+
const rec = result.records[0];
|
|
3665
|
+
const existed = rec.get("existed") === true;
|
|
3666
|
+
const prevMessageId = rec.get("prevMessageId") ?? null;
|
|
3667
|
+
const senderElementId = rec.get("senderElementId");
|
|
3668
|
+
const senderReused = rec.get("senderReused") === true;
|
|
3669
|
+
if (existed) {
|
|
3670
|
+
console.error(`${TAG7} noop reason=existing-message messageId=${messageId}`);
|
|
3671
|
+
return { messageId, created: false, senderElementId };
|
|
3672
|
+
}
|
|
3673
|
+
console.error(
|
|
3674
|
+
`${TAG7} sender-resolved telephone=${senderTelephone} nodeKind=Person elementId=${senderElementId} reused=${senderReused}`
|
|
3675
|
+
);
|
|
3676
|
+
if (prevMessageId) {
|
|
3677
|
+
console.error(`${TAG7} next-merge prior=${prevMessageId} next=${messageId}`);
|
|
3678
|
+
} else {
|
|
3679
|
+
console.error(`${TAG7} next-skip reason=no-prior msgId=${messageId}`);
|
|
3680
|
+
}
|
|
3681
|
+
console.error(
|
|
3682
|
+
`${TAG7} write messageId=${messageId} sessionKey=${input.sessionKey} fromMe=${input.fromMe} dateSent=${dateSentIso} bodyBytes=${Buffer.byteLength(input.body, "utf8")} ms=${ms}`
|
|
3683
|
+
);
|
|
3684
|
+
return { messageId, created: true, senderElementId };
|
|
3685
|
+
} catch (err) {
|
|
3686
|
+
const ms = Date.now() - t0;
|
|
3687
|
+
const reason = sanitizeReason(err);
|
|
3688
|
+
console.error(
|
|
3689
|
+
`${TAG7} FAIL accountId=${input.accountId} remoteJid=${input.remoteJid} msgKey=${input.msgKeyId} reason=${reason} ms=${ms}`
|
|
3690
|
+
);
|
|
3691
|
+
return null;
|
|
3692
|
+
} finally {
|
|
3693
|
+
release();
|
|
3694
|
+
if (sessionWriteLocks.get(input.sessionKey) === chained) {
|
|
3695
|
+
sessionWriteLocks.delete(input.sessionKey);
|
|
3696
|
+
}
|
|
3697
|
+
if (session) await session.close();
|
|
3698
|
+
}
|
|
3699
|
+
}
|
|
3700
|
+
function deriveScope(sessionKey) {
|
|
3701
|
+
const segments = sessionKey.split(":");
|
|
3702
|
+
return segments.length <= 2 ? "admin" : "public";
|
|
3703
|
+
}
|
|
3704
|
+
function splitName(pushName) {
|
|
3705
|
+
if (!pushName) return { givenName: null, familyName: null };
|
|
3706
|
+
const trimmed = pushName.trim();
|
|
3707
|
+
if (!trimmed) return { givenName: null, familyName: null };
|
|
3708
|
+
const firstSpace = trimmed.search(/\s/);
|
|
3709
|
+
if (firstSpace === -1) return { givenName: trimmed, familyName: null };
|
|
3710
|
+
return {
|
|
3711
|
+
givenName: trimmed.slice(0, firstSpace).trim(),
|
|
3712
|
+
familyName: trimmed.slice(firstSpace + 1).trim() || null
|
|
3713
|
+
};
|
|
3714
|
+
}
|
|
3715
|
+
function sanitizeReason(err) {
|
|
3716
|
+
if (!(err instanceof Error)) return String(err).slice(0, 200);
|
|
3717
|
+
const msg = err.message.replace(/bolt:\/\/[^\s)]+/g, "bolt://<redacted>").replace(/\b(neo4j|admin|password)=[^\s)]+/gi, "$1=<redacted>").slice(0, 200);
|
|
3718
|
+
return `${err.name}:${msg}`;
|
|
3719
|
+
}
|
|
3720
|
+
|
|
3555
3721
|
// app/lib/whatsapp/inbound/media.ts
|
|
3556
3722
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3557
3723
|
import { writeFile, mkdir } from "fs/promises";
|
|
@@ -3561,7 +3727,7 @@ import {
|
|
|
3561
3727
|
downloadContentFromMessage,
|
|
3562
3728
|
normalizeMessageContent as normalizeMessageContent2
|
|
3563
3729
|
} from "@whiskeysockets/baileys";
|
|
3564
|
-
var
|
|
3730
|
+
var TAG8 = "[whatsapp:media]";
|
|
3565
3731
|
var MEDIA_DIR = "/tmp/maxy-media";
|
|
3566
3732
|
function mimeToExt(mimetype) {
|
|
3567
3733
|
const map = {
|
|
@@ -3617,25 +3783,25 @@ async function downloadInboundMedia(msg, sock, opts) {
|
|
|
3617
3783
|
}
|
|
3618
3784
|
);
|
|
3619
3785
|
if (!buffer || buffer.length === 0) {
|
|
3620
|
-
console.error(`${
|
|
3786
|
+
console.error(`${TAG8} primary download returned empty, trying direct fallback`);
|
|
3621
3787
|
const downloadable = getDownloadableContent(content);
|
|
3622
3788
|
if (downloadable) {
|
|
3623
3789
|
try {
|
|
3624
3790
|
const stream = await downloadContentFromMessage(downloadable.downloadable, downloadable.mediaType);
|
|
3625
3791
|
buffer = await streamToBuffer(stream);
|
|
3626
3792
|
} catch (fallbackErr) {
|
|
3627
|
-
console.error(`${
|
|
3793
|
+
console.error(`${TAG8} direct download fallback failed: ${String(fallbackErr)}`);
|
|
3628
3794
|
}
|
|
3629
3795
|
}
|
|
3630
3796
|
}
|
|
3631
3797
|
if (!buffer || buffer.length === 0) {
|
|
3632
|
-
console.error(`${
|
|
3798
|
+
console.error(`${TAG8} download failed: empty buffer for ${mimetype ?? "unknown"}`);
|
|
3633
3799
|
return void 0;
|
|
3634
3800
|
}
|
|
3635
3801
|
if (buffer.length > maxBytes) {
|
|
3636
3802
|
const sizeMB = (buffer.length / (1024 * 1024)).toFixed(1);
|
|
3637
3803
|
const limitMB = (maxBytes / (1024 * 1024)).toFixed(0);
|
|
3638
|
-
console.error(`${
|
|
3804
|
+
console.error(`${TAG8} media too large type=${mimetype ?? "unknown"} size=${sizeMB}MB limit=${limitMB}MB`);
|
|
3639
3805
|
return void 0;
|
|
3640
3806
|
}
|
|
3641
3807
|
await mkdir(MEDIA_DIR, { recursive: true });
|
|
@@ -3644,20 +3810,20 @@ async function downloadInboundMedia(msg, sock, opts) {
|
|
|
3644
3810
|
const filePath = join4(MEDIA_DIR, filename);
|
|
3645
3811
|
await writeFile(filePath, buffer);
|
|
3646
3812
|
const sizeKB = (buffer.length / 1024).toFixed(0);
|
|
3647
|
-
console.error(`${
|
|
3813
|
+
console.error(`${TAG8} media downloaded type=${mimetype ?? "unknown"} size=${sizeKB}KB path=${filePath}`);
|
|
3648
3814
|
return {
|
|
3649
3815
|
path: filePath,
|
|
3650
3816
|
mimetype: mimetype ?? "application/octet-stream",
|
|
3651
3817
|
size: buffer.length
|
|
3652
3818
|
};
|
|
3653
3819
|
} catch (err) {
|
|
3654
|
-
console.error(`${
|
|
3820
|
+
console.error(`${TAG8} media download failed type=${mimetype ?? "unknown"} error=${String(err)}`);
|
|
3655
3821
|
return void 0;
|
|
3656
3822
|
}
|
|
3657
3823
|
}
|
|
3658
3824
|
|
|
3659
3825
|
// app/lib/whatsapp/inbound/debounce.ts
|
|
3660
|
-
var
|
|
3826
|
+
var TAG9 = "[whatsapp:debounce]";
|
|
3661
3827
|
var STT_TAG = "[whatsapp:stt-await]";
|
|
3662
3828
|
function createInboundDebouncer(opts) {
|
|
3663
3829
|
const { debounceMs, buildKey, onFlush, onError } = opts;
|
|
@@ -3688,7 +3854,7 @@ function createInboundDebouncer(opts) {
|
|
|
3688
3854
|
pending.delete(key);
|
|
3689
3855
|
const batchSize = batch.entries.length;
|
|
3690
3856
|
try {
|
|
3691
|
-
console.error(`${
|
|
3857
|
+
console.error(`${TAG9} debounce flush key=${key} batchSize=${batchSize}`);
|
|
3692
3858
|
const result = onFlush(batch.entries);
|
|
3693
3859
|
if (result && typeof result.catch === "function") {
|
|
3694
3860
|
result.catch(onError);
|
|
@@ -3760,7 +3926,7 @@ function createInboundDebouncer(opts) {
|
|
|
3760
3926
|
}
|
|
3761
3927
|
|
|
3762
3928
|
// app/lib/whatsapp/opening-hours.ts
|
|
3763
|
-
var
|
|
3929
|
+
var TAG10 = "[whatsapp:hours]";
|
|
3764
3930
|
async function isBusinessOpen(accountId) {
|
|
3765
3931
|
try {
|
|
3766
3932
|
const timezone = await resolveTimezone(accountId);
|
|
@@ -3778,7 +3944,7 @@ async function isBusinessOpen(accountId) {
|
|
|
3778
3944
|
{ accountId, dayOfWeek, previousDayOfWeek }
|
|
3779
3945
|
);
|
|
3780
3946
|
if (result.records.length === 0) {
|
|
3781
|
-
console.error(`${
|
|
3947
|
+
console.error(`${TAG10} [${accountId}] business hours check: no opening hours configured, treating as open`);
|
|
3782
3948
|
return { open: true, reason: "no opening hours configured" };
|
|
3783
3949
|
}
|
|
3784
3950
|
const specs = result.records.filter((r) => r.get("opens") != null && r.get("closes") != null).map((r) => ({
|
|
@@ -3787,7 +3953,7 @@ async function isBusinessOpen(accountId) {
|
|
|
3787
3953
|
closes: String(r.get("closes")).trim()
|
|
3788
3954
|
}));
|
|
3789
3955
|
if (specs.length === 0) {
|
|
3790
|
-
console.error(`${
|
|
3956
|
+
console.error(`${TAG10} [${accountId}] business hours check: no opening hours configured, treating as open`);
|
|
3791
3957
|
return { open: true, reason: "no opening hours configured" };
|
|
3792
3958
|
}
|
|
3793
3959
|
for (const spec of specs) {
|
|
@@ -3797,7 +3963,7 @@ async function isBusinessOpen(accountId) {
|
|
|
3797
3963
|
if (spec.opens > spec.closes && currentTime < spec.closes) {
|
|
3798
3964
|
const hoursStr = `${spec.opens}-${spec.closes} (${spec.day})`;
|
|
3799
3965
|
console.error(
|
|
3800
|
-
`${
|
|
3966
|
+
`${TAG10} [${accountId}] business hours check: open (day=${dayOfWeek}, time=${currentTime}, hours=${hoursStr})`
|
|
3801
3967
|
);
|
|
3802
3968
|
return {
|
|
3803
3969
|
open: true,
|
|
@@ -3810,7 +3976,7 @@ async function isBusinessOpen(accountId) {
|
|
|
3810
3976
|
} else if (isTimeInRange(currentTime, spec.opens, spec.closes)) {
|
|
3811
3977
|
const hoursStr = `${spec.opens}-${spec.closes}`;
|
|
3812
3978
|
console.error(
|
|
3813
|
-
`${
|
|
3979
|
+
`${TAG10} [${accountId}] business hours check: open (day=${dayOfWeek}, time=${currentTime}, hours=${hoursStr})`
|
|
3814
3980
|
);
|
|
3815
3981
|
return {
|
|
3816
3982
|
open: true,
|
|
@@ -3824,7 +3990,7 @@ async function isBusinessOpen(accountId) {
|
|
|
3824
3990
|
const todaySpecs = specs.filter((s) => s.day === dayOfWeek);
|
|
3825
3991
|
const allHours = todaySpecs.map((s) => `${s.opens}-${s.closes}`).join(", ") || "none today";
|
|
3826
3992
|
console.error(
|
|
3827
|
-
`${
|
|
3993
|
+
`${TAG10} [${accountId}] business hours check: closed (day=${dayOfWeek}, time=${currentTime}, hours=${allHours})`
|
|
3828
3994
|
);
|
|
3829
3995
|
return {
|
|
3830
3996
|
open: false,
|
|
@@ -3838,7 +4004,7 @@ async function isBusinessOpen(accountId) {
|
|
|
3838
4004
|
}
|
|
3839
4005
|
} catch (err) {
|
|
3840
4006
|
console.error(
|
|
3841
|
-
`${
|
|
4007
|
+
`${TAG10} [${accountId}] business hours check failed, treating as open: ${err instanceof Error ? err.message : String(err)}`
|
|
3842
4008
|
);
|
|
3843
4009
|
return { open: true, reason: "hours check failed (treating as open)" };
|
|
3844
4010
|
}
|
|
@@ -3884,7 +4050,7 @@ import { execFile } from "child_process";
|
|
|
3884
4050
|
import { unlink, stat } from "fs/promises";
|
|
3885
4051
|
import { promisify } from "util";
|
|
3886
4052
|
var execFileAsync = promisify(execFile);
|
|
3887
|
-
var
|
|
4053
|
+
var TAG11 = "[stt]";
|
|
3888
4054
|
var WHISPER_BINARY = process.env.WHISPER_BINARY ?? "/opt/whisper.cpp/build/bin/whisper-cli";
|
|
3889
4055
|
var WHISPER_MODEL = process.env.WHISPER_MODEL ?? "/opt/whisper.cpp/models/ggml-base.bin";
|
|
3890
4056
|
var WHISPER_TIMEOUT_MS = 20 * 60 * 1e3;
|
|
@@ -3895,11 +4061,11 @@ async function transcribe(audioPath, mimetype) {
|
|
|
3895
4061
|
const s = await stat(audioPath);
|
|
3896
4062
|
audioBytes = s.size;
|
|
3897
4063
|
} catch {
|
|
3898
|
-
console.error(`${
|
|
4064
|
+
console.error(`${TAG11} failed: file not readable path=${audioPath}`);
|
|
3899
4065
|
return void 0;
|
|
3900
4066
|
}
|
|
3901
4067
|
console.error(
|
|
3902
|
-
`${
|
|
4068
|
+
`${TAG11} start provider=whisper-local audio_bytes=${audioBytes} mimetype=${mimetype} path=${audioPath}`
|
|
3903
4069
|
);
|
|
3904
4070
|
const wavPath = audioPath.replace(/\.[^.]+$/, "") + ".wav";
|
|
3905
4071
|
try {
|
|
@@ -3918,7 +4084,7 @@ async function transcribe(audioPath, mimetype) {
|
|
|
3918
4084
|
], { timeout: 3e4 });
|
|
3919
4085
|
} catch (err) {
|
|
3920
4086
|
const reason = err instanceof Error ? err.message : String(err);
|
|
3921
|
-
console.error(`${
|
|
4087
|
+
console.error(`${TAG11} failed: ffmpeg conversion error=${reason}`);
|
|
3922
4088
|
return void 0;
|
|
3923
4089
|
}
|
|
3924
4090
|
try {
|
|
@@ -3936,20 +4102,20 @@ async function transcribe(audioPath, mimetype) {
|
|
|
3936
4102
|
const text = stdout.trim();
|
|
3937
4103
|
const durationMs = Date.now() - startMs;
|
|
3938
4104
|
if (!text) {
|
|
3939
|
-
console.error(`${
|
|
4105
|
+
console.error(`${TAG11} failed: whisper returned empty output duration_ms=${durationMs}`);
|
|
3940
4106
|
return void 0;
|
|
3941
4107
|
}
|
|
3942
4108
|
const langMatch = stderr.match(/auto-detected language:\s*(\w+)/);
|
|
3943
4109
|
const language = langMatch?.[1] ?? "unknown";
|
|
3944
4110
|
const words = text.split(/\s+/).filter(Boolean).length;
|
|
3945
4111
|
console.error(
|
|
3946
|
-
`${
|
|
4112
|
+
`${TAG11} done provider=whisper-local duration_ms=${durationMs} words=${words} lang=${language}`
|
|
3947
4113
|
);
|
|
3948
4114
|
return { text, language, durationMs };
|
|
3949
4115
|
} catch (err) {
|
|
3950
4116
|
const durationMs = Date.now() - startMs;
|
|
3951
4117
|
const reason = err instanceof Error ? err.message : String(err);
|
|
3952
|
-
console.error(`${
|
|
4118
|
+
console.error(`${TAG11} failed provider=whisper-local duration_ms=${durationMs} error=${reason}`);
|
|
3953
4119
|
return void 0;
|
|
3954
4120
|
} finally {
|
|
3955
4121
|
unlink(wavPath).catch(() => {
|
|
@@ -3958,7 +4124,7 @@ async function transcribe(audioPath, mimetype) {
|
|
|
3958
4124
|
}
|
|
3959
4125
|
|
|
3960
4126
|
// app/lib/whatsapp/manager.ts
|
|
3961
|
-
var
|
|
4127
|
+
var TAG12 = "[whatsapp:manager]";
|
|
3962
4128
|
var MAX_RECONNECT_ATTEMPTS = 10;
|
|
3963
4129
|
var MESSAGE_STORE_MAX = 500;
|
|
3964
4130
|
var GROUP_META_TTL = 5 * 60 * 1e3;
|
|
@@ -3978,7 +4144,7 @@ function storeMessage(storeKey, entry) {
|
|
|
3978
4144
|
if (entries.length > MESSAGE_STORE_MAX) {
|
|
3979
4145
|
const trimmed = entries.length - MESSAGE_STORE_MAX;
|
|
3980
4146
|
entries.splice(0, trimmed);
|
|
3981
|
-
console.error(`${
|
|
4147
|
+
console.error(`${TAG12} message store trimmed for ${storeKey}: ${trimmed} oldest messages removed`);
|
|
3982
4148
|
}
|
|
3983
4149
|
}
|
|
3984
4150
|
function deriveSessionKey(input) {
|
|
@@ -3997,7 +4163,7 @@ function deriveSessionKey(input) {
|
|
|
3997
4163
|
}
|
|
3998
4164
|
async function init(opts) {
|
|
3999
4165
|
if (initialized) {
|
|
4000
|
-
console.error(`${
|
|
4166
|
+
console.error(`${TAG12} already initialized`);
|
|
4001
4167
|
return;
|
|
4002
4168
|
}
|
|
4003
4169
|
configDir = opts.configDir;
|
|
@@ -4005,20 +4171,20 @@ async function init(opts) {
|
|
|
4005
4171
|
loadConfig(opts.accountConfig);
|
|
4006
4172
|
const accountIds = listCredentialAccountIds(configDir);
|
|
4007
4173
|
if (accountIds.length === 0) {
|
|
4008
|
-
console.error(`${
|
|
4174
|
+
console.error(`${TAG12} init: no stored WhatsApp credentials found`);
|
|
4009
4175
|
initialized = true;
|
|
4010
4176
|
return;
|
|
4011
4177
|
}
|
|
4012
|
-
console.error(`${
|
|
4178
|
+
console.error(`${TAG12} init: found ${accountIds.length} credentialed account(s): ${accountIds.join(", ")}`);
|
|
4013
4179
|
initialized = true;
|
|
4014
4180
|
for (const accountId of accountIds) {
|
|
4015
4181
|
const accountCfg = whatsAppConfig.accounts?.[accountId];
|
|
4016
4182
|
if (accountCfg?.enabled === false) {
|
|
4017
|
-
console.error(`${
|
|
4183
|
+
console.error(`${TAG12} skipping disabled account=${accountId}`);
|
|
4018
4184
|
continue;
|
|
4019
4185
|
}
|
|
4020
4186
|
startConnection(accountId).catch((err) => {
|
|
4021
|
-
console.error(`${
|
|
4187
|
+
console.error(`${TAG12} failed to auto-start account=${accountId}: ${formatError(err)}`);
|
|
4022
4188
|
});
|
|
4023
4189
|
}
|
|
4024
4190
|
}
|
|
@@ -4027,7 +4193,7 @@ async function startConnection(accountId) {
|
|
|
4027
4193
|
const authDir = resolveAuthDir(configDir, accountId);
|
|
4028
4194
|
const hasAuth = await authExists(authDir);
|
|
4029
4195
|
if (!hasAuth) {
|
|
4030
|
-
console.error(`${
|
|
4196
|
+
console.error(`${TAG12} no credentials for account=${accountId}`);
|
|
4031
4197
|
return;
|
|
4032
4198
|
}
|
|
4033
4199
|
await stopConnection(accountId);
|
|
@@ -4063,11 +4229,11 @@ async function stopConnection(accountId) {
|
|
|
4063
4229
|
conn.sock.ev.removeAllListeners("creds.update");
|
|
4064
4230
|
conn.sock.ws?.close?.();
|
|
4065
4231
|
} catch (err) {
|
|
4066
|
-
console.warn(`${
|
|
4232
|
+
console.warn(`${TAG12} socket cleanup error during stop account=${accountId}: ${String(err)}`);
|
|
4067
4233
|
}
|
|
4068
4234
|
}
|
|
4069
4235
|
connections.delete(accountId);
|
|
4070
|
-
console.error(`${
|
|
4236
|
+
console.error(`${TAG12} stopped account=${accountId}`);
|
|
4071
4237
|
}
|
|
4072
4238
|
function getStatus() {
|
|
4073
4239
|
return Array.from(connections.values()).map((conn) => ({
|
|
@@ -4108,9 +4274,9 @@ async function registerLoginSocket(accountId, sock, authDir) {
|
|
|
4108
4274
|
connections.set(accountId, conn);
|
|
4109
4275
|
try {
|
|
4110
4276
|
await sock.sendPresenceUpdate("available");
|
|
4111
|
-
console.error(`${
|
|
4277
|
+
console.error(`${TAG12} presence set to available account=${accountId}`);
|
|
4112
4278
|
} catch (err) {
|
|
4113
|
-
console.error(`${
|
|
4279
|
+
console.error(`${TAG12} presence update failed account=${accountId}: ${String(err)}`);
|
|
4114
4280
|
}
|
|
4115
4281
|
await runInitQueries(sock, {
|
|
4116
4282
|
accountId,
|
|
@@ -4121,7 +4287,7 @@ async function registerLoginSocket(accountId, sock, authDir) {
|
|
|
4121
4287
|
});
|
|
4122
4288
|
monitorInbound(conn);
|
|
4123
4289
|
watchForDisconnect(conn);
|
|
4124
|
-
console.error(`${
|
|
4290
|
+
console.error(`${TAG12} registered login socket for account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
|
|
4125
4291
|
}
|
|
4126
4292
|
function reloadConfig(accountConfig) {
|
|
4127
4293
|
loadConfig(accountConfig);
|
|
@@ -4145,7 +4311,7 @@ async function shutdown() {
|
|
|
4145
4311
|
const ids = Array.from(connections.keys());
|
|
4146
4312
|
await Promise.all(ids.map((id) => stopConnection(id)));
|
|
4147
4313
|
initialized = false;
|
|
4148
|
-
console.error(`${
|
|
4314
|
+
console.error(`${TAG12} shutdown complete`);
|
|
4149
4315
|
}
|
|
4150
4316
|
function loadConfig(accountConfig) {
|
|
4151
4317
|
try {
|
|
@@ -4157,12 +4323,12 @@ function loadConfig(accountConfig) {
|
|
|
4157
4323
|
if (parsed.success) {
|
|
4158
4324
|
whatsAppConfig = parsed.data;
|
|
4159
4325
|
} else {
|
|
4160
|
-
console.error(`${
|
|
4326
|
+
console.error(`${TAG12} config validation failed: ${parsed.error.message}`);
|
|
4161
4327
|
whatsAppConfig = {};
|
|
4162
4328
|
}
|
|
4163
4329
|
}
|
|
4164
4330
|
} catch (err) {
|
|
4165
|
-
console.error(`${
|
|
4331
|
+
console.error(`${TAG12} config load error: ${String(err)}`);
|
|
4166
4332
|
whatsAppConfig = {};
|
|
4167
4333
|
}
|
|
4168
4334
|
}
|
|
@@ -4173,13 +4339,13 @@ async function connectWithReconnect(conn) {
|
|
|
4173
4339
|
let cycleError = null;
|
|
4174
4340
|
let connectedAt;
|
|
4175
4341
|
try {
|
|
4176
|
-
console.error(`${
|
|
4342
|
+
console.error(`${TAG12} connecting account=${conn.accountId} attempt=${conn.reconnectAttempts}`);
|
|
4177
4343
|
const sock = await createWaSocket({
|
|
4178
4344
|
authDir: conn.authDir,
|
|
4179
4345
|
silent: true
|
|
4180
4346
|
});
|
|
4181
4347
|
conn.sock = sock;
|
|
4182
|
-
console.error(`${
|
|
4348
|
+
console.error(`${TAG12} socket created account=${conn.accountId} \u2014 waiting for connection`);
|
|
4183
4349
|
await waitForConnection(sock);
|
|
4184
4350
|
const selfId = readSelfId(conn.authDir);
|
|
4185
4351
|
connectedAt = Date.now();
|
|
@@ -4189,12 +4355,12 @@ async function connectWithReconnect(conn) {
|
|
|
4189
4355
|
conn.lastConnectedAt = connectedAt;
|
|
4190
4356
|
conn.lastError = void 0;
|
|
4191
4357
|
conn.lidMapping = sock.signalRepository?.lidMapping ?? null;
|
|
4192
|
-
console.error(`${
|
|
4358
|
+
console.error(`${TAG12} connected account=${conn.accountId} phone=${selfId.e164 ?? "unknown"}`);
|
|
4193
4359
|
try {
|
|
4194
4360
|
await sock.sendPresenceUpdate("available");
|
|
4195
|
-
console.error(`${
|
|
4361
|
+
console.error(`${TAG12} presence set to available account=${conn.accountId}`);
|
|
4196
4362
|
} catch (err) {
|
|
4197
|
-
console.error(`${
|
|
4363
|
+
console.error(`${TAG12} presence update failed account=${conn.accountId}: ${String(err)}`);
|
|
4198
4364
|
}
|
|
4199
4365
|
await runInitQueries(sock, {
|
|
4200
4366
|
accountId: conn.accountId,
|
|
@@ -4221,9 +4387,9 @@ async function connectWithReconnect(conn) {
|
|
|
4221
4387
|
}
|
|
4222
4388
|
const classification = classifyDisconnect(err);
|
|
4223
4389
|
conn.lastError = classification.message;
|
|
4224
|
-
console.error(`${
|
|
4390
|
+
console.error(`${TAG12} disconnect account=${conn.accountId}: ${classification.kind} \u2014 ${classification.message}`);
|
|
4225
4391
|
if (!classification.shouldRetry) {
|
|
4226
|
-
console.error(`${
|
|
4392
|
+
console.error(`${TAG12} terminal disconnect for account=${conn.accountId}, stopping reconnection`);
|
|
4227
4393
|
return;
|
|
4228
4394
|
}
|
|
4229
4395
|
}
|
|
@@ -4236,7 +4402,7 @@ async function connectWithReconnect(conn) {
|
|
|
4236
4402
|
if (decision.action === "abort") {
|
|
4237
4403
|
const stuckReason = `GIVING UP account=${conn.accountId} attempts=${decision.finalAttempts}/${maxAttempts} uptimeMsLast=${uptimeMs} stableThresholdMs=${STABLE_UPTIME_MS} lastError=${conn.lastError ?? "(none)"}`;
|
|
4238
4404
|
console.error(
|
|
4239
|
-
`${
|
|
4405
|
+
`${TAG12} ${stuckReason} \u2014 re-pair via QR or restart required; WhatsApp will not reconnect automatically`
|
|
4240
4406
|
);
|
|
4241
4407
|
conn.sessionStuckReason = stuckReason;
|
|
4242
4408
|
conn.lastError = stuckReason;
|
|
@@ -4245,17 +4411,17 @@ async function connectWithReconnect(conn) {
|
|
|
4245
4411
|
conn.reconnectAttempts = decision.nextAttempts;
|
|
4246
4412
|
if (decision.reason === "short-lived") {
|
|
4247
4413
|
console.error(
|
|
4248
|
-
`${
|
|
4414
|
+
`${TAG12} short-lived session account=${conn.accountId} uptimeMs=${uptimeMs} attempt=${decision.nextAttempts}/${maxAttempts} lastError=${conn.lastError ?? "(clean disconnect)"}`
|
|
4249
4415
|
);
|
|
4250
4416
|
} else {
|
|
4251
4417
|
console.error(
|
|
4252
|
-
`${
|
|
4418
|
+
`${TAG12} session stable account=${conn.accountId} uptimeMs=${uptimeMs} \u2014 reconnect counter reset`
|
|
4253
4419
|
);
|
|
4254
4420
|
}
|
|
4255
4421
|
if (decision.reason === "short-lived" || cycleError) {
|
|
4256
4422
|
const delay = computeBackoff(Math.max(1, decision.nextAttempts));
|
|
4257
4423
|
console.error(
|
|
4258
|
-
`${
|
|
4424
|
+
`${TAG12} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
4259
4425
|
);
|
|
4260
4426
|
await new Promise((resolve25) => {
|
|
4261
4427
|
const timer2 = setTimeout(resolve25, delay);
|
|
@@ -4289,11 +4455,11 @@ function watchForDisconnect(conn) {
|
|
|
4289
4455
|
conn.sock.ev.on("connection.update", (update) => {
|
|
4290
4456
|
if (update.connection === "close") {
|
|
4291
4457
|
if (connections.get(conn.accountId) !== conn) return;
|
|
4292
|
-
console.error(`${
|
|
4458
|
+
console.error(`${TAG12} socket disconnected for account=${conn.accountId}`);
|
|
4293
4459
|
conn.connected = false;
|
|
4294
4460
|
conn.sock = null;
|
|
4295
4461
|
connectWithReconnect(conn).catch((err) => {
|
|
4296
|
-
console.error(`${
|
|
4462
|
+
console.error(`${TAG12} reconnection failed for account=${conn.accountId}: ${formatError(err)}`);
|
|
4297
4463
|
});
|
|
4298
4464
|
}
|
|
4299
4465
|
});
|
|
@@ -4302,7 +4468,7 @@ async function getGroupMeta(conn, jid) {
|
|
|
4302
4468
|
const cached = conn.groupMetaCache.get(jid);
|
|
4303
4469
|
if (cached && cached.expires > Date.now()) return cached;
|
|
4304
4470
|
if (!conn.sock) return null;
|
|
4305
|
-
console.error(`${
|
|
4471
|
+
console.error(`${TAG12} group metadata cache miss for ${jid}, fetching from Baileys account=${conn.accountId}`);
|
|
4306
4472
|
try {
|
|
4307
4473
|
const meta = await conn.sock.groupMetadata(jid);
|
|
4308
4474
|
const participants = await Promise.all(
|
|
@@ -4317,12 +4483,12 @@ async function getGroupMeta(conn, jid) {
|
|
|
4317
4483
|
};
|
|
4318
4484
|
conn.groupMetaCache.set(jid, entry);
|
|
4319
4485
|
console.error(
|
|
4320
|
-
`${
|
|
4486
|
+
`${TAG12} group metadata cached for ${jid}: "${meta.subject}", ${participants.length} participants, expires in ${GROUP_META_TTL}ms account=${conn.accountId}`
|
|
4321
4487
|
);
|
|
4322
4488
|
return entry;
|
|
4323
4489
|
} catch (err) {
|
|
4324
4490
|
console.error(
|
|
4325
|
-
`${
|
|
4491
|
+
`${TAG12} group metadata fetch failed for ${jid}: ${err instanceof Error ? err.message : String(err)}, caching empty entry for ${GROUP_META_TTL}ms account=${conn.accountId}`
|
|
4326
4492
|
);
|
|
4327
4493
|
const emptyEntry = { expires: Date.now() + GROUP_META_TTL };
|
|
4328
4494
|
conn.groupMetaCache.set(jid, emptyEntry);
|
|
@@ -4333,7 +4499,7 @@ function monitorInbound(conn) {
|
|
|
4333
4499
|
if (!conn.sock || !onInboundMessage) return;
|
|
4334
4500
|
const sock = conn.sock;
|
|
4335
4501
|
const debounceMs = whatsAppConfig.accounts?.[conn.accountId]?.debounceMs ?? whatsAppConfig.debounceMs ?? 0;
|
|
4336
|
-
console.error(`${
|
|
4502
|
+
console.error(`${TAG12} monitorInbound started account=${conn.accountId} debounceMs=${debounceMs}`);
|
|
4337
4503
|
conn.debouncer = createInboundDebouncer({
|
|
4338
4504
|
debounceMs,
|
|
4339
4505
|
buildKey: (payload) => {
|
|
@@ -4346,7 +4512,7 @@ function monitorInbound(conn) {
|
|
|
4346
4512
|
onInboundMessage(entries[0]);
|
|
4347
4513
|
return;
|
|
4348
4514
|
}
|
|
4349
|
-
console.error(`${
|
|
4515
|
+
console.error(`${TAG12} debounce: combining ${entries.length} messages account=${conn.accountId} from=${entries[0].senderPhone}`);
|
|
4350
4516
|
const last = entries[entries.length - 1];
|
|
4351
4517
|
const mediaEntry = entries.find((e) => e.mediaPath);
|
|
4352
4518
|
const combinedText = entries.map((e) => e.text).filter(Boolean).join("\n");
|
|
@@ -4359,7 +4525,7 @@ function monitorInbound(conn) {
|
|
|
4359
4525
|
});
|
|
4360
4526
|
},
|
|
4361
4527
|
onError: (err) => {
|
|
4362
|
-
console.error(`${
|
|
4528
|
+
console.error(`${TAG12} debounce flush error account=${conn.accountId}: ${String(err)}`);
|
|
4363
4529
|
}
|
|
4364
4530
|
});
|
|
4365
4531
|
sock.ev.on("messages.upsert", async (upsert) => {
|
|
@@ -4387,13 +4553,47 @@ function monitorInbound(conn) {
|
|
|
4387
4553
|
});
|
|
4388
4554
|
const entries = messageStore.get(storeKey);
|
|
4389
4555
|
console.error(
|
|
4390
|
-
`${
|
|
4556
|
+
`${TAG12} stored message ${msg.key.id ?? "?"} for ${remoteJid} (type: ${upsert.type}, store size: ${entries?.length ?? 0}/${MESSAGE_STORE_MAX}) account=${conn.accountId}`
|
|
4391
4557
|
);
|
|
4392
4558
|
recordActivity({
|
|
4393
4559
|
accountId: conn.accountId,
|
|
4394
4560
|
direction: "inbound",
|
|
4395
4561
|
jid: remoteJid
|
|
4396
4562
|
});
|
|
4563
|
+
const fromMe = Boolean(msg.key.fromMe);
|
|
4564
|
+
const sessionKeyAgentType = isGroup ? "public" : fromMe ? "admin" : checkDmAccess({
|
|
4565
|
+
senderPhone,
|
|
4566
|
+
selfPhone: conn.selfPhone ?? "",
|
|
4567
|
+
config: whatsAppConfig,
|
|
4568
|
+
accountConfig: whatsAppConfig.accounts?.[conn.accountId]
|
|
4569
|
+
}).agentType;
|
|
4570
|
+
const sessionKey = deriveSessionKey({
|
|
4571
|
+
agentType: sessionKeyAgentType,
|
|
4572
|
+
accountId: conn.accountId,
|
|
4573
|
+
senderPhone,
|
|
4574
|
+
isGroup,
|
|
4575
|
+
groupJid: isGroup ? remoteJid : void 0,
|
|
4576
|
+
isOwnerMirror: fromMe && !isGroup
|
|
4577
|
+
});
|
|
4578
|
+
if (msg.key.id) {
|
|
4579
|
+
await persistWhatsAppMessage({
|
|
4580
|
+
accountId: conn.accountId,
|
|
4581
|
+
remoteJid,
|
|
4582
|
+
sessionKey,
|
|
4583
|
+
msgKeyId: msg.key.id,
|
|
4584
|
+
fromMe,
|
|
4585
|
+
senderPhone,
|
|
4586
|
+
selfPhone: conn.selfPhone,
|
|
4587
|
+
body: extracted.text,
|
|
4588
|
+
timestamp: ts,
|
|
4589
|
+
pushName: msg.pushName ?? void 0,
|
|
4590
|
+
quoted: extracted.quotedMessage ? {
|
|
4591
|
+
id: extracted.quotedMessage.id,
|
|
4592
|
+
body: extracted.quotedMessage.text,
|
|
4593
|
+
sender: extracted.quotedMessage.sender
|
|
4594
|
+
} : void 0
|
|
4595
|
+
});
|
|
4596
|
+
}
|
|
4397
4597
|
}
|
|
4398
4598
|
if (upsert.type === "append") {
|
|
4399
4599
|
const sendReceipts = whatsAppConfig.accounts?.[conn.accountId]?.sendReadReceipts ?? whatsAppConfig.sendReadReceipts ?? true;
|
|
@@ -4406,13 +4606,13 @@ function monitorInbound(conn) {
|
|
|
4406
4606
|
);
|
|
4407
4607
|
}
|
|
4408
4608
|
console.error(
|
|
4409
|
-
`${
|
|
4609
|
+
`${TAG12} append-type message ${msg.key.id ?? "?"} stored, dispatch skipped account=${conn.accountId}`
|
|
4410
4610
|
);
|
|
4411
4611
|
continue;
|
|
4412
4612
|
}
|
|
4413
4613
|
await handleInboundMessage(conn, msg);
|
|
4414
4614
|
} catch (err) {
|
|
4415
|
-
console.error(`${
|
|
4615
|
+
console.error(`${TAG12} inbound handler error account=${conn.accountId}: ${String(err)}`);
|
|
4416
4616
|
}
|
|
4417
4617
|
}
|
|
4418
4618
|
});
|
|
@@ -4422,31 +4622,31 @@ async function handleInboundMessage(conn, msg) {
|
|
|
4422
4622
|
const remoteJid = msg.key.remoteJid;
|
|
4423
4623
|
if (!remoteJid) return;
|
|
4424
4624
|
if (remoteJid === "status@broadcast") {
|
|
4425
|
-
console.error(`${
|
|
4625
|
+
console.error(`${TAG12} drop: status broadcast account=${conn.accountId}`);
|
|
4426
4626
|
return;
|
|
4427
4627
|
}
|
|
4428
4628
|
if (!msg.message) {
|
|
4429
|
-
console.error(`${
|
|
4629
|
+
console.error(`${TAG12} drop: empty message account=${conn.accountId} from=${remoteJid}`);
|
|
4430
4630
|
return;
|
|
4431
4631
|
}
|
|
4432
4632
|
const dedupKey = `${conn.accountId}:${remoteJid}:${msg.key.id}`;
|
|
4433
4633
|
if (isDuplicateInbound(dedupKey)) {
|
|
4434
|
-
console.error(`${
|
|
4634
|
+
console.error(`${TAG12} drop: duplicate account=${conn.accountId} key=${dedupKey}`);
|
|
4435
4635
|
return;
|
|
4436
4636
|
}
|
|
4437
4637
|
if (msg.key.fromMe) {
|
|
4438
4638
|
if (msg.key.id && isAgentSentMessage(msg.key.id)) {
|
|
4439
|
-
console.error(`${
|
|
4639
|
+
console.error(`${TAG12} drop: echo suppression account=${conn.accountId} msgId=${msg.key.id}`);
|
|
4440
4640
|
return;
|
|
4441
4641
|
}
|
|
4442
4642
|
const extracted2 = extractMessage(msg);
|
|
4443
4643
|
if (!extracted2.text) {
|
|
4444
|
-
console.error(`${
|
|
4644
|
+
console.error(`${TAG12} owner reply skipped \u2014 no text content account=${conn.accountId}`);
|
|
4445
4645
|
return;
|
|
4446
4646
|
}
|
|
4447
4647
|
const isGroup2 = isGroupJid(remoteJid);
|
|
4448
4648
|
const senderPhone2 = conn.selfPhone ?? "owner";
|
|
4449
|
-
console.error(`${
|
|
4649
|
+
console.error(`${TAG12} owner reply mirrored to session from=${senderPhone2} account=${conn.accountId}`);
|
|
4450
4650
|
const reply2 = async (text) => {
|
|
4451
4651
|
const currentSock = conn.sock;
|
|
4452
4652
|
if (!currentSock) throw new Error("WhatsApp disconnected \u2014 cannot reply");
|
|
@@ -4474,7 +4674,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
4474
4674
|
}
|
|
4475
4675
|
const extracted = extractMessage(msg);
|
|
4476
4676
|
if (!extracted.text && !extracted.mediaType) {
|
|
4477
|
-
console.error(`${
|
|
4677
|
+
console.error(`${TAG12} drop: no text or media account=${conn.accountId} from=${remoteJid}`);
|
|
4478
4678
|
return;
|
|
4479
4679
|
}
|
|
4480
4680
|
let mediaResult;
|
|
@@ -4484,7 +4684,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
4484
4684
|
maxBytes: maxMb * 1024 * 1024
|
|
4485
4685
|
});
|
|
4486
4686
|
if (!mediaResult) {
|
|
4487
|
-
console.error(`${
|
|
4687
|
+
console.error(`${TAG12} media download returned undefined account=${conn.accountId} type=${extracted.mediaType} from=${remoteJid}`);
|
|
4488
4688
|
}
|
|
4489
4689
|
}
|
|
4490
4690
|
const isGroup = isGroupJid(remoteJid);
|
|
@@ -4529,7 +4729,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
4529
4729
|
});
|
|
4530
4730
|
}
|
|
4531
4731
|
console.error(
|
|
4532
|
-
`${
|
|
4732
|
+
`${TAG12} inbound account=${conn.accountId} from=${senderPhone} group=${isGroup} access=${accessResult.allowed ? "allowed" : "blocked"}(${accessResult.reason}) agent=${accessResult.agentType}` + (extracted.mediaType ? ` media=${extracted.mediaType}` : "") + (mediaResult ? ` mediaPath=${mediaResult.path}` : "") + (extracted.quotedMessage ? ` replyTo=${extracted.quotedMessage.id}` : "")
|
|
4533
4733
|
);
|
|
4534
4734
|
if (!accessResult.allowed) return;
|
|
4535
4735
|
let groupSubject;
|
|
@@ -4572,15 +4772,15 @@ async function handleInboundMessage(conn, msg) {
|
|
|
4572
4772
|
if (accessResult.agentType === "public") {
|
|
4573
4773
|
const hoursResult = await isBusinessOpen(conn.accountId);
|
|
4574
4774
|
if (!hoursResult.open) {
|
|
4575
|
-
console.error(`${
|
|
4775
|
+
console.error(`${TAG12} [${conn.accountId}] dispatch skipped: business closed`);
|
|
4576
4776
|
const afterHoursMessage = whatsAppConfig.accounts?.[conn.accountId]?.afterHoursMessage ?? whatsAppConfig.afterHoursMessage;
|
|
4577
4777
|
if (afterHoursMessage) {
|
|
4578
4778
|
try {
|
|
4579
4779
|
await reply(afterHoursMessage);
|
|
4580
|
-
console.error(`${
|
|
4780
|
+
console.error(`${TAG12} [${conn.accountId}] after-hours auto-reply sent to ${remoteJid}`);
|
|
4581
4781
|
} catch (err) {
|
|
4582
4782
|
console.error(
|
|
4583
|
-
`${
|
|
4783
|
+
`${TAG12} [${conn.accountId}] after-hours auto-reply failed: ${err instanceof Error ? err.message : String(err)}`
|
|
4584
4784
|
);
|
|
4585
4785
|
}
|
|
4586
4786
|
}
|
|
@@ -5166,7 +5366,7 @@ async function storeGeneratedFile(accountId, filePath) {
|
|
|
5166
5366
|
import { writeFile as writeFile3, mkdtemp, rm } from "fs/promises";
|
|
5167
5367
|
import { tmpdir } from "os";
|
|
5168
5368
|
import { join as join5 } from "path";
|
|
5169
|
-
var
|
|
5369
|
+
var TAG13 = "[voice]";
|
|
5170
5370
|
var AUDIO_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
5171
5371
|
"audio/ogg",
|
|
5172
5372
|
"audio/webm",
|
|
@@ -5198,7 +5398,7 @@ async function transcribeVoiceNote(file, source) {
|
|
|
5198
5398
|
const bytes = file.size;
|
|
5199
5399
|
const mimeType = file.type;
|
|
5200
5400
|
console.error(
|
|
5201
|
-
`${
|
|
5401
|
+
`${TAG13} recording send source=${source} duration_ms=unknown bytes=${bytes} format=${mimeType}`
|
|
5202
5402
|
);
|
|
5203
5403
|
let tempDir;
|
|
5204
5404
|
let tempPath;
|
|
@@ -5210,7 +5410,7 @@ async function transcribeVoiceNote(file, source) {
|
|
|
5210
5410
|
await writeFile3(tempPath, buffer);
|
|
5211
5411
|
} catch (err) {
|
|
5212
5412
|
const reason = err instanceof Error ? err.message : String(err);
|
|
5213
|
-
console.error(`${
|
|
5413
|
+
console.error(`${TAG13} failed source=${source} error=temp-file-write: ${reason}`);
|
|
5214
5414
|
return { ok: false, error: "Could not process voice note" };
|
|
5215
5415
|
}
|
|
5216
5416
|
try {
|
|
@@ -5218,7 +5418,7 @@ async function transcribeVoiceNote(file, source) {
|
|
|
5218
5418
|
if (!sttResult) {
|
|
5219
5419
|
const elapsed2 = Date.now() - startMs;
|
|
5220
5420
|
console.error(
|
|
5221
|
-
`${
|
|
5421
|
+
`${TAG13} failed source=${source} error=transcription-failed duration_ms=${elapsed2}`
|
|
5222
5422
|
);
|
|
5223
5423
|
return { ok: false, error: "Could not transcribe voice note. Please try again or type your message." };
|
|
5224
5424
|
}
|
|
@@ -5226,7 +5426,7 @@ async function transcribeVoiceNote(file, source) {
|
|
|
5226
5426
|
const elapsed = Date.now() - startMs;
|
|
5227
5427
|
const words = rawText.split(/\s+/).filter(Boolean).length;
|
|
5228
5428
|
console.error(
|
|
5229
|
-
`${
|
|
5429
|
+
`${TAG13} transcribed source=${source} duration_ms=${elapsed} stt_ms=${sttResult.durationMs} words=${words}`
|
|
5230
5430
|
);
|
|
5231
5431
|
return {
|
|
5232
5432
|
ok: true,
|
|
@@ -5236,7 +5436,7 @@ async function transcribeVoiceNote(file, source) {
|
|
|
5236
5436
|
const elapsed = Date.now() - startMs;
|
|
5237
5437
|
const reason = err instanceof Error ? err.message : String(err);
|
|
5238
5438
|
console.error(
|
|
5239
|
-
`${
|
|
5439
|
+
`${TAG13} failed source=${source} error=${reason} duration_ms=${elapsed}`
|
|
5240
5440
|
);
|
|
5241
5441
|
return { ok: false, error: "Voice note transcription failed. Please try again or type your message." };
|
|
5242
5442
|
} finally {
|
|
@@ -5271,7 +5471,7 @@ var VERDICT_DEFINITIONS = {
|
|
|
5271
5471
|
};
|
|
5272
5472
|
|
|
5273
5473
|
// app/lib/inbound-gateway.ts
|
|
5274
|
-
var
|
|
5474
|
+
var TAG14 = "[inbound-gateway]";
|
|
5275
5475
|
var GATEWAY_TIMEOUT_MS = 1e4;
|
|
5276
5476
|
var MIN_WORDS_FOR_PROCESSING = 5;
|
|
5277
5477
|
function defaultResult(rawText, latencyMs) {
|
|
@@ -5394,11 +5594,11 @@ async function processInbound(rawText, channel) {
|
|
|
5394
5594
|
};
|
|
5395
5595
|
result.fallthrough = false;
|
|
5396
5596
|
console.warn(
|
|
5397
|
-
`${
|
|
5597
|
+
`${TAG14} short-message-injection channel=${channel} words=${words.length} latency_ms=${result.latencyMs}`
|
|
5398
5598
|
);
|
|
5399
5599
|
} else {
|
|
5400
5600
|
console.log(
|
|
5401
|
-
`${
|
|
5601
|
+
`${TAG14} passthrough channel=${channel} reason=short-message words=${words.length} latency_ms=${result.latencyMs}`
|
|
5402
5602
|
);
|
|
5403
5603
|
}
|
|
5404
5604
|
return result;
|
|
@@ -5415,13 +5615,13 @@ async function processInbound(rawText, channel) {
|
|
|
5415
5615
|
const latencyMs = Date.now() - startMs;
|
|
5416
5616
|
if (llmResult.kind === "fallback") {
|
|
5417
5617
|
console.warn(
|
|
5418
|
-
`${
|
|
5618
|
+
`${TAG14} fallthrough channel=${channel} reason=${llmResult.cause} detail=${llmResult.reason} latency_ms=${latencyMs}`
|
|
5419
5619
|
);
|
|
5420
5620
|
return defaultResult(rawText.trim(), latencyMs);
|
|
5421
5621
|
}
|
|
5422
5622
|
if (llmResult.kind !== "ok-tool") {
|
|
5423
5623
|
console.warn(
|
|
5424
|
-
`${
|
|
5624
|
+
`${TAG14} fallthrough channel=${channel} reason=no-tool-response latency_ms=${latencyMs}`
|
|
5425
5625
|
);
|
|
5426
5626
|
return defaultResult(rawText.trim(), latencyMs);
|
|
5427
5627
|
}
|
|
@@ -5431,7 +5631,7 @@ async function processInbound(rawText, channel) {
|
|
|
5431
5631
|
const verdict = input.verdict;
|
|
5432
5632
|
if (!processedText || !verdict || !["clean", "suspicious", "discard"].includes(verdict)) {
|
|
5433
5633
|
console.warn(
|
|
5434
|
-
`${
|
|
5634
|
+
`${TAG14} fallthrough channel=${channel} reason=mandatory-fields-missing latency_ms=${latencyMs} hasProcessedText=${!!processedText} verdict=${String(verdict)}`
|
|
5435
5635
|
);
|
|
5436
5636
|
return defaultResult(rawText.trim(), latencyMs);
|
|
5437
5637
|
}
|
|
@@ -5459,18 +5659,18 @@ async function processInbound(rawText, channel) {
|
|
|
5459
5659
|
fallthrough: false
|
|
5460
5660
|
};
|
|
5461
5661
|
console.log(
|
|
5462
|
-
`${
|
|
5662
|
+
`${TAG14} channel=${channel} verdict=${verdict} promptInjection=${promptInjectionRisk} intent=${intent} language=${language} complexity=${complexity} requiresHistory=${requiresHistory} rewrite=${!isAdmin && processedText !== rawText.trim()} searchQuery=${searchQuery ? "yes" : "null"} latency_ms=${latencyMs}`
|
|
5463
5663
|
);
|
|
5464
5664
|
if (verdict !== "clean") {
|
|
5465
5665
|
console.warn(
|
|
5466
|
-
`${
|
|
5666
|
+
`${TAG14} ${verdict.toUpperCase()} channel=${channel} reason=${reason} promptInjection=${promptInjectionRisk}`
|
|
5467
5667
|
);
|
|
5468
5668
|
}
|
|
5469
5669
|
return result;
|
|
5470
5670
|
} catch (err) {
|
|
5471
5671
|
const reason = err instanceof Error ? err.message : String(err);
|
|
5472
5672
|
console.warn(
|
|
5473
|
-
`${
|
|
5673
|
+
`${TAG14} fallthrough channel=${channel} reason=parse-error: ${reason} latency_ms=${latencyMs}`
|
|
5474
5674
|
);
|
|
5475
5675
|
return defaultResult(rawText.trim(), latencyMs);
|
|
5476
5676
|
}
|
|
@@ -6563,7 +6763,7 @@ function checkTelegramAccess(params) {
|
|
|
6563
6763
|
}
|
|
6564
6764
|
|
|
6565
6765
|
// server/routes/telegram.ts
|
|
6566
|
-
var
|
|
6766
|
+
var TAG15 = "[telegram-webhook]";
|
|
6567
6767
|
var TELEGRAM_API = "https://api.telegram.org";
|
|
6568
6768
|
function getWebhookSecret(botType) {
|
|
6569
6769
|
const filePath = botType === "admin" ? TELEGRAM_ADMIN_WEBHOOK_SECRET_FILE : TELEGRAM_WEBHOOK_SECRET_FILE;
|
|
@@ -6587,12 +6787,12 @@ async function handleInbound(params) {
|
|
|
6587
6787
|
const gatewayChannel = agentType === "admin" ? "telegram-admin" : "telegram-dm";
|
|
6588
6788
|
registerSession(sessionKey, agentType, accountId);
|
|
6589
6789
|
console.error(
|
|
6590
|
-
`${
|
|
6790
|
+
`${TAG15} session registered: sessionKey=${sessionKey} agentType=${agentType} botType=${botType} senderId=${senderId} accountId=${accountId.slice(0, 8)}\u2026`
|
|
6591
6791
|
);
|
|
6592
6792
|
const gatewayResult = await processInbound(text, gatewayChannel);
|
|
6593
6793
|
if (gatewayResult.screening.verdict === "discard") {
|
|
6594
6794
|
console.error(
|
|
6595
|
-
`${
|
|
6795
|
+
`${TAG15} discarded: senderId=${senderId} chatId=${chatId} reason=${gatewayResult.screening.reason}`
|
|
6596
6796
|
);
|
|
6597
6797
|
return;
|
|
6598
6798
|
}
|
|
@@ -6615,7 +6815,7 @@ async function handleInbound(params) {
|
|
|
6615
6815
|
}
|
|
6616
6816
|
} catch (err) {
|
|
6617
6817
|
console.error(
|
|
6618
|
-
`${
|
|
6818
|
+
`${TAG15} agent-error: chatId=${chatId} senderId=${senderId} error=${err instanceof Error ? err.message : String(err)}`
|
|
6619
6819
|
);
|
|
6620
6820
|
responseText = "I'm having trouble right now. Please try again in a moment.";
|
|
6621
6821
|
}
|
|
@@ -6633,12 +6833,12 @@ async function handleInbound(params) {
|
|
|
6633
6833
|
const data = await res.json();
|
|
6634
6834
|
if (!data.ok) {
|
|
6635
6835
|
console.error(
|
|
6636
|
-
`${
|
|
6836
|
+
`${TAG15} send-error: chatId=${chatId} error=${data.description ?? "unknown"}`
|
|
6637
6837
|
);
|
|
6638
6838
|
}
|
|
6639
6839
|
} catch (err) {
|
|
6640
6840
|
console.error(
|
|
6641
|
-
`${
|
|
6841
|
+
`${TAG15} send-error: chatId=${chatId} error=${err instanceof Error ? err.message : String(err)}`
|
|
6642
6842
|
);
|
|
6643
6843
|
}
|
|
6644
6844
|
}
|
|
@@ -6646,28 +6846,28 @@ var app6 = new Hono();
|
|
|
6646
6846
|
app6.post("/webhook", async (c) => {
|
|
6647
6847
|
const botType = c.req.query("bot");
|
|
6648
6848
|
if (!botType || botType !== "public" && botType !== "admin") {
|
|
6649
|
-
console.error(`${
|
|
6849
|
+
console.error(`${TAG15} invalid-bot-type: received=${botType ?? "missing"}`);
|
|
6650
6850
|
return c.json({ error: "Missing or invalid bot type" }, 400);
|
|
6651
6851
|
}
|
|
6652
6852
|
const storedSecret = getWebhookSecret(botType);
|
|
6653
6853
|
if (!storedSecret) {
|
|
6654
|
-
console.error(`${
|
|
6854
|
+
console.error(`${TAG15} secret=missing botType=${botType}`);
|
|
6655
6855
|
return c.json({ error: "Webhook not configured" }, 401);
|
|
6656
6856
|
}
|
|
6657
6857
|
const headerSecret = c.req.header("x-telegram-bot-api-secret-token");
|
|
6658
6858
|
if (!headerSecret || !verifyWebhookSecret(headerSecret, storedSecret)) {
|
|
6659
|
-
console.error(`${
|
|
6859
|
+
console.error(`${TAG15} secret=invalid botType=${botType}`);
|
|
6660
6860
|
return c.json({ error: "Unauthorized" }, 401);
|
|
6661
6861
|
}
|
|
6662
6862
|
const account = resolveAccount();
|
|
6663
6863
|
if (!account) {
|
|
6664
|
-
console.error(`${
|
|
6864
|
+
console.error(`${TAG15} config=no-account`);
|
|
6665
6865
|
return c.json({ error: "No account configured" }, 500);
|
|
6666
6866
|
}
|
|
6667
6867
|
const tgConfig = account.config.telegram ?? {};
|
|
6668
6868
|
const botToken = botType === "admin" ? tgConfig.adminBotToken : tgConfig.publicBotToken;
|
|
6669
6869
|
if (!botToken) {
|
|
6670
|
-
console.error(`${
|
|
6870
|
+
console.error(`${TAG15} config=no-token botType=${botType}`);
|
|
6671
6871
|
return c.json({ error: `No ${botType} bot token configured` }, 500);
|
|
6672
6872
|
}
|
|
6673
6873
|
let update;
|
|
@@ -6695,7 +6895,7 @@ app6.post("/webhook", async (c) => {
|
|
|
6695
6895
|
}
|
|
6696
6896
|
const accessResult = checkTelegramAccess({ senderId, botType, config: tgConfig });
|
|
6697
6897
|
console.error(
|
|
6698
|
-
`${
|
|
6898
|
+
`${TAG15} access: botType=${botType} senderId=${senderId} chatId=${chatId} allowed=${accessResult.allowed} reason=${accessResult.reason} agentType=${accessResult.agentType}`
|
|
6699
6899
|
);
|
|
6700
6900
|
if (!accessResult.allowed) {
|
|
6701
6901
|
return c.json({ ok: true });
|
|
@@ -6706,7 +6906,7 @@ app6.post("/webhook", async (c) => {
|
|
|
6706
6906
|
headers: { "Content-Type": "application/json" },
|
|
6707
6907
|
body: JSON.stringify({ callback_query_id: callbackId })
|
|
6708
6908
|
}).catch((err) => {
|
|
6709
|
-
console.error(`${
|
|
6909
|
+
console.error(`${TAG15} callback-ack-error: ${err instanceof Error ? err.message : String(err)}`);
|
|
6710
6910
|
});
|
|
6711
6911
|
}
|
|
6712
6912
|
handleInbound({
|
|
@@ -6719,7 +6919,7 @@ app6.post("/webhook", async (c) => {
|
|
|
6719
6919
|
agentType: accessResult.agentType
|
|
6720
6920
|
}).catch((err) => {
|
|
6721
6921
|
console.error(
|
|
6722
|
-
`${
|
|
6922
|
+
`${TAG15} unhandled-error: chatId=${chatId} senderId=${senderId} error=${err instanceof Error ? err.message : String(err)}`
|
|
6723
6923
|
);
|
|
6724
6924
|
});
|
|
6725
6925
|
return c.json({ ok: true });
|
|
@@ -6733,14 +6933,14 @@ import { realpathSync as realpathSync2, readdirSync as readdirSync2, readFileSyn
|
|
|
6733
6933
|
|
|
6734
6934
|
// app/lib/whatsapp/login.ts
|
|
6735
6935
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
6736
|
-
var
|
|
6936
|
+
var TAG16 = "[whatsapp:login]";
|
|
6737
6937
|
var ACTIVE_LOGIN_TTL_MS = 3 * 6e4;
|
|
6738
6938
|
var activeLogins = /* @__PURE__ */ new Map();
|
|
6739
6939
|
function closeSocket(sock) {
|
|
6740
6940
|
try {
|
|
6741
6941
|
sock.ws?.close?.();
|
|
6742
6942
|
} catch (err) {
|
|
6743
|
-
console.warn(`${
|
|
6943
|
+
console.warn(`${TAG16} socket close error during cleanup: ${String(err)}`);
|
|
6744
6944
|
}
|
|
6745
6945
|
}
|
|
6746
6946
|
function resetActiveLogin(accountId) {
|
|
@@ -6763,7 +6963,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
6763
6963
|
const current = activeLogins.get(accountId);
|
|
6764
6964
|
if (current?.id === login.id) {
|
|
6765
6965
|
current.connected = true;
|
|
6766
|
-
console.error(`${
|
|
6966
|
+
console.error(`${TAG16} loginConnectionLoop: connected account=${accountId} attempt=${attempt}`);
|
|
6767
6967
|
}
|
|
6768
6968
|
return;
|
|
6769
6969
|
} catch (err) {
|
|
@@ -6773,7 +6973,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
6773
6973
|
if (!classification.shouldRetry || attempt >= LOGIN_MAX_RECONNECTS) {
|
|
6774
6974
|
if (attempt >= LOGIN_MAX_RECONNECTS) {
|
|
6775
6975
|
console.error(
|
|
6776
|
-
`${
|
|
6976
|
+
`${TAG16} login reconnect attempts exhausted (${attempt}/${LOGIN_MAX_RECONNECTS}) \u2014 surfacing error to agent`
|
|
6777
6977
|
);
|
|
6778
6978
|
current.error = `Login failed after ${attempt} reconnect attempts: ${formatError(err)}`;
|
|
6779
6979
|
} else {
|
|
@@ -6785,7 +6985,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
6785
6985
|
attempt++;
|
|
6786
6986
|
const delay = LOGIN_RECONNECT_DELAYS[attempt - 1] ?? 8e3;
|
|
6787
6987
|
console.error(
|
|
6788
|
-
`${
|
|
6988
|
+
`${TAG16} status=${classification.statusCode ?? "unknown"} restart required \u2014 reconnecting with saved creds (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}) delay=${delay}ms`
|
|
6789
6989
|
);
|
|
6790
6990
|
closeSocket(current.sock);
|
|
6791
6991
|
await new Promise((r) => setTimeout(r, delay));
|
|
@@ -6796,7 +6996,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
6796
6996
|
current.sock = newSock;
|
|
6797
6997
|
} catch (sockErr) {
|
|
6798
6998
|
console.error(
|
|
6799
|
-
`${
|
|
6999
|
+
`${TAG16} reconnect socket creation failed (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}): ${String(sockErr)}`
|
|
6800
7000
|
);
|
|
6801
7001
|
current.error = `Reconnection failed: ${String(sockErr)}`;
|
|
6802
7002
|
return;
|
|
@@ -6810,7 +7010,7 @@ async function startLogin(opts) {
|
|
|
6810
7010
|
const hasAuth = await authExists(authDir);
|
|
6811
7011
|
const selfId = readSelfId(authDir);
|
|
6812
7012
|
console.error(
|
|
6813
|
-
`${
|
|
7013
|
+
`${TAG16} startLogin account=${accountId} force=${!!force} hasAuth=${hasAuth}` + (existing0 ? ` activeLogin={id=${existing0.id.slice(0, 8)},age=${Math.round((Date.now() - existing0.startedAt) / 1e3)}s,hasQr=${!!existing0.qr}}` : " activeLogin=none")
|
|
6814
7014
|
);
|
|
6815
7015
|
if (hasAuth && !force) {
|
|
6816
7016
|
const who = selfId.e164 ?? selfId.jid ?? "unknown";
|
|
@@ -6822,7 +7022,7 @@ async function startLogin(opts) {
|
|
|
6822
7022
|
await clearAuth(authDir);
|
|
6823
7023
|
const existing = activeLogins.get(accountId);
|
|
6824
7024
|
if (existing && isLoginFresh(existing) && existing.qrDataUrl && !force) {
|
|
6825
|
-
console.error(`${
|
|
7025
|
+
console.error(`${TAG16} startLogin account=${accountId} guard: returning existing QR (age=${Math.round((Date.now() - existing.startedAt) / 1e3)}s)`);
|
|
6826
7026
|
return {
|
|
6827
7027
|
qrDataUrl: existing.qrDataUrl,
|
|
6828
7028
|
qrRaw: existing.qr,
|
|
@@ -6830,7 +7030,7 @@ async function startLogin(opts) {
|
|
|
6830
7030
|
};
|
|
6831
7031
|
}
|
|
6832
7032
|
if (existing) {
|
|
6833
|
-
console.error(`${
|
|
7033
|
+
console.error(`${TAG16} startLogin account=${accountId} ${force ? "force override" : "stale/no-QR"}, resetting active login`);
|
|
6834
7034
|
}
|
|
6835
7035
|
resetActiveLogin(accountId);
|
|
6836
7036
|
let resolveQr = null;
|
|
@@ -6852,14 +7052,14 @@ async function startLogin(opts) {
|
|
|
6852
7052
|
onQr: (qr2) => {
|
|
6853
7053
|
loginQrCount++;
|
|
6854
7054
|
if (pendingQr) {
|
|
6855
|
-
console.error(`${
|
|
7055
|
+
console.error(`${TAG16} QR rotation #${loginQrCount} received for account=${accountId} \u2014 not forwarded (initial QR already captured)`);
|
|
6856
7056
|
return;
|
|
6857
7057
|
}
|
|
6858
7058
|
pendingQr = qr2;
|
|
6859
7059
|
const current = activeLogins.get(accountId);
|
|
6860
7060
|
if (current && !current.qr) current.qr = qr2;
|
|
6861
7061
|
clearTimeout(qrTimer);
|
|
6862
|
-
console.error(`${
|
|
7062
|
+
console.error(`${TAG16} QR #${loginQrCount} received for account=${accountId} \u2014 forwarding to caller`);
|
|
6863
7063
|
resolveQr?.(qr2);
|
|
6864
7064
|
}
|
|
6865
7065
|
});
|
|
@@ -6879,7 +7079,7 @@ async function startLogin(opts) {
|
|
|
6879
7079
|
activeLogins.set(accountId, login);
|
|
6880
7080
|
if (pendingQr && !login.qr) login.qr = pendingQr;
|
|
6881
7081
|
loginConnectionLoop(accountId, login).catch((err) => {
|
|
6882
|
-
console.error(`${
|
|
7082
|
+
console.error(`${TAG16} loginConnectionLoop unexpected error: ${String(err)}`);
|
|
6883
7083
|
const current = activeLogins.get(accountId);
|
|
6884
7084
|
if (current?.id === login.id) {
|
|
6885
7085
|
current.error = `Unexpected login error: ${String(err)}`;
|
|
@@ -6904,7 +7104,7 @@ async function waitForLogin(opts) {
|
|
|
6904
7104
|
const { accountId, timeoutMs = 6e4 } = opts;
|
|
6905
7105
|
const login = activeLogins.get(accountId);
|
|
6906
7106
|
console.error(
|
|
6907
|
-
`${
|
|
7107
|
+
`${TAG16} waitForLogin account=${accountId} timeout=${timeoutMs}ms` + (login ? ` login={id=${login.id.slice(0, 8)},age=${Math.round((Date.now() - login.startedAt) / 1e3)}s,connected=${login.connected},hasQr=${!!login.qr}}` : " login=none")
|
|
6908
7108
|
);
|
|
6909
7109
|
if (!login) {
|
|
6910
7110
|
return { connected: false, message: "No active WhatsApp login in progress." };
|
|
@@ -6917,7 +7117,7 @@ async function waitForLogin(opts) {
|
|
|
6917
7117
|
while (Date.now() < deadline) {
|
|
6918
7118
|
if (login.connected) {
|
|
6919
7119
|
const selfId = readSelfId(login.authDir);
|
|
6920
|
-
console.error(`${
|
|
7120
|
+
console.error(`${TAG16} login complete for account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
|
|
6921
7121
|
const sock = login.sock;
|
|
6922
7122
|
const authDir = login.authDir;
|
|
6923
7123
|
activeLogins.delete(accountId);
|
|
@@ -6937,7 +7137,7 @@ async function waitForLogin(opts) {
|
|
|
6937
7137
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
6938
7138
|
}
|
|
6939
7139
|
const elapsed = Math.round((Date.now() - (deadline - timeoutMs)) / 1e3);
|
|
6940
|
-
console.error(`${
|
|
7140
|
+
console.error(`${TAG16} waitForLogin timeout account=${accountId} elapsed=${elapsed}s \u2014 cleaning up active login`);
|
|
6941
7141
|
resetActiveLogin(accountId);
|
|
6942
7142
|
return { connected: false, message: "Login timed out. Try generating a new QR." };
|
|
6943
7143
|
}
|
|
@@ -7051,17 +7251,17 @@ function serializeWhatsAppSchema() {
|
|
|
7051
7251
|
}
|
|
7052
7252
|
|
|
7053
7253
|
// server/routes/whatsapp.ts
|
|
7054
|
-
var
|
|
7254
|
+
var TAG17 = "[whatsapp:api]";
|
|
7055
7255
|
var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
7056
7256
|
var app7 = new Hono();
|
|
7057
7257
|
app7.get("/status", (c) => {
|
|
7058
7258
|
try {
|
|
7059
7259
|
const status = getStatus();
|
|
7060
7260
|
const summary = status.map((a) => `${a.accountId}:${a.connected ? "up" : "down"}`).join(", ");
|
|
7061
|
-
console.error(`${
|
|
7261
|
+
console.error(`${TAG17} status accounts=${status.length} [${summary}]`);
|
|
7062
7262
|
return c.json({ accounts: status });
|
|
7063
7263
|
} catch (err) {
|
|
7064
|
-
console.error(`${
|
|
7264
|
+
console.error(`${TAG17} status error: ${String(err)}`);
|
|
7065
7265
|
return c.json({ error: String(err) }, 500);
|
|
7066
7266
|
}
|
|
7067
7267
|
});
|
|
@@ -7072,10 +7272,10 @@ app7.post("/login/start", async (c) => {
|
|
|
7072
7272
|
const force = body.force ?? false;
|
|
7073
7273
|
const authDir = join6(MAXY_DIR, "credentials", "whatsapp", accountId);
|
|
7074
7274
|
const result = await startLogin({ accountId, authDir, force });
|
|
7075
|
-
console.error(`${
|
|
7275
|
+
console.error(`${TAG17} login/start result account=${accountId} hasQr=${!!result.qrRaw}${result.selfPhone ? ` phone=${result.selfPhone}` : ""}`);
|
|
7076
7276
|
return c.json(result);
|
|
7077
7277
|
} catch (err) {
|
|
7078
|
-
console.error(`${
|
|
7278
|
+
console.error(`${TAG17} login/start error: ${String(err)}`);
|
|
7079
7279
|
return c.json({ error: String(err) }, 500);
|
|
7080
7280
|
}
|
|
7081
7281
|
});
|
|
@@ -7090,7 +7290,7 @@ app7.post("/login/wait", async (c) => {
|
|
|
7090
7290
|
try {
|
|
7091
7291
|
await registerLoginSocket(accountId, result.sock, result.authDir);
|
|
7092
7292
|
} catch (regErr) {
|
|
7093
|
-
console.error(`${
|
|
7293
|
+
console.error(`${TAG17} registerLoginSocket failed account=${accountId}: ${String(regErr)}`);
|
|
7094
7294
|
}
|
|
7095
7295
|
try {
|
|
7096
7296
|
const account = resolveAccount();
|
|
@@ -7098,16 +7298,16 @@ app7.post("/login/wait", async (c) => {
|
|
|
7098
7298
|
const persistResult = persistAfterPairing(account.accountDir, accountId, result.selfPhone ?? null);
|
|
7099
7299
|
configPersisted = persistResult.ok;
|
|
7100
7300
|
if (!persistResult.ok) {
|
|
7101
|
-
console.error(`${
|
|
7301
|
+
console.error(`${TAG17} config persist failed account=${accountId}: ${persistResult.error}`);
|
|
7102
7302
|
}
|
|
7103
7303
|
} else {
|
|
7104
|
-
console.error(`${
|
|
7304
|
+
console.error(`${TAG17} config persist skipped \u2014 no account resolved`);
|
|
7105
7305
|
}
|
|
7106
7306
|
} catch (persistErr) {
|
|
7107
|
-
console.error(`${
|
|
7307
|
+
console.error(`${TAG17} config persist error account=${accountId}: ${String(persistErr)}`);
|
|
7108
7308
|
}
|
|
7109
7309
|
}
|
|
7110
|
-
console.error(`${
|
|
7310
|
+
console.error(`${TAG17} login/wait result account=${accountId} connected=${result.connected}${result.selfPhone ? ` phone=${result.selfPhone}` : ""} configPersisted=${configPersisted}`);
|
|
7111
7311
|
return c.json({
|
|
7112
7312
|
connected: result.connected,
|
|
7113
7313
|
message: result.message,
|
|
@@ -7115,7 +7315,7 @@ app7.post("/login/wait", async (c) => {
|
|
|
7115
7315
|
configPersisted
|
|
7116
7316
|
});
|
|
7117
7317
|
} catch (err) {
|
|
7118
|
-
console.error(`${
|
|
7318
|
+
console.error(`${TAG17} login/wait error: ${String(err)}`);
|
|
7119
7319
|
return c.json({ error: String(err) }, 500);
|
|
7120
7320
|
}
|
|
7121
7321
|
});
|
|
@@ -7126,7 +7326,7 @@ app7.post("/disconnect", async (c) => {
|
|
|
7126
7326
|
await stopConnection(accountId);
|
|
7127
7327
|
return c.json({ disconnected: true, accountId });
|
|
7128
7328
|
} catch (err) {
|
|
7129
|
-
console.error(`${
|
|
7329
|
+
console.error(`${TAG17} disconnect error: ${String(err)}`);
|
|
7130
7330
|
return c.json({ error: String(err) }, 500);
|
|
7131
7331
|
}
|
|
7132
7332
|
});
|
|
@@ -7137,7 +7337,7 @@ app7.post("/reconnect", async (c) => {
|
|
|
7137
7337
|
await startConnection(accountId);
|
|
7138
7338
|
return c.json({ reconnecting: true, accountId });
|
|
7139
7339
|
} catch (err) {
|
|
7140
|
-
console.error(`${
|
|
7340
|
+
console.error(`${TAG17} reconnect error: ${String(err)}`);
|
|
7141
7341
|
return c.json({ error: String(err) }, 500);
|
|
7142
7342
|
}
|
|
7143
7343
|
});
|
|
@@ -7156,7 +7356,7 @@ app7.post("/send", async (c) => {
|
|
|
7156
7356
|
const result = await sendTextMessage(sock, to, text, { accountId });
|
|
7157
7357
|
return c.json(result);
|
|
7158
7358
|
} catch (err) {
|
|
7159
|
-
console.error(`${
|
|
7359
|
+
console.error(`${TAG17} send error: ${String(err)}`);
|
|
7160
7360
|
return c.json({ error: String(err) }, 500);
|
|
7161
7361
|
}
|
|
7162
7362
|
});
|
|
@@ -7177,7 +7377,7 @@ app7.post("/config", async (c) => {
|
|
|
7177
7377
|
return c.json({ ok: false, error: 'Missing required field "phone" (E.164 format, e.g. +441234567890).' }, 400);
|
|
7178
7378
|
}
|
|
7179
7379
|
const result = addAdminPhone(account.accountDir, phone);
|
|
7180
|
-
console.error(`${
|
|
7380
|
+
console.error(`${TAG17} config action=add-admin-phone phone=${phone} ok=${result.ok}`);
|
|
7181
7381
|
return c.json(result, result.ok ? 200 : 400);
|
|
7182
7382
|
}
|
|
7183
7383
|
case "remove-admin-phone": {
|
|
@@ -7185,12 +7385,12 @@ app7.post("/config", async (c) => {
|
|
|
7185
7385
|
return c.json({ ok: false, error: 'Missing required field "phone".' }, 400);
|
|
7186
7386
|
}
|
|
7187
7387
|
const result = removeAdminPhone(account.accountDir, phone);
|
|
7188
|
-
console.error(`${
|
|
7388
|
+
console.error(`${TAG17} config action=remove-admin-phone phone=${phone} ok=${result.ok}`);
|
|
7189
7389
|
return c.json(result, result.ok ? 200 : 400);
|
|
7190
7390
|
}
|
|
7191
7391
|
case "list-admin-phones": {
|
|
7192
7392
|
const phones = readAdminPhones(account.accountDir);
|
|
7193
|
-
console.error(`${
|
|
7393
|
+
console.error(`${TAG17} config action=list-admin-phones count=${phones.length}`);
|
|
7194
7394
|
return c.json({ ok: true, phones });
|
|
7195
7395
|
}
|
|
7196
7396
|
case "set-public-agent": {
|
|
@@ -7198,14 +7398,14 @@ app7.post("/config", async (c) => {
|
|
|
7198
7398
|
return c.json({ ok: false, error: 'Missing required field "slug" (the agent directory name, e.g. "my-agent").' }, 400);
|
|
7199
7399
|
}
|
|
7200
7400
|
const result = setPublicAgent(account.accountDir, slug);
|
|
7201
|
-
console.error(`${
|
|
7401
|
+
console.error(`${TAG17} config action=set-public-agent slug=${slug} ok=${result.ok}`);
|
|
7202
7402
|
return c.json(result, result.ok ? 200 : 400);
|
|
7203
7403
|
}
|
|
7204
7404
|
case "get-public-agent": {
|
|
7205
7405
|
const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
|
|
7206
7406
|
const targetGroup = typeof groupJid === "string" && groupJid.trim() ? groupJid.trim() : void 0;
|
|
7207
7407
|
const resolved = resolvePublicAgent(account.accountDir, { accountId: targetAccount, groupJid: targetGroup });
|
|
7208
|
-
console.error(`${
|
|
7408
|
+
console.error(`${TAG17} config action=get-public-agent accountId=${targetAccount} groupJid=${targetGroup ?? "none"} slug=${resolved?.slug ?? "none"} source=${resolved?.source ?? "none"}`);
|
|
7209
7409
|
return c.json({ ok: true, slug: resolved?.slug ?? null, source: resolved?.source ?? null });
|
|
7210
7410
|
}
|
|
7211
7411
|
case "set-group-public-agent": {
|
|
@@ -7217,7 +7417,7 @@ app7.post("/config", async (c) => {
|
|
|
7217
7417
|
}
|
|
7218
7418
|
const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
|
|
7219
7419
|
const result = setGroupPublicAgent(account.accountDir, targetAccount, groupJid, slug);
|
|
7220
|
-
console.error(`${
|
|
7420
|
+
console.error(`${TAG17} config action=set-group-public-agent accountId=${targetAccount} groupJid=${groupJid} slug=${slug} ok=${result.ok}`);
|
|
7221
7421
|
return c.json(result, result.ok ? 200 : 400);
|
|
7222
7422
|
}
|
|
7223
7423
|
case "unset-group-public-agent": {
|
|
@@ -7226,7 +7426,7 @@ app7.post("/config", async (c) => {
|
|
|
7226
7426
|
}
|
|
7227
7427
|
const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
|
|
7228
7428
|
const result = unsetGroupPublicAgent(account.accountDir, targetAccount, groupJid);
|
|
7229
|
-
console.error(`${
|
|
7429
|
+
console.error(`${TAG17} config action=unset-group-public-agent accountId=${targetAccount} groupJid=${groupJid} ok=${result.ok}`);
|
|
7230
7430
|
return c.json(result, result.ok ? 200 : 400);
|
|
7231
7431
|
}
|
|
7232
7432
|
case "list-public-agents": {
|
|
@@ -7243,26 +7443,26 @@ app7.post("/config", async (c) => {
|
|
|
7243
7443
|
const config = JSON.parse(readFileSync9(configPath2, "utf-8"));
|
|
7244
7444
|
agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
|
|
7245
7445
|
} catch {
|
|
7246
|
-
console.error(`${
|
|
7446
|
+
console.error(`${TAG17} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
|
|
7247
7447
|
}
|
|
7248
7448
|
}
|
|
7249
7449
|
} catch (err) {
|
|
7250
|
-
console.error(`${
|
|
7450
|
+
console.error(`${TAG17} config action=list-public-agents error="failed to scan agents directory: ${String(err)}"`);
|
|
7251
7451
|
}
|
|
7252
7452
|
}
|
|
7253
|
-
console.error(`${
|
|
7453
|
+
console.error(`${TAG17} config action=list-public-agents count=${agents.length}`);
|
|
7254
7454
|
return c.json({ ok: true, agents });
|
|
7255
7455
|
}
|
|
7256
7456
|
case "schema": {
|
|
7257
7457
|
const text = serializeWhatsAppSchema();
|
|
7258
|
-
console.error(`${
|
|
7458
|
+
console.error(`${TAG17} config action=schema`);
|
|
7259
7459
|
return c.json({ ok: true, text });
|
|
7260
7460
|
}
|
|
7261
7461
|
case "list-groups": {
|
|
7262
7462
|
const groupAccountId = accountId ?? "default";
|
|
7263
7463
|
const sock = getSocket(groupAccountId);
|
|
7264
7464
|
if (!sock) {
|
|
7265
|
-
console.error(`${
|
|
7465
|
+
console.error(`${TAG17} config action=list-groups error="not connected" accountId=${groupAccountId}`);
|
|
7266
7466
|
return c.json({ ok: false, error: `WhatsApp account "${groupAccountId}" is not connected. Connect first, then retry.` });
|
|
7267
7467
|
}
|
|
7268
7468
|
try {
|
|
@@ -7272,10 +7472,10 @@ app7.post("/config", async (c) => {
|
|
|
7272
7472
|
name: g.subject ?? g.id,
|
|
7273
7473
|
participantCount: Array.isArray(g.participants) ? g.participants.length : 0
|
|
7274
7474
|
}));
|
|
7275
|
-
console.error(`${
|
|
7475
|
+
console.error(`${TAG17} config action=list-groups count=${groups.length} accountId=${groupAccountId}`);
|
|
7276
7476
|
return c.json({ ok: true, groups });
|
|
7277
7477
|
} catch (err) {
|
|
7278
|
-
console.error(`${
|
|
7478
|
+
console.error(`${TAG17} config action=list-groups error="${String(err)}" accountId=${groupAccountId}`);
|
|
7279
7479
|
return c.json({ ok: false, error: `Failed to fetch groups: ${String(err)}` });
|
|
7280
7480
|
}
|
|
7281
7481
|
}
|
|
@@ -7285,12 +7485,12 @@ app7.post("/config", async (c) => {
|
|
|
7285
7485
|
}
|
|
7286
7486
|
const result = updateConfig(account.accountDir, fields);
|
|
7287
7487
|
const fieldNames = Object.keys(fields);
|
|
7288
|
-
console.error(`${
|
|
7488
|
+
console.error(`${TAG17} config action=update-config fields=[${fieldNames.join(",")}] ok=${result.ok}`);
|
|
7289
7489
|
return c.json(result, result.ok ? 200 : 400);
|
|
7290
7490
|
}
|
|
7291
7491
|
case "get-config": {
|
|
7292
7492
|
const waConfig = getConfig(account.accountDir);
|
|
7293
|
-
console.error(`${
|
|
7493
|
+
console.error(`${TAG17} config action=get-config`);
|
|
7294
7494
|
return c.json({ ok: true, config: waConfig });
|
|
7295
7495
|
}
|
|
7296
7496
|
default:
|
|
@@ -7300,7 +7500,7 @@ app7.post("/config", async (c) => {
|
|
|
7300
7500
|
);
|
|
7301
7501
|
}
|
|
7302
7502
|
} catch (err) {
|
|
7303
|
-
console.error(`${
|
|
7503
|
+
console.error(`${TAG17} config error: ${String(err)}`);
|
|
7304
7504
|
return c.json({ ok: false, error: String(err) }, 500);
|
|
7305
7505
|
}
|
|
7306
7506
|
});
|
|
@@ -7322,16 +7522,16 @@ app7.post("/send-document", async (c) => {
|
|
|
7322
7522
|
const accountResolved = realpathSync2(accountDir);
|
|
7323
7523
|
if (!resolvedPath.startsWith(accountResolved + "/")) {
|
|
7324
7524
|
const sanitised = filePath.replace(accountDir, "<account>/");
|
|
7325
|
-
console.error(`${
|
|
7525
|
+
console.error(`${TAG17} send-document REJECTED path=${sanitised} reason=outside_account_directory`);
|
|
7326
7526
|
return c.json({ error: "Access denied: file is outside the account directory" }, 403);
|
|
7327
7527
|
}
|
|
7328
7528
|
} catch (err) {
|
|
7329
7529
|
const code = err.code;
|
|
7330
7530
|
if (code === "ENOENT") {
|
|
7331
|
-
console.error(`${
|
|
7531
|
+
console.error(`${TAG17} send-document ENOENT path=${filePath}`);
|
|
7332
7532
|
return c.json({ error: `File not found: ${filePath}` }, 404);
|
|
7333
7533
|
}
|
|
7334
|
-
console.error(`${
|
|
7534
|
+
console.error(`${TAG17} send-document path error: ${String(err)}`);
|
|
7335
7535
|
return c.json({ error: String(err) }, 500);
|
|
7336
7536
|
}
|
|
7337
7537
|
const fileStat = await stat3(resolvedPath);
|
|
@@ -7353,11 +7553,11 @@ app7.post("/send-document", async (c) => {
|
|
|
7353
7553
|
caption
|
|
7354
7554
|
}, { accountId });
|
|
7355
7555
|
console.error(
|
|
7356
|
-
`${
|
|
7556
|
+
`${TAG17} send-document to=${to} size=${fileStat.size} mime=${mimetype} ok=${result.success}` + (result.messageId ? ` id=${result.messageId}` : "")
|
|
7357
7557
|
);
|
|
7358
7558
|
return c.json(result);
|
|
7359
7559
|
} catch (err) {
|
|
7360
|
-
console.error(`${
|
|
7560
|
+
console.error(`${TAG17} send-document error: ${String(err)}`);
|
|
7361
7561
|
return c.json({ error: String(err) }, 500);
|
|
7362
7562
|
}
|
|
7363
7563
|
});
|
|
@@ -7367,11 +7567,11 @@ app7.get("/activity", (c) => {
|
|
|
7367
7567
|
const result = getChannelActivity(accountId);
|
|
7368
7568
|
const total = result.accounts.reduce((sum, a) => sum + a.total, 0);
|
|
7369
7569
|
console.error(
|
|
7370
|
-
`${
|
|
7570
|
+
`${TAG17} activity accounts=${result.accounts.length} total=${total} recentEvents=${result.recentEvents.length}` + (accountId ? ` filter=${accountId}` : "")
|
|
7371
7571
|
);
|
|
7372
7572
|
return c.json(result);
|
|
7373
7573
|
} catch (err) {
|
|
7374
|
-
console.error(`${
|
|
7574
|
+
console.error(`${TAG17} activity error: ${String(err)}`);
|
|
7375
7575
|
return c.json({ error: String(err) }, 500);
|
|
7376
7576
|
}
|
|
7377
7577
|
});
|
|
@@ -7390,10 +7590,10 @@ app7.get("/conversations", (c) => {
|
|
|
7390
7590
|
};
|
|
7391
7591
|
});
|
|
7392
7592
|
conversations.sort((a, b) => (b.lastMessageTimestamp ?? 0) - (a.lastMessageTimestamp ?? 0));
|
|
7393
|
-
console.error(`${
|
|
7593
|
+
console.error(`${TAG17} conversations account=${accountId} count=${conversations.length}`);
|
|
7394
7594
|
return c.json({ conversations });
|
|
7395
7595
|
} catch (err) {
|
|
7396
|
-
console.error(`${
|
|
7596
|
+
console.error(`${TAG17} conversations error: ${String(err)}`);
|
|
7397
7597
|
return c.json({ error: String(err) }, 500);
|
|
7398
7598
|
}
|
|
7399
7599
|
});
|
|
@@ -7408,10 +7608,10 @@ app7.get("/messages", (c) => {
|
|
|
7408
7608
|
const limit = limitParam ? parseInt(limitParam, 10) : void 0;
|
|
7409
7609
|
const effectiveLimit = limit && !Number.isNaN(limit) && limit > 0 ? limit : void 0;
|
|
7410
7610
|
const messages = getMessages(accountId, jid, effectiveLimit);
|
|
7411
|
-
console.error(`${
|
|
7611
|
+
console.error(`${TAG17} messages account=${accountId} jid=${jid} limit=${effectiveLimit ?? "all"} returned=${messages.length}`);
|
|
7412
7612
|
return c.json({ messages });
|
|
7413
7613
|
} catch (err) {
|
|
7414
|
-
console.error(`${
|
|
7614
|
+
console.error(`${TAG17} messages error: ${String(err)}`);
|
|
7415
7615
|
return c.json({ error: String(err) }, 500);
|
|
7416
7616
|
}
|
|
7417
7617
|
});
|
|
@@ -7423,12 +7623,12 @@ app7.get("/group-info", async (c) => {
|
|
|
7423
7623
|
return c.json({ error: "Missing required parameter: jid" }, 400);
|
|
7424
7624
|
}
|
|
7425
7625
|
if (!isGroupJid(jid)) {
|
|
7426
|
-
console.error(`${
|
|
7626
|
+
console.error(`${TAG17} group-info error="not a group JID" jid=${jid} account=${accountId}`);
|
|
7427
7627
|
return c.json({ error: `"${jid}" is not a group JID. Group JIDs end with @g.us.` }, 400);
|
|
7428
7628
|
}
|
|
7429
7629
|
const sock = getSocket(accountId);
|
|
7430
7630
|
if (!sock) {
|
|
7431
|
-
console.error(`${
|
|
7631
|
+
console.error(`${TAG17} group-info error="not connected" account=${accountId}`);
|
|
7432
7632
|
return c.json({ error: `WhatsApp account "${accountId}" is not connected. Connect first, then retry.` }, 400);
|
|
7433
7633
|
}
|
|
7434
7634
|
const meta = await sock.groupMetadata(jid);
|
|
@@ -7441,10 +7641,10 @@ app7.get("/group-info", async (c) => {
|
|
|
7441
7641
|
participantCount: meta.participants.length,
|
|
7442
7642
|
participants: meta.participants.map((p) => ({ jid: p.id, admin: p.admin ?? null }))
|
|
7443
7643
|
};
|
|
7444
|
-
console.error(`${
|
|
7644
|
+
console.error(`${TAG17} group-info jid=${jid} subject="${meta.subject}" participants=${meta.participants.length} account=${accountId}`);
|
|
7445
7645
|
return c.json(result);
|
|
7446
7646
|
} catch (err) {
|
|
7447
|
-
console.error(`${
|
|
7647
|
+
console.error(`${TAG17} group-info error="${String(err)}" jid=${jid} account=${accountId}`);
|
|
7448
7648
|
return c.json({ error: `Failed to fetch group info: ${String(err)}` }, 500);
|
|
7449
7649
|
}
|
|
7450
7650
|
});
|
|
@@ -8325,7 +8525,7 @@ var app11 = new Hono();
|
|
|
8325
8525
|
app11.post("/cancel", requireAdminSession, async (c) => {
|
|
8326
8526
|
const session_key = c.var.sessionKey;
|
|
8327
8527
|
try {
|
|
8328
|
-
const { interruptClient: interruptClient2 } = await import("./client-pool-
|
|
8528
|
+
const { interruptClient: interruptClient2 } = await import("./client-pool-S4UZCYDJ.js");
|
|
8329
8529
|
await interruptClient2(session_key);
|
|
8330
8530
|
return c.json({ ok: true });
|
|
8331
8531
|
} catch (err) {
|
|
@@ -9478,13 +9678,13 @@ async function cdpNavigateNewTab(url, opts = {}) {
|
|
|
9478
9678
|
// server/routes/admin/device-browser.ts
|
|
9479
9679
|
var app19 = new Hono();
|
|
9480
9680
|
app19.post("/navigate", async (c) => {
|
|
9481
|
-
const
|
|
9681
|
+
const TAG18 = "[device-url:click]";
|
|
9482
9682
|
let body;
|
|
9483
9683
|
try {
|
|
9484
9684
|
body = await c.req.json();
|
|
9485
9685
|
} catch (err) {
|
|
9486
9686
|
const detail = err instanceof Error ? err.message : String(err);
|
|
9487
|
-
console.error(`${
|
|
9687
|
+
console.error(`${TAG18} reject reason=body-not-json detail=${detail} browser=fallback navigateResult=error`);
|
|
9488
9688
|
return c.json(
|
|
9489
9689
|
{ ok: false, navigateResult: "error", browser: "fallback", detail: "Request body was not valid JSON" },
|
|
9490
9690
|
400
|
|
@@ -9494,7 +9694,7 @@ app19.post("/navigate", async (c) => {
|
|
|
9494
9694
|
const intent = typeof body.intent === "string" ? body.intent : "";
|
|
9495
9695
|
const hostname2 = typeof body.hostname === "string" ? body.hostname : "";
|
|
9496
9696
|
if (!url) {
|
|
9497
|
-
console.error(`${
|
|
9697
|
+
console.error(`${TAG18} reject reason=missing-url intent=${JSON.stringify(intent)} browser=fallback navigateResult=error`);
|
|
9498
9698
|
return c.json(
|
|
9499
9699
|
{ ok: false, navigateResult: "error", browser: "fallback", detail: "url field is required" },
|
|
9500
9700
|
400
|
|
@@ -9504,7 +9704,7 @@ app19.post("/navigate", async (c) => {
|
|
|
9504
9704
|
try {
|
|
9505
9705
|
parsed = new URL(url);
|
|
9506
9706
|
} catch {
|
|
9507
|
-
console.error(`${
|
|
9707
|
+
console.error(`${TAG18} reject reason=url-malformed intent=${JSON.stringify(intent)} url=${url} browser=fallback navigateResult=error`);
|
|
9508
9708
|
return c.json(
|
|
9509
9709
|
{ ok: false, navigateResult: "error", browser: "fallback", detail: "url is not a valid URL" },
|
|
9510
9710
|
400
|
|
@@ -9512,7 +9712,7 @@ app19.post("/navigate", async (c) => {
|
|
|
9512
9712
|
}
|
|
9513
9713
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
9514
9714
|
console.error(
|
|
9515
|
-
`${
|
|
9715
|
+
`${TAG18} reject reason=scheme-not-allowed scheme=${parsed.protocol} intent=${JSON.stringify(intent)} browser=fallback navigateResult=error`
|
|
9516
9716
|
);
|
|
9517
9717
|
return c.json(
|
|
9518
9718
|
{
|
|
@@ -9528,7 +9728,7 @@ app19.post("/navigate", async (c) => {
|
|
|
9528
9728
|
const cdpOk = await ensureCdp(transport);
|
|
9529
9729
|
if (!cdpOk) {
|
|
9530
9730
|
console.error(
|
|
9531
|
-
`${
|
|
9731
|
+
`${TAG18} intent=${JSON.stringify(intent)} browser=fallback navigateResult=cdp-unreachable hostname=${JSON.stringify(hostname2)}`
|
|
9532
9732
|
);
|
|
9533
9733
|
return c.json(
|
|
9534
9734
|
{
|
|
@@ -9544,7 +9744,7 @@ app19.post("/navigate", async (c) => {
|
|
|
9544
9744
|
const browser = outcome.result === "ok" ? "vnc" : "fallback";
|
|
9545
9745
|
const detailStr = outcome.detail ? ` detail=${JSON.stringify(outcome.detail.length > 230 ? outcome.detail.slice(0, 227) + "..." : outcome.detail)}` : "";
|
|
9546
9746
|
console.error(
|
|
9547
|
-
`${
|
|
9747
|
+
`${TAG18} intent=${JSON.stringify(intent)} browser=${browser} navigateResult=${outcome.result} hostname=${JSON.stringify(hostname2)} targetId=${outcome.targetId ?? "none"}${detailStr}`
|
|
9548
9748
|
);
|
|
9549
9749
|
if (outcome.result !== "ok") {
|
|
9550
9750
|
return c.json(
|
|
@@ -9575,18 +9775,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
|
|
|
9575
9775
|
]);
|
|
9576
9776
|
var app20 = new Hono();
|
|
9577
9777
|
app20.post("/", async (c) => {
|
|
9578
|
-
const
|
|
9778
|
+
const TAG18 = "[admin:events]";
|
|
9579
9779
|
let body;
|
|
9580
9780
|
try {
|
|
9581
9781
|
body = await c.req.json();
|
|
9582
9782
|
} catch (err) {
|
|
9583
9783
|
const detail = err instanceof Error ? err.message : String(err);
|
|
9584
|
-
console.error(`${
|
|
9784
|
+
console.error(`${TAG18} reject reason=body-not-json detail=${detail}`);
|
|
9585
9785
|
return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
|
|
9586
9786
|
}
|
|
9587
9787
|
const event = typeof body.event === "string" ? body.event : "";
|
|
9588
9788
|
if (!ALLOWED_EVENTS.has(event)) {
|
|
9589
|
-
console.error(`${
|
|
9789
|
+
console.error(`${TAG18} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
|
|
9590
9790
|
return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
|
|
9591
9791
|
}
|
|
9592
9792
|
const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
|