From bd5a8f0c6c4570db57bf6ebb3e5f6986c0848ccd Mon Sep 17 00:00:00 2001 From: Clairton Rodrigo Heinzen Date: Wed, 12 Feb 2025 16:51:00 -0300 Subject: [PATCH 1/2] contact in redis --- src/services/redis.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/services/redis.ts b/src/services/redis.ts index 32bdc107..2d24ab5a 100644 --- a/src/services/redis.ts +++ b/src/services/redis.ts @@ -1,7 +1,7 @@ import { createClient } from '@redis/client' import { REDIS_URL, DATA_JID_TTL, DATA_TTL, SESSION_TTL, DATA_URL_TTL } from '../defaults' import logger from './logger' -import { GroupMetadata } from 'baileys' +import { Contact, GroupMetadata } from 'baileys' import { Webhook, configs } from './config' export const BASE_KEY = 'unoapi-' @@ -139,6 +139,10 @@ const connectCountKey = (phone: string, ordinal: number | string) => { return `${BASE_KEY}connect-count:${phone}:${ordinal}` } +const contactKey = (phone: string, id: string) => { + return `${BASE_KEY}contact:${phone}:${id}` +} + export const sessionStatusKey = (phone: string) => { return `${BASE_KEY}status:${phone}` } @@ -222,6 +226,18 @@ export const setSessionStatus = async (phone: string, status: string) => { await client.set(key, status) } +export const getContact = async (phone: string, id: string): Promise => { + const key = contactKey(phone, id) + const json = await redisGet(key) + return json ? JSON.parse(json) as Contact : undefined +} + +export const setContact = async (phone: string, id: string, contact: Partial | undefined ) => { + const key = contactKey(phone, id) + const currentContact = await getContact(phone, id) || {} + await client.set(key, JSON.stringify({ ...currentContact, ...(contact || {} )})) +} + export const getMessageStatus = async (phone: string, id: string) => { const key = messageStatusKey(phone, id) return redisGet(key) From e786bd3546acae04812f0099b7a85ebcc78ca099 Mon Sep 17 00:00:00 2001 From: Clairton Rodrigo Heinzen Date: Thu, 13 Feb 2025 18:14:03 -0300 Subject: [PATCH 2/2] save contact --- README.md | 3 ++- src/services/client_baileys.ts | 33 ++++++++++++++++++++++++++++---- src/services/data_store.ts | 4 +++- src/services/data_store_file.ts | 22 ++++++++++++++++++++- src/services/data_store_redis.ts | 19 +++++++++++++++++- src/services/listener_baileys.ts | 4 ++-- src/services/transformer.ts | 2 +- 7 files changed, 76 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 70ab8197..a5c9a2cd 100644 --- a/README.md +++ b/README.md @@ -153,12 +153,13 @@ To send media curl -i -X POST \ http://localhost:9876/v15.0/5549988290955/messages \ -H 'Content-Type: application/json' \ +-H 'Authorization: 1' \ -d '{ "messaging_product": "whatsapp", "to": "5549988290955", "type": "image", "image": { - "link" : "https://github.githubassets.com/favicons/favicon-dark.png" + "link" : "https://github.githubassets.com/favicons/favicon.png" } }' ``` diff --git a/src/services/client_baileys.ts b/src/services/client_baileys.ts index 20163a27..629c0b59 100644 --- a/src/services/client_baileys.ts +++ b/src/services/client_baileys.ts @@ -1,4 +1,4 @@ -import { GroupMetadata, WAMessage, proto, delay, isJidGroup, jidNormalizedUser, AnyMessageContent } from 'baileys' +import { GroupMetadata, WAMessage, proto, delay, isJidGroup, jidNormalizedUser, AnyMessageContent, Contact as ContactBaileys } from 'baileys' import fetch, { Response as FetchResponse } from 'node-fetch' import { Incoming } from './incoming' import { Listener } from './listener' @@ -305,9 +305,22 @@ export class ClientBaileys implements Client { }) if (!this.config.ignoreHistoryMessages) { logger.info('Config import history messages %', this.phone) - this.event('messaging-history.set', async ({ messages, isLatest }: { messages: proto.IWebMessageInfo[]; isLatest?: boolean }) => { - logger.info('Importing history messages, is latest %s %s', isLatest, this.phone) - this.listener.process(this.phone, messages, 'history') + this.event('messaging-history.set', async ({ messages, isLatest, contacts }: { messages: proto.IWebMessageInfo[]; contacts: ContactBaileys[]; isLatest?: boolean }) => { + if (contacts) { + const { dataStore } = this.store! + await Promise.all(contacts.map(async (c: Partial) => { + const cId = c.lid || c.id + if (cId && c.imgUrl) { + await dataStore.setImageUrl(cId, c.imgUrl) + } + return dataStore.setContact(c) + }) + ) + } + if (messages) { + logger.info('Importing history messages, is latest %s %s', isLatest, this.phone) + this.listener.process(this.phone, messages, 'history') + } }) } // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -625,6 +638,18 @@ export class ClientBaileys implements Client { remoteJid = key.remoteJid } if (remoteJid) { + if (key.fromMe) { + const { dataStore } = this.store! + logger.debug('Loading contact for %s...', remoteJid) + const contact = await dataStore.getContact(remoteJid) + logger.debug('Loaded contact for %s => %s', remoteJid, contact) + if (contact?.name) { + message['contactName'] = contact.name + } + if (contact?.verifiedName) { + message['contactBizName'] = contact?.verifiedName + } + } const jid = await this.exists(remoteJid) if (jid) { try { diff --git a/src/services/data_store.ts b/src/services/data_store.ts index 549793cf..543f9b5d 100644 --- a/src/services/data_store.ts +++ b/src/services/data_store.ts @@ -1,4 +1,4 @@ -import { GroupMetadata, makeInMemoryStore, WAMessage, WAMessageKey, WASocket } from 'baileys' +import { Contact, GroupMetadata, makeInMemoryStore, WAMessage, WAMessageKey, WASocket } from 'baileys' import { Config } from './config' export const dataStores: Map = new Map() @@ -29,6 +29,8 @@ export type DataStore = ReturnType & { loadImageUrl: (jid: string, sock: Partial) => Promise setGroupMetada: (jid: string, data: GroupMetadata) => Promise getGroupMetada: (jid: string) => Promise + setContact: (contact: Partial) => Promise + getContact: (id: string) => Promise loadGroupMetada: (jid: string, sock: Partial) => Promise loadUnoId: (id: string) => Promise setStatus: (id: string, status: MessageStatus) => Promise diff --git a/src/services/data_store_file.ts b/src/services/data_store_file.ts index 66f0ed31..bdccd6ba 100644 --- a/src/services/data_store_file.ts +++ b/src/services/data_store_file.ts @@ -111,6 +111,7 @@ const dataStoreFile = async (phone: string, config: Config): Promise logger.debug('contacts.upsert %s', phone, JSON.stringify(contacts)) const { saveProfilePicture } = mediaStore await Promise.all(contacts.map(async (c) => { + await dataStore.setContact(c) return saveProfilePicture(c) }) ) @@ -118,9 +119,28 @@ const dataStoreFile = async (phone: string, config: Config): Promise ev.on('contacts.update', async (contacts: Partial[]) => { logger.debug('contacts.update %s => %s', phone, JSON.stringify(contacts)) const { saveProfilePicture } = mediaStore - await Promise.all(contacts.map(async (c) => saveProfilePicture(c))) + await Promise.all(contacts.map(async (c: Partial) => { + await dataStore.setContact(c) + return saveProfilePicture(c) + }) + ) }) } + dataStore.setContact = async (contact: Partial) => { + const id = jidToPhoneNumber(contact.id || contact.lid) + const newData: Partial = {} + if (contact.name) { + newData.name = contact.name + } + if (contact.verifiedName) { + newData.verifiedName = contact.verifiedName + } + dataStore.contacts[id] = { ...(dataStore.contacts[id] || {}), ...newData } + } + dataStore.getContact = async (id: string) => { + const newId = jidToPhoneNumber(id) + return dataStore.contacts[newId] + } const loadKey = async (id: string) => { return keys.get(id) } diff --git a/src/services/data_store_redis.ts b/src/services/data_store_redis.ts index 73d104e6..924408b6 100644 --- a/src/services/data_store_redis.ts +++ b/src/services/data_store_redis.ts @@ -1,4 +1,4 @@ -import { proto, WAMessage, WAMessageKey, GroupMetadata } from 'baileys' +import { proto, WAMessage, WAMessageKey, GroupMetadata, Contact } from 'baileys' import { DataStore, MessageStatus } from './data_store' import { jidToPhoneNumber, phoneNumberToJid, isIndividualJid } from './transformer' import { getDataStore, dataStores } from './data_store' @@ -22,6 +22,8 @@ import { getGroup, delConfig, setTemplates, + setContact, + getContact, } from './redis' import { Config } from './config' import logger from './logger' @@ -66,6 +68,21 @@ const dataStoreRedis = async (phone: string, config: Config): Promise } } } + store.setContact = async (contact: Partial) => { + const id = jidToPhoneNumber(contact.id || contact.lid) + const newData: Partial = {} + if (contact.name) { + newData.name = contact.name + } + if (contact.verifiedName) { + newData.verifiedName = contact.verifiedName + } + return setContact(phone, id, newData) + } + store.getContact = async (id: string) => { + const newId = jidToPhoneNumber(id) + return getContact(phone, newId) + } store.getGroupMetada = async (jid: string) => { return getGroup(phone, jid) } diff --git a/src/services/listener_baileys.ts b/src/services/listener_baileys.ts index 81b35dc1..ff3ff4b3 100644 --- a/src/services/listener_baileys.ts +++ b/src/services/listener_baileys.ts @@ -89,9 +89,9 @@ export class ListenerBaileys implements Listener { const store = await config.getStore(phone, config) if (messageType && !['update', 'receipt'].includes(messageType)) { i = await config.getMessageMetadata(i) - if (i.key && i.key) { + if (i.key && i.key.id) { const idUno = uuid() - const idBaileys = i.key.id! + const idBaileys = i.key.id await store?.dataStore.setUnoId(idBaileys, idUno) await store?.dataStore.setKey(idUno, i.key) await store?.dataStore.setKey(idBaileys, i.key) diff --git a/src/services/transformer.ts b/src/services/transformer.ts index a22d3fc0..9e148ffa 100644 --- a/src/services/transformer.ts +++ b/src/services/transformer.ts @@ -464,7 +464,7 @@ export const fromBaileysMessageContent = (phone: string, payload: any, config?: const binMessage = payload.update || payload.receipt || (messageType && payload.message && payload.message[messageType]) let profileName if (fromMe) { - profileName = senderPhone + profileName = payload.contactBizName || payload.contactName || senderPhone } else { profileName = payload.verifiedBizName || payload.pushName || senderPhone }