fix: resolve WhatsApp LID inbound mapping
This commit is contained in:
@@ -81,6 +81,7 @@
|
||||
- **Multi-agent safety:** do **not** switch branches / check out a different branch unless explicitly requested.
|
||||
- **Multi-agent safety:** running multiple agents is OK as long as each agent has its own session.
|
||||
- **Multi-agent safety:** when you see unrecognized files, keep going; focus on your changes and commit only those.
|
||||
- Bug investigations: read source code of relevant npm dependencies and all related local code before concluding; aim for high-confidence root cause.
|
||||
- When asked to open a “session” file, open the Pi session logs under `~/.clawdbot/sessions/*.jsonl` (newest unless a specific ID is given), not the default `sessions.json`. If logs are needed from another machine, SSH via Tailscale and read the same path there.
|
||||
- Menubar dimming + restart flow mirrors Trimmy: use `scripts/restart-mac.sh` (kills all Clawdbot variants, runs `swift build`, packages, relaunches). Icon dimming depends on MenuBarExtraAccess wiring in AppMain; keep `appearsDisabled` updates intact when touching the status item.
|
||||
- Do not rebuild the macOS app over SSH; rebuilds must be run directly on the Mac.
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
- Control UI: add Docs link, remove chat composer divider, and add New session button.
|
||||
- Telegram: retry long-polling conflicts with backoff to avoid fatal exits.
|
||||
- Telegram: fix grammY fetch type mismatch when injecting `fetch`. (#512) — thanks @YuriNachos
|
||||
- WhatsApp: resolve @lid JIDs via Baileys mapping to unblock inbound messages. (#415)
|
||||
- Agent system prompt: avoid automatic self-updates unless explicitly requested.
|
||||
- Onboarding: tighten QuickStart hint copy for configuring later.
|
||||
- Onboarding: avoid “token expired” for Codex CLI when expiry is heuristic.
|
||||
|
||||
@@ -118,6 +118,25 @@ export async function monitorWebInbox(options: {
|
||||
{ subject?: string; participants?: string[]; expires: number }
|
||||
>();
|
||||
const GROUP_META_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
||||
const lidLookup = sock.signalRepository?.lidMapping;
|
||||
|
||||
const resolveJidToE164 = async (
|
||||
jid: string | null | undefined,
|
||||
): Promise<string | null> => {
|
||||
if (!jid) return null;
|
||||
const direct = jidToE164(jid);
|
||||
if (direct) return direct;
|
||||
if (!/(@lid|@hosted\.lid)$/.test(jid)) return null;
|
||||
if (!lidLookup?.getPNForLID) return null;
|
||||
try {
|
||||
const pnJid = await lidLookup.getPNForLID(jid);
|
||||
if (!pnJid) return null;
|
||||
return jidToE164(pnJid);
|
||||
} catch (err) {
|
||||
logVerbose(`LID mapping lookup failed for ${jid}: ${String(err)}`);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const getGroupMeta = async (jid: string) => {
|
||||
const cached = groupMetaCache.get(jid);
|
||||
@@ -125,9 +144,14 @@ export async function monitorWebInbox(options: {
|
||||
try {
|
||||
const meta = await sock.groupMetadata(jid);
|
||||
const participants =
|
||||
meta.participants
|
||||
?.map((p) => jidToE164(p.id) ?? p.id)
|
||||
.filter(Boolean) ?? [];
|
||||
(
|
||||
await Promise.all(
|
||||
meta.participants?.map(async (p) => {
|
||||
const mapped = await resolveJidToE164(p.id);
|
||||
return mapped ?? p.id;
|
||||
}) ?? [],
|
||||
)
|
||||
).filter(Boolean) ?? [];
|
||||
const entry = {
|
||||
subject: meta.subject,
|
||||
participants,
|
||||
@@ -159,12 +183,12 @@ export async function monitorWebInbox(options: {
|
||||
continue;
|
||||
const group = isJidGroup(remoteJid);
|
||||
const participantJid = msg.key?.participant ?? undefined;
|
||||
const from = group ? remoteJid : jidToE164(remoteJid);
|
||||
const from = group ? remoteJid : await resolveJidToE164(remoteJid);
|
||||
// Skip if we still can't resolve an id to key conversation
|
||||
if (!from) continue;
|
||||
const senderE164 = group
|
||||
? participantJid
|
||||
? jidToE164(participantJid)
|
||||
? await resolveJidToE164(participantJid)
|
||||
: null
|
||||
: from;
|
||||
let groupSubject: string | undefined;
|
||||
|
||||
@@ -50,6 +50,11 @@ vi.mock("./session.js", () => {
|
||||
readMessages: vi.fn().mockResolvedValue(undefined),
|
||||
updateMediaMessage: vi.fn(),
|
||||
logger: {},
|
||||
signalRepository: {
|
||||
lidMapping: {
|
||||
getPNForLID: vi.fn().mockResolvedValue(null),
|
||||
},
|
||||
},
|
||||
user: { id: "123@s.whatsapp.net" },
|
||||
};
|
||||
return {
|
||||
@@ -136,6 +141,85 @@ describe("web monitor inbox", () => {
|
||||
await listener.close();
|
||||
});
|
||||
|
||||
it("resolves LID JIDs using Baileys LID mapping store", async () => {
|
||||
const onMessage = vi.fn(async () => {
|
||||
return;
|
||||
});
|
||||
|
||||
const listener = await monitorWebInbox({ verbose: false, onMessage });
|
||||
const sock = await createWaSocket();
|
||||
sock.signalRepository.lidMapping.getPNForLID.mockResolvedValueOnce(
|
||||
"999:0@s.whatsapp.net",
|
||||
);
|
||||
const upsert = {
|
||||
type: "notify",
|
||||
messages: [
|
||||
{
|
||||
key: { id: "abc", fromMe: false, remoteJid: "999@lid" },
|
||||
message: { conversation: "ping" },
|
||||
messageTimestamp: 1_700_000_000,
|
||||
pushName: "Tester",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
sock.ev.emit("messages.upsert", upsert);
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
|
||||
expect(sock.signalRepository.lidMapping.getPNForLID).toHaveBeenCalledWith(
|
||||
"999@lid",
|
||||
);
|
||||
expect(onMessage).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ body: "ping", from: "+999", to: "+123" }),
|
||||
);
|
||||
|
||||
await listener.close();
|
||||
});
|
||||
|
||||
it("resolves group participant LID JIDs via Baileys mapping", async () => {
|
||||
const onMessage = vi.fn(async () => {
|
||||
return;
|
||||
});
|
||||
|
||||
const listener = await monitorWebInbox({ verbose: false, onMessage });
|
||||
const sock = await createWaSocket();
|
||||
sock.signalRepository.lidMapping.getPNForLID.mockResolvedValueOnce(
|
||||
"444:0@s.whatsapp.net",
|
||||
);
|
||||
const upsert = {
|
||||
type: "notify",
|
||||
messages: [
|
||||
{
|
||||
key: {
|
||||
id: "abc",
|
||||
fromMe: false,
|
||||
remoteJid: "123@g.us",
|
||||
participant: "444@lid",
|
||||
},
|
||||
message: { conversation: "ping" },
|
||||
messageTimestamp: 1_700_000_000,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
sock.ev.emit("messages.upsert", upsert);
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
|
||||
expect(sock.signalRepository.lidMapping.getPNForLID).toHaveBeenCalledWith(
|
||||
"444@lid",
|
||||
);
|
||||
expect(onMessage).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
body: "ping",
|
||||
from: "123@g.us",
|
||||
senderE164: "+444",
|
||||
chatType: "group",
|
||||
}),
|
||||
);
|
||||
|
||||
await listener.close();
|
||||
});
|
||||
|
||||
it("does not block follow-up messages when handler is pending", async () => {
|
||||
let resolveFirst: (() => void) | null = null;
|
||||
const onMessage = vi.fn(async () => {
|
||||
|
||||
Reference in New Issue
Block a user