import { readFileSync, writeFileSync, existsSync } from 'fs' /** * @type {import('@adiwajshing/baileys')} */ const { initAuthCreds, BufferJSON, proto } = (await import('@adiwajshing/baileys')).default /** * @param {import('@adiwajshing/baileys').WASocket | import('@adiwajshing/baileys').WALegacySocket} */ function bind(conn) { if (!conn.chats) conn.chats = {} /** * * @param {import('@adiwajshing/baileys').Contact[]|{contacts:import('@adiwajshing/baileys').Contact[]}} contacts * @returns */ function updateNameToDb(contacts) { if (!contacts) return try { contacts = contacts.contacts || contacts for (const contact of contacts) { const id = conn.decodeJid(contact.id) if (!id || id === 'status@broadcast') continue let chats = conn.chats[id] if (!chats) chats = conn.chats[id] = { ...contact, id } conn.chats[id] = { ...chats, ...({ ...contact, id, ...(id.endsWith('@g.us') ? { subject: contact.subject || contact.name || chats.subject || '' } : { name: contact.notify || contact.name || chats.name || chats.notify || '' }) } || {}) } } } catch (e) { console.error(e) } } conn.ev.on('contacts.upsert', updateNameToDb) conn.ev.on('groups.update', updateNameToDb) conn.ev.on('contacts.set', updateNameToDb) conn.ev.on('chats.set', async ({ chats }) => { try { for (let { id, name, readOnly } of chats) { id = conn.decodeJid(id) if (!id || id === 'status@broadcast') continue const isGroup = id.endsWith('@g.us') let chats = conn.chats[id] if (!chats) chats = conn.chats[id] = { id } chats.isChats = !readOnly if (name) chats[isGroup ? 'subject' : 'name'] = name if (isGroup) { const metadata = await conn.groupMetadata(id).catch(_ => null) if (name || metadata?.subject) chats.subject = name || metadata.subject if (!metadata) continue chats.metadata = metadata } } } catch (e) { console.error(e) } }) conn.ev.on('group-participants.update', async function updateParticipantsToDb({ id, participants, action }) { if (!id) return id = conn.decodeJid(id) if (id === 'status@broadcast') return if (!(id in conn.chats)) conn.chats[id] = { id } let chats = conn.chats[id] chats.isChats = true const groupMetadata = await conn.groupMetadata(id).catch(_ => null) if (!groupMetadata) return chats.subject = groupMetadata.subject chats.metadata = groupMetadata }) conn.ev.on('groups.update', async function groupUpdatePushToDb(groupsUpdates) { try { for (const update of groupsUpdates) { const id = conn.decodeJid(update.id) if (!id || id === 'status@broadcast') continue const isGroup = id.endsWith('@g.us') if (!isGroup) continue let chats = conn.chats[id] if (!chats) chats = conn.chats[id] = { id } chats.isChats = true const metadata = await conn.groupMetadata(id).catch(_ => null) if (metadata) chats.metadata = metadata if (update.subject || metadata?.subject) chats.subject = update.subject || metadata.subject } } catch (e) { console.error(e) } }) conn.ev.on('chats.upsert', function chatsUpsertPushToDb(chatsUpsert) { try { const { id, name } = chatsUpsert if (!id || id === 'status@broadcast') return conn.chats[id] = { ...(conn.chats[id] || {}), ...chatsUpsert, isChats: true } const isGroup = id.endsWith('@g.us') if (isGroup) conn.insertAllGroup().catch(_ => null) } catch (e) { console.error(e) } }) conn.ev.on('presence.update', async function presenceUpdatePushToDb({ id, presences }) { try { const sender = Object.keys(presences)[0] || id const _sender = conn.decodeJid(sender) const presence = presences[sender]['lastKnownPresence'] || 'composing' let chats = conn.chats[_sender] if (!chats) chats = conn.chats[_sender] = { id: sender } chats.presences = presence if (id.endsWith('@g.us')) { let chats = conn.chats[id] if (!chats) chats = conn.chats[id] = { id } } } catch (e) { console.error(e) } }) } const KEY_MAP = { 'pre-key': 'preKeys', 'session': 'sessions', 'sender-key': 'senderKeys', 'app-state-sync-key': 'appStateSyncKeys', 'app-state-sync-version': 'appStateVersions', 'sender-key-memory': 'senderKeyMemory' } /** * * @param {String} filename * @param {import('pino').Logger} logger * @returns */ function useSingleFileAuthState(filename, logger) { let creds, keys = {}, saveCount = 0 // save the authentication state to a file const saveState = (forceSave) => { logger?.trace('saving auth state') saveCount++ if (forceSave || saveCount > 5) { writeFileSync( filename, // BufferJSON replacer utility saves buffers nicely JSON.stringify({ creds, keys }, BufferJSON.replacer, 2) ) saveCount = 0 } } if (existsSync(filename)) { const result = JSON.parse( readFileSync(filename, { encoding: 'utf-8' }), BufferJSON.reviver ) creds = result.creds keys = result.keys } else { creds = initAuthCreds() keys = {} } return { state: { creds, keys: { get: (type, ids) => { const key = KEY_MAP[type] return ids.reduce( (dict, id) => { let value = keys[key]?.[id] if (value) { if (type === 'app-state-sync-key') { value = proto.AppStateSyncKeyData.fromObject(value) } dict[id] = value } return dict }, {} ) }, set: (data) => { for (const _key in data) { const key = KEY_MAP[_key] keys[key] = keys[key] || {} Object.assign(keys[key], data[_key]) } saveState() } } }, saveState } } export default { bind, useSingleFileAuthState }