feat(providers): normalize location parsing
This commit is contained in:
@@ -39,6 +39,7 @@ import { emitHeartbeatEvent } from "../infra/heartbeat-events.js";
|
||||
import { enqueueSystemEvent } from "../infra/system-events.js";
|
||||
import { registerUnhandledRejectionHandler } from "../infra/unhandled-rejections.js";
|
||||
import { createSubsystemLogger, getChildLogger } from "../logging.js";
|
||||
import { toLocationContext } from "../providers/location.js";
|
||||
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
|
||||
import { isSelfChatMode, jidToE164, normalizeE164 } from "../utils.js";
|
||||
import { setActiveWebListener } from "./active-listener.js";
|
||||
@@ -1216,6 +1217,7 @@ export async function monitorWebProvider(
|
||||
SenderName: msg.senderName,
|
||||
SenderE164: msg.senderE164,
|
||||
WasMentioned: msg.wasMentioned,
|
||||
...(msg.location ? toLocationContext(msg.location) : {}),
|
||||
Surface: "whatsapp",
|
||||
},
|
||||
cfg,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { extractMediaPlaceholder, extractText } from "./inbound.js";
|
||||
import {
|
||||
extractLocationData,
|
||||
extractMediaPlaceholder,
|
||||
extractText,
|
||||
} from "./inbound.js";
|
||||
|
||||
describe("web inbound helpers", () => {
|
||||
it("prefers the main conversation body", () => {
|
||||
@@ -45,4 +49,46 @@ describe("web inbound helpers", () => {
|
||||
} as unknown as import("@whiskeysockets/baileys").proto.IMessage),
|
||||
).toBe("<media:audio>");
|
||||
});
|
||||
|
||||
it("extracts WhatsApp location messages", () => {
|
||||
const location = extractLocationData({
|
||||
locationMessage: {
|
||||
degreesLatitude: 48.858844,
|
||||
degreesLongitude: 2.294351,
|
||||
name: "Eiffel Tower",
|
||||
address: "Champ de Mars, Paris",
|
||||
accuracyInMeters: 12,
|
||||
comment: "Meet here",
|
||||
},
|
||||
} as unknown as import("@whiskeysockets/baileys").proto.IMessage);
|
||||
expect(location).toEqual({
|
||||
latitude: 48.858844,
|
||||
longitude: 2.294351,
|
||||
accuracy: 12,
|
||||
name: "Eiffel Tower",
|
||||
address: "Champ de Mars, Paris",
|
||||
caption: "Meet here",
|
||||
source: "place",
|
||||
isLive: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("extracts WhatsApp live location messages", () => {
|
||||
const location = extractLocationData({
|
||||
liveLocationMessage: {
|
||||
degreesLatitude: 37.819929,
|
||||
degreesLongitude: -122.478255,
|
||||
accuracyInMeters: 20,
|
||||
caption: "On the move",
|
||||
},
|
||||
} as unknown as import("@whiskeysockets/baileys").proto.IMessage);
|
||||
expect(location).toEqual({
|
||||
latitude: 37.819929,
|
||||
longitude: -122.478255,
|
||||
accuracy: 20,
|
||||
caption: "On the move",
|
||||
source: "live",
|
||||
isLive: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,6 +16,10 @@ import { loadConfig } from "../config/config.js";
|
||||
import { logVerbose, shouldLogVerbose } from "../globals.js";
|
||||
import { createSubsystemLogger, getChildLogger } from "../logging.js";
|
||||
import { saveMediaBuffer } from "../media/store.js";
|
||||
import {
|
||||
formatLocationText,
|
||||
type NormalizedLocation,
|
||||
} from "../providers/location.js";
|
||||
import {
|
||||
isSelfChatMode,
|
||||
jidToE164,
|
||||
@@ -56,6 +60,7 @@ export type WebInboundMessage = {
|
||||
mentionedJids?: string[];
|
||||
selfJid?: string | null;
|
||||
selfE164?: string | null;
|
||||
location?: NormalizedLocation;
|
||||
sendComposing: () => Promise<void>;
|
||||
reply: (text: string) => Promise<void>;
|
||||
sendMedia: (payload: AnyMessageContent) => Promise<void>;
|
||||
@@ -241,7 +246,12 @@ export async function monitorWebInbox(options: {
|
||||
// but we skip triggering the auto-reply logic to avoid spamming old context.
|
||||
if (upsert.type === "append") continue;
|
||||
|
||||
const location = extractLocationData(msg.message ?? undefined);
|
||||
const locationText = location ? formatLocationText(location) : undefined;
|
||||
let body = extractText(msg.message ?? undefined);
|
||||
if (locationText) {
|
||||
body = [body, locationText].filter(Boolean).join("\n").trim();
|
||||
}
|
||||
if (!body) {
|
||||
body = extractMediaPlaceholder(msg.message ?? undefined);
|
||||
if (!body) continue;
|
||||
@@ -319,6 +329,7 @@ export async function monitorWebInbox(options: {
|
||||
mentionedJids: mentionedJids ?? undefined,
|
||||
selfJid,
|
||||
selfE164,
|
||||
location: location ?? undefined,
|
||||
sendComposing,
|
||||
reply,
|
||||
sendMedia,
|
||||
@@ -598,6 +609,62 @@ export function extractMediaPlaceholder(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function extractLocationData(
|
||||
rawMessage: proto.IMessage | undefined,
|
||||
): NormalizedLocation | null {
|
||||
const message = unwrapMessage(rawMessage);
|
||||
if (!message) return null;
|
||||
|
||||
const live = message.liveLocationMessage ?? undefined;
|
||||
if (live) {
|
||||
const latitudeRaw = live.degreesLatitude;
|
||||
const longitudeRaw = live.degreesLongitude;
|
||||
if (latitudeRaw != null && longitudeRaw != null) {
|
||||
const latitude = Number(latitudeRaw);
|
||||
const longitude = Number(longitudeRaw);
|
||||
if (Number.isFinite(latitude) && Number.isFinite(longitude)) {
|
||||
return {
|
||||
latitude,
|
||||
longitude,
|
||||
accuracy: live.accuracyInMeters ?? undefined,
|
||||
caption: live.caption ?? undefined,
|
||||
source: "live",
|
||||
isLive: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const location = message.locationMessage ?? undefined;
|
||||
if (location) {
|
||||
const latitudeRaw = location.degreesLatitude;
|
||||
const longitudeRaw = location.degreesLongitude;
|
||||
if (latitudeRaw != null && longitudeRaw != null) {
|
||||
const latitude = Number(latitudeRaw);
|
||||
const longitude = Number(longitudeRaw);
|
||||
if (Number.isFinite(latitude) && Number.isFinite(longitude)) {
|
||||
const isLive = Boolean(location.isLive);
|
||||
return {
|
||||
latitude,
|
||||
longitude,
|
||||
accuracy: location.accuracyInMeters ?? undefined,
|
||||
name: location.name ?? undefined,
|
||||
address: location.address ?? undefined,
|
||||
caption: location.comment ?? undefined,
|
||||
source: isLive
|
||||
? "live"
|
||||
: location.name || location.address
|
||||
? "place"
|
||||
: "pin",
|
||||
isLive,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function describeReplyContext(rawMessage: proto.IMessage | undefined): {
|
||||
id?: string;
|
||||
body: string;
|
||||
@@ -610,7 +677,11 @@ function describeReplyContext(rawMessage: proto.IMessage | undefined): {
|
||||
contextInfo?.quotedMessage as proto.IMessage | undefined,
|
||||
) as proto.IMessage | undefined;
|
||||
if (!quoted) return null;
|
||||
const body = extractText(quoted) ?? extractMediaPlaceholder(quoted);
|
||||
const location = extractLocationData(quoted);
|
||||
const locationText = location ? formatLocationText(location) : undefined;
|
||||
const text = extractText(quoted);
|
||||
let body = [text, locationText].filter(Boolean).join("\n").trim();
|
||||
if (!body) body = extractMediaPlaceholder(quoted);
|
||||
if (!body) {
|
||||
const quotedType = quoted ? getContentType(quoted) : undefined;
|
||||
logVerbose(
|
||||
|
||||
Reference in New Issue
Block a user