diff --git a/src/core/engines/gows/session.gows.core.ts b/src/core/engines/gows/session.gows.core.ts index d1440ecd..5d25a604 100644 --- a/src/core/engines/gows/session.gows.core.ts +++ b/src/core/engines/gows/session.gows.core.ts @@ -120,6 +120,7 @@ import { } from '@waha/structures/presence.dto'; import { MessageSource, + WALocation, WAMessage, WAMessageReaction, } from '@waha/structures/responses.dto'; @@ -174,6 +175,9 @@ import { import esm from '@waha/vendor/esm'; import { IsEditedMessage } from '@waha/core/utils/pwa'; import MessageServiceClient = messages.MessageServiceClient; +import { GoToJSWAProto } from '@waha/core/engines/gows/waproto'; +import { extractWALocation } from '@waha/core/engines/waproto/locaiton'; +import { extractVCards } from '@waha/core/engines/waproto/vcards'; enum WhatsMeowEvent { CONNECTED = 'gows.ConnectedEventData', @@ -2041,6 +2045,16 @@ export class WhatsappSessionGoWSCore extends WhatsappSession { const mediaContent = extractMediaContent(message.Message); const source = this.getSourceDeviceByMsg(message); + let waproto: proto.Message | null = null; + try { + waproto = GoToJSWAProto(message.Message); + } catch (e) { + this.logger.error( + 'Failed to resolve proto message from GOWS to JS format', + ); + this.logger.error(e, e.stack); + } + return { id: id, timestamp: parseTimestampToSeconds(message.Info.Timestamp), @@ -2056,7 +2070,8 @@ export class WhatsappSessionGoWSCore extends WhatsappSession { mediaUrl: message.media?.url, // @ts-ignore ack: ack, - // @ts-ignore + location: extractWALocation(waproto), + vCards: extractVCards(waproto), ackName: WAMessageAck[ack] || ACK_UNKNOWN, replyTo: replyTo, _data: message, diff --git a/src/core/engines/noweb/session.noweb.core.ts b/src/core/engines/noweb/session.noweb.core.ts index bd1f230b..47dc9ce2 100644 --- a/src/core/engines/noweb/session.noweb.core.ts +++ b/src/core/engines/noweb/session.noweb.core.ts @@ -159,7 +159,11 @@ import { WAHAChatPresences, WAHAPresenceData, } from '@waha/structures/presence.dto'; -import { WAMessage, WAMessageReaction } from '@waha/structures/responses.dto'; +import { + WALocation, + WAMessage, + WAMessageReaction, +} from '@waha/structures/responses.dto'; import { MeInfo } from '@waha/structures/sessions.dto'; import { BROADCAST_ID, @@ -205,6 +209,8 @@ import { IsEditedMessage, IsHistorySyncNotification, } from '@waha/core/utils/pwa'; +import { extractWALocation } from '@waha/core/engines/waproto/locaiton'; +import { extractVCards } from '@waha/core/engines/waproto/vcards'; // eslint-disable-next-line @typescript-eslint/no-var-requires const promiseRetry = require('promise-retry'); @@ -2280,6 +2286,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession { const ack = message.ack || StatusToAck(message.status); const mediaContent = extractMediaContent(message.message); const source = this.getMessageSource(message.key.id); + const waproto = message.message; return { id: id, timestamp: ensureNumber(message.messageTimestamp), @@ -2297,8 +2304,8 @@ export class WhatsappSessionNoWebCore extends WhatsappSession { ack: ack, // @ts-ignore ackName: WAMessageAck[ack] || ACK_UNKNOWN, - location: message.location, - vCards: message.vCards, + location: extractWALocation(waproto), + vCards: extractVCards(waproto), replyTo: replyTo, _data: message, }; diff --git a/src/core/engines/waproto/locaiton.ts b/src/core/engines/waproto/locaiton.ts new file mode 100644 index 00000000..b07c6c66 --- /dev/null +++ b/src/core/engines/waproto/locaiton.ts @@ -0,0 +1,34 @@ +import type { proto } from '@adiwajshing/baileys'; +import { WALocation } from '@waha/structures/responses.dto'; +import * as lodash from 'lodash'; +import { ensureBase64 } from '@waha/utils/bytes'; + +export function extractWALocation(waproto: proto.Message): WALocation | null { + if (!waproto) { + return null; + } + if (!lodash.isEmpty(waproto.locationMessage)) { + const location = waproto.locationMessage; + return { + live: false, + latitude: location.degreesLatitude?.toString(), + longitude: location.degreesLongitude?.toString(), + name: location.name, + address: location.address, + url: location.url, + description: location.comment, + thumbnail: ensureBase64(location.jpegThumbnail), + }; + } + if (!lodash.isEmpty(waproto.liveLocationMessage)) { + const location = waproto.liveLocationMessage; + return { + live: true, + latitude: location.degreesLatitude?.toString(), + longitude: location.degreesLongitude?.toString(), + description: location.caption, + thumbnail: ensureBase64(location.jpegThumbnail), + }; + } + return null; +} diff --git a/src/core/engines/waproto/vcards.ts b/src/core/engines/waproto/vcards.ts new file mode 100644 index 00000000..4d8ba2f1 --- /dev/null +++ b/src/core/engines/waproto/vcards.ts @@ -0,0 +1,18 @@ +import type { proto } from '@adiwajshing/baileys'; +import * as lodash from 'lodash'; + +export function extractVCards(waproto: proto.Message): string[] | null { + if (!waproto) { + return null; + } + if (!lodash.isEmpty(waproto.contactMessage)) { + return [waproto.contactMessage.vcard]; + } + + if (!lodash.isEmpty(waproto.contactsArrayMessage)) { + return waproto.contactsArrayMessage.contacts.map( + (contact) => contact.vcard, + ); + } + return null; +} diff --git a/src/core/engines/webjs/session.webjs.core.ts b/src/core/engines/webjs/session.webjs.core.ts index ac987265..7a931191 100644 --- a/src/core/engines/webjs/session.webjs.core.ts +++ b/src/core/engines/webjs/session.webjs.core.ts @@ -110,7 +110,11 @@ import { WAHAChatPresences, WAHAPresenceData, } from '@waha/structures/presence.dto'; -import { WAMessage, WAMessageReaction } from '@waha/structures/responses.dto'; +import { + WALocation, + WAMessage, + WAMessageReaction, +} from '@waha/structures/responses.dto'; import { BrowserTraceQuery } from '@waha/structures/server.debug.dto'; import { MeInfo } from '@waha/structures/sessions.dto'; import { StatusRequest, TextStatus } from '@waha/structures/status.dto'; @@ -1802,7 +1806,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession { // @ts-ignore ack: message.ack, ackName: WAMessageAck[message.ack] || ACK_UNKNOWN, - location: message.location, + location: this.extractLocation(message), vCards: message.vCards, replyTo: replyTo, _data: message.rawData, @@ -1823,6 +1827,24 @@ export class WhatsappSessionWebJSCore extends WhatsappSession { }; } + protected extractLocation(message: Message): WALocation | null { + const location = message.location; + if (lodash.isEmpty(location)) { + return null; + } + const rawData: any = message.rawData; + return { + live: Boolean(rawData.isLive), + latitude: location.latitude, + longitude: location.longitude, + name: location.name, + address: location.address, + description: rawData.comment || location.description, + url: location.url, + thumbnail: message.body, + }; + } + public async getEngineInfo() { if (!this.whatsapp || !this.whatsapp.pupPage) { return null; diff --git a/src/structures/responses.dto.ts b/src/structures/responses.dto.ts index e6826d62..34f5957a 100644 --- a/src/structures/responses.dto.ts +++ b/src/structures/responses.dto.ts @@ -6,9 +6,14 @@ import { WAMessageAck } from './enums.dto'; import { ChatIdProperty, MessageIdProperty } from './properties.dto'; export class WALocation { - description?: string; latitude: string; longitude: string; + live: boolean; + name?: string; + address?: string; + url?: string; + description?: string; // "comment" in proto + thumbnail?: string; } export enum MessageSource { diff --git a/src/utils/bytes.ts b/src/utils/bytes.ts new file mode 100644 index 00000000..ec357e92 --- /dev/null +++ b/src/utils/bytes.ts @@ -0,0 +1,22 @@ +export function ensureBase64( + data: string | Uint8Array | ArrayBuffer | null | undefined, +): string | null | undefined { + // Preserves null or undefined + if (!data) { + return data as any; + } + // Already base64 + if (typeof data === 'string') { + return data.trim(); + } + + if (data instanceof Uint8Array) { + return Buffer.from(data).toString('base64'); + } + + if (data instanceof ArrayBuffer) { + return Buffer.from(new Uint8Array(data)).toString('base64'); + } + + return null; +}