Add location parameters to the message tool and wire them through WhatsApp web outbound delivery to send native location pins.
135 lines
4.1 KiB
TypeScript
135 lines
4.1 KiB
TypeScript
import type { AnyMessageContent, WAPresence } from '@whiskeysockets/baileys';
|
|
import { recordChannelActivity } from '../../infra/channel-activity.js';
|
|
import { toWhatsappJid } from '../../utils.js';
|
|
import type { ActiveWebSendOptions } from '../active-listener.js';
|
|
|
|
export function createWebSendApi(params: {
|
|
sock: {
|
|
sendMessage: (jid: string, content: AnyMessageContent) => Promise<unknown>;
|
|
sendPresenceUpdate: (
|
|
presence: WAPresence,
|
|
jid?: string
|
|
) => Promise<unknown>;
|
|
};
|
|
defaultAccountId: string;
|
|
}) {
|
|
return {
|
|
sendMessage: async (
|
|
to: string,
|
|
text: string,
|
|
mediaBuffer?: Buffer,
|
|
mediaType?: string,
|
|
sendOptions?: ActiveWebSendOptions,
|
|
location?: {
|
|
latitude: number;
|
|
longitude: number;
|
|
name?: string;
|
|
address?: string;
|
|
accuracy?: number;
|
|
}
|
|
): Promise<{ messageId: string }> => {
|
|
const jid = toWhatsappJid(to);
|
|
let payload: AnyMessageContent;
|
|
|
|
// Location messages take precedence
|
|
if (location) {
|
|
payload = {
|
|
location: {
|
|
degreesLatitude: location.latitude,
|
|
degreesLongitude: location.longitude,
|
|
name: location.name,
|
|
address: location.address,
|
|
accuracyInMeters: location.accuracy,
|
|
},
|
|
};
|
|
} else if (mediaBuffer && mediaType) {
|
|
if (mediaType.startsWith('image/')) {
|
|
payload = {
|
|
image: mediaBuffer,
|
|
caption: text || undefined,
|
|
mimetype: mediaType,
|
|
};
|
|
} else if (mediaType.startsWith('audio/')) {
|
|
payload = { audio: mediaBuffer, ptt: true, mimetype: mediaType };
|
|
} else if (mediaType.startsWith('video/')) {
|
|
const gifPlayback = sendOptions?.gifPlayback;
|
|
payload = {
|
|
video: mediaBuffer,
|
|
caption: text || undefined,
|
|
mimetype: mediaType,
|
|
...(gifPlayback ? { gifPlayback: true } : {}),
|
|
};
|
|
} else {
|
|
payload = {
|
|
document: mediaBuffer,
|
|
fileName: 'file',
|
|
caption: text || undefined,
|
|
mimetype: mediaType,
|
|
};
|
|
}
|
|
} else {
|
|
payload = { text };
|
|
}
|
|
const result = await params.sock.sendMessage(jid, payload);
|
|
const accountId = sendOptions?.accountId ?? params.defaultAccountId;
|
|
recordChannelActivity({
|
|
channel: 'whatsapp',
|
|
accountId,
|
|
direction: 'outbound',
|
|
});
|
|
const messageId =
|
|
typeof result === 'object' && result && 'key' in result
|
|
? String((result as { key?: { id?: string } }).key?.id ?? 'unknown')
|
|
: 'unknown';
|
|
return { messageId };
|
|
},
|
|
sendPoll: async (
|
|
to: string,
|
|
poll: { question: string; options: string[]; maxSelections?: number }
|
|
): Promise<{ messageId: string }> => {
|
|
const jid = toWhatsappJid(to);
|
|
const result = await params.sock.sendMessage(jid, {
|
|
poll: {
|
|
name: poll.question,
|
|
values: poll.options,
|
|
selectableCount: poll.maxSelections ?? 1,
|
|
},
|
|
} as AnyMessageContent);
|
|
recordChannelActivity({
|
|
channel: 'whatsapp',
|
|
accountId: params.defaultAccountId,
|
|
direction: 'outbound',
|
|
});
|
|
const messageId =
|
|
typeof result === 'object' && result && 'key' in result
|
|
? String((result as { key?: { id?: string } }).key?.id ?? 'unknown')
|
|
: 'unknown';
|
|
return { messageId };
|
|
},
|
|
sendReaction: async (
|
|
chatJid: string,
|
|
messageId: string,
|
|
emoji: string,
|
|
fromMe: boolean,
|
|
participant?: string
|
|
): Promise<void> => {
|
|
const jid = toWhatsappJid(chatJid);
|
|
await params.sock.sendMessage(jid, {
|
|
react: {
|
|
text: emoji,
|
|
key: {
|
|
remoteJid: jid,
|
|
id: messageId,
|
|
fromMe,
|
|
participant: participant ? toWhatsappJid(participant) : undefined,
|
|
},
|
|
},
|
|
} as AnyMessageContent);
|
|
},
|
|
sendComposingTo: async (to: string): Promise<void> => {
|
|
const jid = toWhatsappJid(to);
|
|
await params.sock.sendPresenceUpdate('composing', jid);
|
|
},
|
|
} as const;
|
|
}
|