mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-09 15:10:45 +08:00
fix(whatsapp): handle group mentions and caller allowlists
This commit is contained in:
parent
54468bbe1e
commit
9664e85599
13 changed files with 563 additions and 214 deletions
|
|
@ -63,6 +63,7 @@ export function normalizeWhatsappAccessPolicy(value = {}) {
|
|||
return Object.freeze({
|
||||
accessMode,
|
||||
allowedNumbers: normalizeWhatsappAllowedNumbers(value.allowedNumbers),
|
||||
groupAllowedNumbers: normalizeWhatsappAllowedNumbers(value.groupAllowedNumbers),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -334,6 +334,7 @@ export class WhatsappController {
|
|||
connectedAt: new Date().toISOString(),
|
||||
accessMode: previous?.accessMode,
|
||||
allowedNumbers: previous?.allowedNumbers,
|
||||
groupAllowedNumbers: previous?.groupAllowedNumbers,
|
||||
};
|
||||
try {
|
||||
if (record.controller.signal.aborted || this.#closed) throw Object.assign(new Error(), { name: 'AbortError' });
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { createHash, randomBytes } from 'node:crypto';
|
|||
import {
|
||||
areJidsSameUser,
|
||||
downloadMediaMessage,
|
||||
jidNormalizedUser,
|
||||
normalizeMessageContent,
|
||||
} from '@whiskeysockets/baileys';
|
||||
|
||||
|
|
@ -13,6 +14,7 @@ import { trackOutboundArtifactProviderPromise } from '../shared/semantic/artifac
|
|||
import { createWhatsappBridgeStatus, WhatsappHarnessBridge } from './whatsapp-bridge.mjs';
|
||||
import {
|
||||
WHATSAPP_ACCESS_MODES,
|
||||
normalizeWhatsappAccountJid,
|
||||
normalizeWhatsappAccessPolicy,
|
||||
} from './config-store.mjs';
|
||||
import { createWhatsappWebSession } from './whatsapp-web-session.mjs';
|
||||
|
|
@ -204,8 +206,21 @@ export function createWhatsappMediaDownloader({
|
|||
});
|
||||
}
|
||||
|
||||
function whatsappAccountMatcher(accountJid, aliases) {
|
||||
const accountJids = new Set(
|
||||
[accountJid, ...(Array.isArray(aliases) ? aliases : [])]
|
||||
.map((jid) => normalizeWhatsappAccountJid(jidNormalizedUser(jid)))
|
||||
.filter(Boolean),
|
||||
);
|
||||
return (jid) => {
|
||||
const normalized = normalizeWhatsappAccountJid(jidNormalizedUser(jid));
|
||||
return normalized !== null && accountJids.has(normalized);
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeWhatsappMessage(message, accountJid, {
|
||||
download = downloadMediaMessage,
|
||||
accountAliases = [],
|
||||
} = {}) {
|
||||
const remoteJid = typeof message?.key?.remoteJid === 'string' ? message.key.remoteJid : '';
|
||||
const alternateRemoteJid = typeof message?.key?.remoteJidAlt === 'string'
|
||||
|
|
@ -215,8 +230,9 @@ export function normalizeWhatsappMessage(message, accountJid, {
|
|||
|| remoteJid.endsWith('@newsletter')) return null;
|
||||
const group = remoteJid.endsWith('@g.us');
|
||||
const fromMe = message.key.fromMe === true;
|
||||
const matchesAccount = whatsappAccountMatcher(accountJid, accountAliases);
|
||||
const selfChat = fromMe && !group
|
||||
&& [remoteJid, alternateRemoteJid].some((jid) => jid && areJidsSameUser(jid, accountJid));
|
||||
&& [remoteJid, alternateRemoteJid].some(matchesAccount);
|
||||
if (fromMe && !selfChat && !group) return null;
|
||||
const senderJid = fromMe ? accountJid : group ? message.key.participant : remoteJid;
|
||||
const senderAlternateJid = group && !fromMe ? message.key.participantAlt : alternateRemoteJid;
|
||||
|
|
@ -225,9 +241,9 @@ export function normalizeWhatsappMessage(message, accountJid, {
|
|||
const content = normalizeMessageContent(message.message);
|
||||
const context = messageContext(content);
|
||||
const mentioned = Array.isArray(context?.mentionedJid)
|
||||
&& context.mentionedJid.some((jid) => areJidsSameUser(jid, accountJid));
|
||||
&& context.mentionedJid.some(matchesAccount);
|
||||
const replyToSelf = typeof context?.participant === 'string'
|
||||
&& areJidsSameUser(context.participant, accountJid);
|
||||
&& matchesAccount(context.participant);
|
||||
const image = whatsappImageSource(message, content, download, { viewOnce });
|
||||
const file = whatsappFileSource(message, content, download);
|
||||
return {
|
||||
|
|
@ -235,6 +251,7 @@ export function normalizeWhatsappMessage(message, accountJid, {
|
|||
providerMessageId: messageId,
|
||||
senderId: senderJid,
|
||||
senderAlternateId: typeof senderAlternateJid === 'string' ? senderAlternateJid : '',
|
||||
senderIsSelf: fromMe,
|
||||
senderIsBot: false,
|
||||
kind: group ? 'group' : 'direct',
|
||||
conversationId: remoteJid,
|
||||
|
|
@ -253,12 +270,22 @@ export function normalizeWhatsappMessage(message, accountJid, {
|
|||
export function whatsappInboundAllowed(message, {
|
||||
accessMode = WHATSAPP_ACCESS_MODES.selfOnly,
|
||||
allowedNumbers = new Set(),
|
||||
groupAllowedNumbers = new Set(),
|
||||
} = {}) {
|
||||
if (accessMode === WHATSAPP_ACCESS_MODES.open) return true;
|
||||
if (accessMode === WHATSAPP_ACCESS_MODES.open) {
|
||||
if (message?.kind !== 'group' || message.senderIsSelf === true) return true;
|
||||
if (!(groupAllowedNumbers instanceof Set)) return false;
|
||||
if (groupAllowedNumbers.size === 0) return true;
|
||||
const senderJids = [message.senderId, message.senderAlternateId]
|
||||
.filter((jid) => typeof jid === 'string' && jid.endsWith('@s.whatsapp.net'));
|
||||
return [...groupAllowedNumbers].some((number) => senderJids.some((jid) => (
|
||||
areJidsSameUser(jid, `${number}@s.whatsapp.net`)
|
||||
)));
|
||||
}
|
||||
if (message?.kind !== 'direct') return false;
|
||||
if (message.selfChat === true) return true;
|
||||
if (accessMode !== WHATSAPP_ACCESS_MODES.privateAllowlist
|
||||
|| !(allowedNumbers instanceof Set)) return false;
|
||||
if (accessMode !== WHATSAPP_ACCESS_MODES.privateAllowlist) return false;
|
||||
if (!(allowedNumbers instanceof Set)) return false;
|
||||
const senderJids = [message.senderId, message.senderAlternateId]
|
||||
.filter((jid) => typeof jid === 'string' && jid.endsWith('@s.whatsapp.net'));
|
||||
return [...allowedNumbers].some((number) => senderJids.some((jid) => (
|
||||
|
|
@ -542,6 +569,7 @@ export class WhatsappRuntime {
|
|||
#mediaUploadTimeoutMs;
|
||||
#accessMode;
|
||||
#allowedPrivateNumbers;
|
||||
#allowedGroupNumbers;
|
||||
#createSession;
|
||||
#status = createWhatsappRuntimeStatus();
|
||||
#abortController = null;
|
||||
|
|
@ -590,6 +618,7 @@ export class WhatsappRuntime {
|
|||
const policy = normalizeWhatsappAccessPolicy(value);
|
||||
this.#accessMode = policy.accessMode;
|
||||
this.#allowedPrivateNumbers = new Set(policy.allowedNumbers);
|
||||
this.#allowedGroupNumbers = new Set(policy.groupAllowedNumbers);
|
||||
this.#config = { ...this.#config, ...policy };
|
||||
return policy;
|
||||
}
|
||||
|
|
@ -624,7 +653,13 @@ export class WhatsappRuntime {
|
|||
{ code: 'relink-required' },
|
||||
)),
|
||||
onMessage: async (raw, context) => {
|
||||
const linkedAccount = context?.socket?.user;
|
||||
const message = normalizeWhatsappMessage(raw, this.#config.accountJid, {
|
||||
accountAliases: [
|
||||
linkedAccount?.id,
|
||||
linkedAccount?.lid,
|
||||
linkedAccount?.phoneNumber,
|
||||
],
|
||||
download: createWhatsappMediaDownloader({
|
||||
socket: context?.socket,
|
||||
logger: this.#logger,
|
||||
|
|
@ -635,6 +670,7 @@ export class WhatsappRuntime {
|
|||
if (!whatsappInboundAllowed(message, {
|
||||
accessMode: this.#accessMode,
|
||||
allowedNumbers: this.#allowedPrivateNumbers,
|
||||
groupAllowedNumbers: this.#allowedGroupNumbers,
|
||||
})) {
|
||||
this.#status.messagesRejected += 1;
|
||||
this.#status.lastRejectedAt = new Date().toISOString();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue