import path from 'path'; import {toAudio} from './converter.js'; import chalk from 'chalk'; import fetch from 'node-fetch'; import PhoneNumber from 'awesome-phonenumber'; import fs from 'fs'; import util from 'util'; import {fileTypeFromBuffer} from 'file-type'; import {format} from 'util'; import {fileURLToPath} from 'url'; import store from './store.js'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); /** * @type {import('@whiskeysockets/baileys')} */ const { default: _makeWaSocket, makeWALegacySocket, proto, downloadContentFromMessage, jidDecode, areJidsSameUser, generateWAMessage, generateForwardMessageContent, generateWAMessageFromContent, WAMessageStubType, extractMessageContent, WA_DEFAULT_EPHEMERAL, prepareWAMessageMedia, } = (await import('@whiskeysockets/baileys')).default; export function makeWASocket(connectionOptions, options = {}) { /** * @type {import('@whiskeysockets/baileys').WASocket | import('@whiskeysockets/baileys').WALegacySocket} */ const conn = (global.opts['legacy'] ? makeWALegacySocket : _makeWaSocket)(connectionOptions); const sock = Object.defineProperties(conn, { chats: { value: {...(options.chats || {})}, writable: true, }, decodeJid: { value(jid) { if (!jid || typeof jid !== 'string') return (!nullish(jid) && jid) || null; return jid.decodeJid(); }, }, logger: { get() { return { info(...args) { console.log( chalk.bold.bgRgb(51, 204, 51)('INFO '), `[${chalk.rgb(255, 255, 255)(new Date().toUTCString())}]:`, chalk.cyan(format(...args)), ); }, error(...args) { console.log( chalk.bold.bgRgb(247, 38, 33)('ERROR '), `[${chalk.rgb(255, 255, 255)(new Date().toUTCString())}]:`, chalk.rgb(255, 38, 0)(format(...args)), ); }, warn(...args) { console.log( chalk.bold.bgRgb(255, 153, 0)('WARNING '), `[${chalk.rgb(255, 255, 255)(new Date().toUTCString())}]:`, chalk.redBright(format(...args)), ); }, trace(...args) { console.log( chalk.grey('TRACE '), `[${chalk.rgb(255, 255, 255)(new Date().toUTCString())}]:`, chalk.white(format(...args)), ); }, debug(...args) { console.log( chalk.bold.bgRgb(66, 167, 245)('DEBUG '), `[${chalk.rgb(255, 255, 255)(new Date().toUTCString())}]:`, chalk.white(format(...args)), ); }, }; }, enumerable: true, }, sendNyanCat: { async value(jid, text = '', buffer, title, body, url, quoted, options) { if (buffer) { try { (type = await conn.getFile(buffer), buffer = type.data); } catch { buffer = buffer; } } const prep = generateWAMessageFromContent(jid, {extendedTextMessage: {text: text, contextInfo: {externalAdReply: {title: title, body: body, thumbnail: buffer, sourceUrl: url}, mentionedJid: await conn.parseMention(text)}}}, {quoted: quoted}); return conn.relayMessage(jid, prep.message, {messageId: prep.key.id}); }, }, sendPayment: { async value(jid, amount, text, quoted, options) { conn.relayMessage(jid, { requestPaymentMessage: { currencyCodeIso4217: 'PEN', amount1000: amount, requestFrom: null, noteMessage: { extendedTextMessage: { text: text, contextInfo: { externalAdReply: { showAdAttribution: true, }, mentionedJid: conn.parseMention(text)}}}}}, {}); }, }, getFile: { /** * getBuffer hehe * @param {fs.PathLike} PATH * @param {Boolean} saveToFile */ async value(PATH, saveToFile = false) { let res; let filename; const data = Buffer.isBuffer(PATH) ? PATH : PATH instanceof ArrayBuffer ? PATH.toBuffer() : /^data:.*?\/.*?;base64,/i.test(PATH) ? Buffer.from(PATH.split`,`[1], 'base64') : /^https?:\/\//.test(PATH) ? await (res = await fetch(PATH)).buffer() : fs.existsSync(PATH) ? (filename = PATH, fs.readFileSync(PATH)) : typeof PATH === 'string' ? PATH : Buffer.alloc(0); if (!Buffer.isBuffer(data)) throw new TypeError('Result is not a buffer'); const type = await fileTypeFromBuffer(data) || { mime: 'application/octet-stream', ext: '.bin', }; if (data && saveToFile && !filename) (filename = path.join(__dirname, '../tmp/' + new Date * 1 + '.' + type.ext), await fs.promises.writeFile(filename, data)); return { res, filename, ...type, data, deleteFile() { return filename && fs.promises.unlink(filename); }, }; }, enumerable: true, }, waitEvent: { /** * waitEvent * @param {String} eventName * @param {Boolean} is * @param {Number} maxTries */ value(eventName, is = () => true, maxTries = 25) { // Idk why this exist? return new Promise((resolve, reject) => { let tries = 0; const on = (...args) => { if (++tries > maxTries) reject('Max tries reached'); else if (is()) { conn.ev.off(eventName, on); resolve(...args); } }; conn.ev.on(eventName, on); }); }, }, relayWAMessage: { async value(pesanfull) { if (pesanfull.message.audioMessage) { await conn.sendPresenceUpdate('recording', pesanfull.key.remoteJid); } else { await conn.sendPresenceUpdate('composing', pesanfull.key.remoteJid); } const mekirim = await conn.relayMessage(pesanfull.key.remoteJid, pesanfull.message, {messageId: pesanfull.key.id}); conn.ev.emit('messages.upsert', {messages: [pesanfull], type: 'append'}); return mekirim; }, }, sendFile: { /** * Send Media/File with Automatic Type Specifier * @param {String} jid * @param {String|Buffer} path * @param {String} filename * @param {String} caption * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo} quoted * @param {Boolean} ptt * @param {Object} options */ async value(jid, path, filename = '', caption = '', quoted, ptt = false, options = {}) { const type = await conn.getFile(path, true); let {res, data: file, filename: pathFile} = type; if (res && res.status !== 200 || file.length <= 65536) { try { throw {json: JSON.parse(file.toString())}; } catch (e) { if (e.json) throw e.json; } } // const fileSize = fs.statSync(pathFile).size / 1024 / 1024 // if (fileSize >= 100) throw new Error('File size is too big!') const opt = {}; if (quoted) opt.quoted = quoted; if (!type) options.asDocument = true; let mtype = ''; let mimetype = options.mimetype || type.mime; let convert; if (/webp/.test(type.mime) || (/image/.test(type.mime) && options.asSticker)) mtype = 'sticker'; else if (/image/.test(type.mime) || (/webp/.test(type.mime) && options.asImage)) mtype = 'image'; else if (/video/.test(type.mime)) mtype = 'video'; else if (/audio/.test(type.mime)) { ( convert = await toAudio(file, type.ext), file = convert.data, pathFile = convert.filename, mtype = 'audio', mimetype = options.mimetype || 'audio/mpeg; codecs=opus' ); } else mtype = 'document'; if (options.asDocument) mtype = 'document'; delete options.asSticker; delete options.asLocation; delete options.asVideo; delete options.asDocument; delete options.asImage; const message = { ...options, caption, ptt, [mtype]: {url: pathFile}, mimetype, fileName: filename || pathFile.split('/').pop(), }; /** * @type {import('@whiskeysockets/baileys').proto.WebMessageInfo} */ let m; try { m = await conn.sendMessage(jid, message, {...opt, ...options}); } catch (e) { console.error(e); m = null; } finally { if (!m) m = await conn.sendMessage(jid, {...message, [mtype]: file}, {...opt, ...options}); file = null; // releasing the memory return m; } }, enumerable: true, }, sendContact: { /** * Send Contact * @param {String} jid * @param {String[][]|String[]} data * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo} quoted * @param {Object} options */ async value(jid, data, quoted, options) { if (!Array.isArray(data[0]) && typeof data[0] === 'string') data = [data]; const contacts = []; for (let [number, name] of data) { number = number.replace(/[^0-9]/g, ''); const njid = number + '@s.whatsapp.net'; const biz = await conn.getBusinessProfile(njid).catch((_) => null) || {}; const vcard = ` BEGIN:VCARD VERSION:3.0 N:;${name.replace(/\n/g, '\\n')};;; FN:${name.replace(/\n/g, '\\n')} TEL;type=CELL;type=VOICE;waid=${number}:${PhoneNumber('+' + number).getNumber('international')}${biz.description ? ` X-WA-BIZ-NAME:${(conn.chats[njid]?.vname || conn.getName(njid) || name).replace(/\n/, '\\n')} X-WA-BIZ-DESCRIPTION:${biz.description.replace(/\n/g, '\\n')} `.trim() : ''} END:VCARD `.trim(); contacts.push({vcard, displayName: name}); } return await conn.sendMessage(jid, { ...options, contacts: { ...options, displayName: (contacts.length >= 2 ? `${contacts.length} kontak` : contacts[0].displayName) || null, contacts, }, }, {quoted, ...options}); }, enumerable: true, }, reply: { /** * Reply to a message * @param {String} jid * @param {String|Buffer} text * @param {import('@adiwajshing/baileys').proto.WebMessageInfo} quoted * @param {Object} options */ async value(jid, text = '', quoted, options) { if (Buffer.isBuffer(text)) { return conn.sendFile(jid, text, 'file', '', quoted, false, options) } else { let canalId = ["120363371008200788@newsletter", "120363371008200788@newsletter", "120363371008200788@newsletter", "120363178718483875@newsletter"] let canalNombre = ["Hack Store X 💫", "Kantu Bot Ofc ⚡", "The Kantu Bot 🔥", "KantuBot Test ✨"] async function getRandomChannel() { let randomIndex = Math.floor(Math.random() * canalId.length) let id = canalId[randomIndex] let nombre = canalNombre[randomIndex] return { id, nombre } } let randomChannel = await getRandomChannel() const contextInfo = { mentionedJid: await conn.parseMention(text), isForwarded: true, forwardingScore: 1, forwardedNewsletterMessageInfo: { newsletterJid: randomChannel.id, newsletterName: randomChannel.nombre }} const messageOptions = { ...options, text, contextInfo } return conn.sendMessage(jid, messageOptions, { quoted, ...options }) }} }, // sendButton: { /** * send Button * @param {String} jid * @param {String} text * @param {String} footer * @param {Buffer} buffer * @param {String[] | String[][]} buttons * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo} quoted * @param {Object} options */ /* async value(jid, text = '', footer = '', buffer, buttons, quoted, options) { let type if (Array.isArray(buffer)) (options = quoted, quoted = buttons, buttons = buffer, buffer = null) else if (buffer) try { (type = await conn.getFile(buffer), buffer = type.data) } catch { buffer = null } if (!Array.isArray(buttons[0]) && typeof buttons[0] === 'string') buttons = [buttons] if (!options) options = {} let message = { ...options, [buffer ? 'caption' : 'text']: text || '', footer, buttons: buttons.map(btn => ({ buttonId: !nullish(btn[1]) && btn[1] || !nullish(btn[0]) && btn[0] || '', buttonText: { displayText: !nullish(btn[0]) && btn[0] || !nullish(btn[1]) && btn[1] || '' } })), ...(buffer ? options.asLocation && /image/.test(type.mime) ? { location: { ...options, jpegThumbnail: buffer } } : { [/video/.test(type.mime) ? 'video' : /image/.test(type.mime) ? 'image' : 'document']: buffer } : {}) } return await conn.sendMessage(jid, message, { quoted, upload: conn.waUploadToServer, ...options }) }, enumerable: true }, */ //-- new sendButton: { async value(jid, text = '', footer = '', buffer, buttons, copy, urls, list, quoted, options) { let img, video if (/^https?:\/\//i.test(buffer)) { try { // Obtener el tipo MIME de la URL const response = await fetch(buffer) const contentType = response.headers.get('content-type') if (/^image\//i.test(contentType)) { img = await prepareWAMessageMedia({ image: { url: buffer } }, { upload: conn.waUploadToServer }) } else if (/^video\//i.test(contentType)) { video = await prepareWAMessageMedia({ video: { url: buffer } }, { upload: conn.waUploadToServer }) } else { console.error("Tipo MIME no compatible:", contentType) } } catch (error) { console.error("Error al obtener el tipo MIME:", error) } } else { try { const type = await conn.getFile(buffer) if (/^image\//i.test(type.mime)) { img = await prepareWAMessageMedia({ image: { url: buffer } }, { upload: conn.waUploadToServer }) } else if (/^video\//i.test(type.mime)) { video = await prepareWAMessageMedia({ video: { url: buffer } }, { upload: conn.waUploadToServer }) } } catch (error) { console.error("Error al obtener el tipo de archivo:", error); } } const dynamicButtons = [] // Botones de tipo quick_reply if (buttons && Array.isArray(buttons)) { dynamicButtons.push(...buttons.map(btn => ({ name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: btn[0], id: btn[1] }) }))); } // Botones de copiar if (copy && Array.isArray(copy)) { dynamicButtons.push(...copy.map(copyBtn => ({ name: 'cta_copy', buttonParamsJson: JSON.stringify({ display_text: copyBtn[0] || 'Copy', copy_code: copyBtn[1] }) }))); } // Botones de URL if (urls && Array.isArray(urls)) { urls.forEach(url => { dynamicButtons.push({ name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: url[0], url: url[1], merchant_url: url[1] }) }); }); } // Botones de lista if (list && Array.isArray(list)) { list.forEach(lister => { dynamicButtons.push({ name: 'single_select', buttonParamsJson: JSON.stringify({ title: lister[0], sections: lister[1] }) }) }) } const interactiveMessage = { body: { text: text }, footer: { text: footer }, header: { hasMediaAttachment: false, imageMessage: img ? img.imageMessage : null, videoMessage: video ? video.videoMessage : null }, nativeFlowMessage: { buttons: dynamicButtons, messageParamsJson: '' } } let msgL = generateWAMessageFromContent(jid, { viewOnceMessage: { message: { interactiveMessage } } }, { userJid: conn.user.jid, quoted }) conn.relayMessage(jid, msgL.message, { messageId: msgL.key.id, ...options }) } }, sendAlbumMessage: { async value(jid, medias, caption = "", quoted = null) { let img, video; const album = generateWAMessageFromContent(jid, { albumMessage: { expectedImageCount: medias.filter(media => media.type === "image").length, expectedVideoCount: medias.filter(media => media.type === "video").length, ...(quoted ? { contextInfo: { remoteJid: quoted.key.remoteJid, fromMe: quoted.key.fromMe, stanzaId: quoted.key.id, participant: quoted.key.participant || quoted.key.remoteJid, quotedMessage: quoted.message } } : {}) } }, { quoted: quoted }); await conn.relayMessage(album.key.remoteJid, album.message, { messageId: album.key.id }); for (const media of medias) { const { type, data } = media; if (/^https?:\/\//i.test(data.url)) { try { const response = await fetch(data.url); const contentType = response.headers.get('content-type'); if (/^image\//i.test(contentType)) { img = await prepareWAMessageMedia({ image: { url: data.url } }, { upload: conn.waUploadToServer }); } else if (/^video\//i.test(contentType)) { video = await prepareWAMessageMedia({ video: { url: data.url } }, { upload: conn.waUploadToServer }); } } catch (error) { console.error("Error al obtener el tipo MIME:", error); } } const mediaMessage = await generateWAMessage(album.key.remoteJid, { [type]: data, ...(media === medias[0] ? { caption } : {}) }, { upload: conn.waUploadToServer }); mediaMessage.message.messageContextInfo = { messageAssociation: { associationType: 1, parentMessageKey: album.key } }; await conn.relayMessage(mediaMessage.key.remoteJid, mediaMessage.message, { messageId: mediaMessage.key.id }); } return album; } }, /** * Send nativeFlowMessage */ sendNCarousel: { async value(jid, text = '', footer = '', buffer, buttons, copy, urls, list, quoted, options) { let img, video; if (buffer) { if (/^https?:\/\//i.test(buffer)) { try { const response = await fetch(buffer); const contentType = response.headers.get('content-type'); if (/^image\//i.test(contentType)) { img = await prepareWAMessageMedia({ image: { url: buffer } }, { upload: conn.waUploadToServer, ...options }); } else if (/^video\//i.test(contentType)) { video = await prepareWAMessageMedia({ video: { url: buffer } }, { upload: conn.waUploadToServer, ...options }); } else { console.error("Incompatible MIME type:", contentType); } } catch (error) { console.error("Failed to get MIME type:", error); } } else { try { const type = await conn.getFile(buffer); if (/^image\//i.test(type.mime)) { img = await prepareWAMessageMedia({ image: (/^https?:\/\//i.test(buffer)) ? { url: buffer } : (type && type?.data) }, { upload: conn.waUploadToServer, ...options }); } else if (/^video\//i.test(type.mime)) { video = await prepareWAMessageMedia({ video: (/^https?:\/\//i.test(buffer)) ? { url: buffer } : (type && type?.data) }, { upload: conn.waUploadToServer, ...options }); } } catch (error) { console.error("Failed to get file type:", error); } } } const dynamicButtons = buttons.map(btn => ({ name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: btn[0], id: btn[1] }), })); dynamicButtons.push( (copy && (typeof copy === 'string' || typeof copy === 'number')) ? { name: 'cta_copy', buttonParamsJson: JSON.stringify({ display_text: 'Copy', copy_code: copy }) } : null) urls?.forEach(url => { dynamicButtons.push({ name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: url[0], url: url[1], merchant_url: url[1] }) }); }); list?.forEach(lister => { dynamicButtons.push({ name: 'single_select', buttonParamsJson: JSON.stringify({ title: lister[0], sections: lister[1] }) }); }) const interactiveMessage = { body: { text: text || '' }, footer: { text: footer || wm }, header: { hasMediaAttachment: img?.imageMessage || video?.videoMessage ? true : false, imageMessage: img?.imageMessage || null, videoMessage: video?.videoMessage || null }, nativeFlowMessage: { buttons: dynamicButtons.filter(Boolean), messageParamsJson: '' }, ...Object.assign({ mentions: typeof text === 'string' ? conn.parseMention(text || '@0') : [], contextInfo: { mentionedJid: typeof text === 'string' ? conn.parseMention(text || '@0') : [], } }, { ...(options || {}), ...(conn.temareply?.contextInfo && { contextInfo: { ...(options?.contextInfo || {}), ...conn.temareply?.contextInfo, externalAdReply: { ...(options?.contextInfo?.externalAdReply || {}), ...conn.temareply?.contextInfo?.externalAdReply, }, }, }) }) }; const messageContent = proto.Message.fromObject({ viewOnceMessage: { message: { messageContextInfo: { deviceListMetadata: {}, deviceListMetadataVersion: 2 }, interactiveMessage } } }); const msgs = await generateWAMessageFromContent(jid, messageContent, { userJid: conn.user.jid, quoted: quoted, upload: conn.waUploadToServer, ephemeralExpiration: WA_DEFAULT_EPHEMERAL }); await conn.relayMessage(jid, msgs.message, { messageId: msgs.key.id }); } }, /** * Send carouselMessage */ sendCarousel: { async value(jid, text = '', footer = '', messages, quoted, options = {}) { try { if (messages.length > 1) { const cards = await Promise.all(messages.map(async ([text = '', footer = '', buffer, buttons, copy, urls, list]) => { let img, video; if (/^https?:\/\//i.test(buffer)) { try { const response = await fetch(buffer); const contentType = response.headers.get('content-type'); if (/^image\//i.test(contentType)) { img = await prepareWAMessageMedia({ image: { url: buffer } }, { upload: conn.waUploadToServer, ...options }); } else if (/^video\//i.test(contentType)) { video = await prepareWAMessageMedia({ video: { url: buffer } }, { upload: conn.waUploadToServer, ...options }); } else { console.error("Tipo MIME no compatible:", contentType); } } catch (error) { console.error("Error al obtener el tipo MIME:", error); } } else { try { const type = await conn.getFile(buffer); if (/^image\//i.test(type.mime)) { img = await prepareWAMessageMedia({ image: { url: buffer } }, { upload: conn.waUploadToServer, ...options }); } else if (/^video\//i.test(type.mime)) { video = await prepareWAMessageMedia({ video: { url: buffer } }, { upload: conn.waUploadToServer, ...options }); } } catch (error) { console.error("Error al obtener el tipo de archivo:", error); } } const dynamicButtons = []; if (buttons && Array.isArray(buttons)) { buttons.forEach(btn => { dynamicButtons.push({ name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: btn[0], id: btn[1] }) }); }); } if (copy && Array.isArray(copy)) { copy.forEach(copyBtn => { dynamicButtons.push({ name: 'cta_copy', buttonParamsJson: JSON.stringify({ display_text: copyBtn[0] || 'Copy', copy_code: copyBtn[1] }) }); }); } if (urls && Array.isArray(urls)) { urls.forEach(url => { dynamicButtons.push({ name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: url[0], url: url[1], merchant_url: url[1] }) }); }); } if (list && Array.isArray(list)) { list.forEach(lister => { dynamicButtons.push({ name: 'single_select', buttonParamsJson: JSON.stringify({ title: lister[0], sections: lister[1] }) }); }); } return { body: proto.Message.InteractiveMessage.Body.fromObject({ text: text || '' }), footer: proto.Message.InteractiveMessage.Footer.fromObject({ text: footer || null }), header: proto.Message.InteractiveMessage.Header.fromObject({ title: text || null, subtitle: text || null, hasMediaAttachment: img?.imageMessage || video?.videoMessage ? true : false, imageMessage: img?.imageMessage || null, videoMessage: video?.videoMessage || null }), nativeFlowMessage: proto.Message.InteractiveMessage.NativeFlowMessage.fromObject({ buttons: dynamicButtons.filter(Boolean), messageParamsJson: '' }) }; })); const interactiveMessage = proto.Message.InteractiveMessage.create({ body: proto.Message.InteractiveMessage.Body.fromObject({ text: text || '' }), footer: proto.Message.InteractiveMessage.Footer.fromObject({ text: footer || '' }), header: proto.Message.InteractiveMessage.Header.fromObject({ title: text || '', subtitle: text || '', hasMediaAttachment: false }), carouselMessage: proto.Message.InteractiveMessage.CarouselMessage.fromObject({ cards: cards }) }); const messageContent = proto.Message.fromObject({ viewOnceMessage: { message: { messageContextInfo: { deviceListMetadata: {}, deviceListMetadataVersion: 2 }, interactiveMessage } } }); const msgs = await generateWAMessageFromContent(jid, messageContent, { userJid: conn.user.jid, quoted: quoted, upload: conn.waUploadToServer, ephemeralExpiration: WA_DEFAULT_EPHEMERAL }); await conn.relayMessage(jid, msgs.message, { messageId: msgs.key.id }); } else { await conn.sendNCarousel(jid, ...messages[0], quoted, options); } } catch (error) { console.error("Error en sendCarousel:", error); throw error; } } }, sendButton2: { async value(jid, text = '', footer = '', buffer, buttons, copy, urls, quoted, options) { let img, video if (/^https?:\/\//i.test(buffer)) { try { // Obtener el tipo MIME de la URL const response = await fetch(buffer) const contentType = response.headers.get('content-type') if (/^image\//i.test(contentType)) { img = await prepareWAMessageMedia({ image: { url: buffer } }, { upload: conn.waUploadToServer }) } else if (/^video\//i.test(contentType)) { video = await prepareWAMessageMedia({ video: { url: buffer } }, { upload: conn.waUploadToServer }) } else { console.error("Tipo MIME no compatible:", contentType) } } catch (error) { console.error("Error al obtener el tipo MIME:", error) } } else { try { const type = await conn.getFile(buffer) if (/^image\//i.test(type.mime)) { img = await prepareWAMessageMedia({ image: { url: buffer } }, { upload: conn.waUploadToServer }) } else if (/^video\//i.test(type.mime)) { video = await prepareWAMessageMedia({ video: { url: buffer } }, { upload: conn.waUploadToServer }) } } catch (error) { console.error("Error al obtener el tipo de archivo:", error); } } const dynamicButtons = buttons.map(btn => ({ name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: btn[0], id: btn[1] }), })); if (copy && (typeof copy === 'string' || typeof copy === 'number')) { // Añadir botón de copiar dynamicButtons.push({ name: 'cta_copy', buttonParamsJson: JSON.stringify({ display_text: 'Copy', copy_code: copy }) }); } // Añadir botones de URL if (urls && Array.isArray(urls)) { urls.forEach(url => { dynamicButtons.push({ name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: url[0], url: url[1], merchant_url: url[1] }) }) }) } const interactiveMessage = { body: { text: text }, footer: { text: footer }, header: { hasMediaAttachment: false, imageMessage: img ? img.imageMessage : null, videoMessage: video ? video.videoMessage : null }, nativeFlowMessage: { buttons: dynamicButtons, messageParamsJson: '' } } let msgL = generateWAMessageFromContent(jid, { viewOnceMessage: { message: { interactiveMessage } } }, { userJid: conn.user.jid, quoted }) conn.relayMessage(jid, msgL.message, { messageId: msgL.key.id, ...options }) } }, //--- sendList: { async value(jid, title, text, buttonText, buffer, listSections, quoted, options = {}) { let img, video if (/^https?:\/\//i.test(buffer)) { try { // Obtener el tipo MIME de la URL const response = await fetch(buffer) const contentType = response.headers.get('content-type') if (/^image\//i.test(contentType)) { img = await prepareWAMessageMedia({ image: { url: buffer } }, { upload: conn.waUploadToServer }) } else if (/^video\//i.test(contentType)) { video = await prepareWAMessageMedia({ video: { url: buffer } }, { upload: conn.waUploadToServer }) } else { console.error("Tipo MIME no compatible:", contentType) } } catch (error) { console.error("Error al obtener el tipo MIME:", error) } } else { try { const type = await conn.getFile(buffer) if (/^image\//i.test(type.mime)) { img = await prepareWAMessageMedia({ image: { url: buffer } }, { upload: conn.waUploadToServer }) } else if (/^video\//i.test(type.mime)) { video = await prepareWAMessageMedia({ video: { url: buffer } }, { upload: conn.waUploadToServer }) } } catch (error) { console.error("Error al obtener el tipo de archivo:", error); } } const sections = [...listSections] const message = { interactiveMessage: { header: {title: title, hasMediaAttachment: false, imageMessage: img ? img.imageMessage : null, videoMessage: video ? video.videoMessage : null } , body: {text: text}, nativeFlowMessage: { buttons: [ { name: 'single_select', buttonParamsJson: JSON.stringify({ title: buttonText, sections }) } ], messageParamsJson: '' } } }; let msgL = generateWAMessageFromContent(jid, { viewOnceMessage: { message} }, { userJid: conn.user.jid, quoted }) //await conn.relayMessage(jid, { viewOnceMessage: { message } }, {}); conn.relayMessage(jid, msgL.message, { messageId: msgL.key.id, ...options }) } }, sendPoll: { async value(jid, name = '', optiPoll, options) { if (!Array.isArray(optiPoll[0]) && typeof optiPoll[0] === 'string') optiPoll = [optiPoll]; if (!options) options = {}; const pollMessage = { name: name, options: optiPoll.map((btn) => ({ optionName: !nullish(btn[0]) && btn[0] || '', })), selectableOptionsCount: 1, }; return conn.relayMessage(jid, {pollCreationMessage: pollMessage}, {...options}); }, }, sendHydrated: { /** * * @param {String} jid * @param {String} text * @param {String} footer * @param {fs.PathLike} buffer * @param {String|string[]} url * @param {String|string[]} urlText * @param {String|string[]} call * @param {String|string[]} callText * @param {String[][]} buttons * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo} quoted * @param {Object} options */ async value(jid, text = '', footer = '', buffer, url, urlText, call, callText, buttons, quoted, options) { let type; if (buffer) { try { (type = await conn.getFile(buffer), buffer = type.data); } catch { buffer = buffer; } } if (buffer && !Buffer.isBuffer(buffer) && (typeof buffer === 'string' || Array.isArray(buffer))) (options = quoted, quoted = buttons, buttons = callText, callText = call, call = urlText, urlText = url, url = buffer, buffer = null); if (!options) options = {}; const templateButtons = []; if (url || urlText) { if (!Array.isArray(url)) url = [url]; if (!Array.isArray(urlText)) urlText = [urlText]; templateButtons.push(...( url.map((v, i) => [v, urlText[i]]) .map(([url, urlText], i) => ({ index: templateButtons.length + i + 1, urlButton: { displayText: !nullish(urlText) && urlText || !nullish(url) && url || '', url: !nullish(url) && url || !nullish(urlText) && urlText || '', }, })) || [] )); } if (call || callText) { if (!Array.isArray(call)) call = [call]; if (!Array.isArray(callText)) callText = [callText]; templateButtons.push(...( call.map((v, i) => [v, callText[i]]) .map(([call, callText], i) => ({ index: templateButtons.length + i + 1, callButton: { displayText: !nullish(callText) && callText || !nullish(call) && call || '', phoneNumber: !nullish(call) && call || !nullish(callText) && callText || '', }, })) || [] )); } if (buttons.length) { if (!Array.isArray(buttons[0])) buttons = [buttons]; templateButtons.push(...( buttons.map(([text, id], index) => ({ index: templateButtons.length + index + 1, quickReplyButton: { displayText: !nullish(text) && text || !nullish(id) && id || '', id: !nullish(id) && id || !nullish(text) && text || '', }, })) || [] )); } const message = { ...options, [buffer ? 'caption' : 'text']: text || '', footer, templateButtons, ...(buffer ? options.asLocation && /image/.test(type.mime) ? { location: { ...options, jpegThumbnail: buffer, }, } : { [/video/.test(type.mime) ? 'video' : /image/.test(type.mime) ? 'image' : 'document']: buffer, } : {}), }; return await conn.sendMessage(jid, message, { quoted, upload: conn.waUploadToServer, ...options, }); }, enumerable: true, }, sendHydrated2: { /** * * @param {String} jid * @param {String} text * @param {String} footer * @param {fs.PathLike} buffer * @param {String|string[]} url * @param {String|string[]} urlText * @param {String|string[]} call * @param {String|string[]} callText * @param {String[][]} buttons * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo} quoted * @param {Object} options */ async value(jid, text = '', footer = '', buffer, url, urlText, url2, urlText2, buttons, quoted, options) { let type; if (buffer) { try { (type = await conn.getFile(buffer), buffer = type.data); } catch { buffer = buffer; } } if (buffer && !Buffer.isBuffer(buffer) && (typeof buffer === 'string' || Array.isArray(buffer))) (options = quoted, quoted = buttons, buttons = callText, callText = call, call = urlText, urlText = url, url = buffer, buffer = null); if (!options) options = {}; const templateButtons = []; if (url || urlText) { if (!Array.isArray(url)) url = [url]; if (!Array.isArray(urlText)) urlText = [urlText]; templateButtons.push(...( url.map((v, i) => [v, urlText[i]]) .map(([url, urlText], i) => ({ index: templateButtons.length + i + 1, urlButton: { displayText: !nullish(urlText) && urlText || !nullish(url) && url || '', url: !nullish(url) && url || !nullish(urlText) && urlText || '', }, })) || [] )); } if (url2 || urlText2) { if (!Array.isArray(url2)) url2 = [url2]; if (!Array.isArray(urlText2)) urlText2 = [urlText2]; templateButtons.push(...( url2.map((v, i) => [v, urlText2[i]]) .map(([url2, urlText2], i) => ({ index: templateButtons.length + i + 1, urlButton: { displayText: !nullish(urlText2) && urlText2 || !nullish(url2) && url2 || '', url: !nullish(url2) && url2 || !nullish(urlText2) && urlText2 || '', }, })) || [] )); } if (buttons.length) { if (!Array.isArray(buttons[0])) buttons = [buttons]; templateButtons.push(...( buttons.map(([text, id], index) => ({ index: templateButtons.length + index + 1, quickReplyButton: { displayText: !nullish(text) && text || !nullish(id) && id || '', id: !nullish(id) && id || !nullish(text) && text || '', }, })) || [] )); } const message = { ...options, [buffer ? 'caption' : 'text']: text || '', footer, templateButtons, ...(buffer ? options.asLocation && /image/.test(type.mime) ? { location: { ...options, jpegThumbnail: buffer, }, } : { [/video/.test(type.mime) ? 'video' : /image/.test(type.mime) ? 'image' : 'document']: buffer, } : {}), }; return await conn.sendMessage(jid, message, { quoted, upload: conn.waUploadToServer, ...options, }); }, enumerable: true, }, cMod: { /** * cMod * @param {String} jid * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo} message * @param {String} text * @param {String} sender * @param {*} options * @returns */ value(jid, message, text = '', sender = conn.user.jid, options = {}) { if (options.mentions && !Array.isArray(options.mentions)) options.mentions = [options.mentions]; const copy = message.toJSON(); delete copy.message.messageContextInfo; delete copy.message.senderKeyDistributionMessage; const mtype = Object.keys(copy.message)[0]; const msg = copy.message; const content = msg[mtype]; if (typeof content === 'string') msg[mtype] = text || content; else if (content.caption) content.caption = text || content.caption; else if (content.text) content.text = text || content.text; if (typeof content !== 'string') { msg[mtype] = {...content, ...options}; msg[mtype].contextInfo = { ...(content.contextInfo || {}), mentionedJid: options.mentions || content.contextInfo?.mentionedJid || [], }; } if (copy.participant) sender = copy.participant = sender || copy.participant; else if (copy.key.participant) sender = copy.key.participant = sender || copy.key.participant; if (copy.key.remoteJid.includes('@s.whatsapp.net')) sender = sender || copy.key.remoteJid; else if (copy.key.remoteJid.includes('@broadcast')) sender = sender || copy.key.remoteJid; copy.key.remoteJid = jid; copy.key.fromMe = areJidsSameUser(sender, conn.user.id) || false; return proto.WebMessageInfo.fromObject(copy); }, enumerable: true, }, copyNForward: { /** * Exact Copy Forward * @param {String} jid * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo} message * @param {Boolean|Number} forwardingScore * @param {Object} options */ async value(jid, message, forwardingScore = true, options = {}) { let vtype; if (options.readViewOnce && message.message.viewOnceMessage?.message) { vtype = Object.keys(message.message.viewOnceMessage.message)[0]; delete message.message.viewOnceMessage.message[vtype].viewOnce; message.message = proto.Message.fromObject( JSON.parse(JSON.stringify(message.message.viewOnceMessage.message)), ); message.message[vtype].contextInfo = message.message.viewOnceMessage.contextInfo; } const mtype = Object.keys(message.message)[0]; let m = generateForwardMessageContent(message, !!forwardingScore); const ctype = Object.keys(m)[0]; if (forwardingScore && typeof forwardingScore === 'number' && forwardingScore > 1) m[ctype].contextInfo.forwardingScore += forwardingScore; m[ctype].contextInfo = { ...(message.message[mtype].contextInfo || {}), ...(m[ctype].contextInfo || {}), }; m = generateWAMessageFromContent(jid, m, { ...options, userJid: conn.user.jid, }); await conn.relayMessage(jid, m.message, {messageId: m.key.id, additionalAttributes: {...options}}); return m; }, enumerable: true, }, fakeReply: { /** * Fake Replies * @param {String} jid * @param {String|Object} text * @param {String} fakeJid * @param {String} fakeText * @param {String} fakeGroupJid * @param {String} options */ value(jid, text = '', fakeJid = this.user.jid, fakeText = '', fakeGroupJid, options) { return conn.reply(jid, text, {key: {fromMe: areJidsSameUser(fakeJid, conn.user.id), participant: fakeJid, ...(fakeGroupJid ? {remoteJid: fakeGroupJid} : {})}, message: {conversation: fakeText}, ...options}); }, }, downloadM: { /** * Download media message * @param {Object} m * @param {String} type * @param {fs.PathLike | fs.promises.FileHandle} saveToFile * @return {Promise} */ async value(m, type, saveToFile) { let filename; if (!m || !(m.url || m.directPath)) return Buffer.alloc(0); const stream = await downloadContentFromMessage(m, type); let buffer = Buffer.from([]); for await (const chunk of stream) { buffer = Buffer.concat([buffer, chunk]); } if (saveToFile) ({filename} = await conn.getFile(buffer, true)); return saveToFile && fs.existsSync(filename) ? filename : buffer; }, enumerable: true, }, parseMention: { /** * Parses string into mentionedJid(s) * @param {String} text * @return {Array} */ value(text = '') { return [...text.matchAll(/@([0-9]{5,16}|0)/g)].map((v) => v[1] + '@s.whatsapp.net'); }, enumerable: true, }, getName: { /** * Get name from jid * @param {String} jid * @param {Boolean} withoutContact */ value(jid = '', withoutContact = false) { jid = conn.decodeJid(jid); withoutContact = conn.withoutContact || withoutContact; let v; if (jid.endsWith('@g.us')) { return new Promise(async (resolve) => { v = conn.chats[jid] || {}; if (!(v.name || v.subject)) v = await conn.groupMetadata(jid) || {}; resolve(v.name || v.subject || PhoneNumber('+' + jid.replace('@s.whatsapp.net', '')).getNumber('international')); }); } else { v = jid === '0@s.whatsapp.net' ? { jid, vname: 'WhatsApp', } : areJidsSameUser(jid, conn.user.id) ? conn.user : (conn.chats[jid] || {}); } return (withoutContact ? '' : v.name) || v.subject || v.vname || v.notify || v.verifiedName || PhoneNumber('+' + jid.replace('@s.whatsapp.net', '')).getNumber('international'); }, enumerable: true, }, loadMessage: { /** * * @param {String} messageID * @returns {import('@whiskeysockets/baileys').proto.WebMessageInfo} */ value(messageID) { return Object.entries(conn.chats) .filter(([_, {messages}]) => typeof messages === 'object') .find(([_, {messages}]) => Object.entries(messages) .find(([k, v]) => (k === messageID || v.key?.id === messageID))) ?.[1].messages?.[messageID]; }, enumerable: true, }, sendGroupV4Invite: { /** * sendGroupV4Invite * @param {String} jid * @param {*} participant * @param {String} inviteCode * @param {Number} inviteExpiration * @param {String} groupName * @param {String} caption * @param {Buffer} jpegThumbnail * @param {*} options */ async value(jid, participant, inviteCode, inviteExpiration, groupName = 'unknown subject', caption = 'Invitation to join my WhatsApp group', jpegThumbnail, options = {}) { const msg = proto.Message.fromObject({ groupInviteMessage: proto.GroupInviteMessage.fromObject({ inviteCode, inviteExpiration: parseInt(inviteExpiration) || + new Date(new Date + (3 * 86400000)), groupJid: jid, groupName: (groupName ? groupName : await conn.getName(jid)) || null, jpegThumbnail: Buffer.isBuffer(jpegThumbnail) ? jpegThumbnail : null, caption, }), }); const message = generateWAMessageFromContent(participant, msg, options); await conn.relayMessage(participant, message.message, {messageId: message.key.id, additionalAttributes: {...options}}); return message; }, enumerable: true, }, processMessageStubType: { /** * to process MessageStubType * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo} m */ async value(m) { if (!m.messageStubType) return; const chat = conn.decodeJid(m.key.remoteJid || m.message?.senderKeyDistributionMessage?.groupId || ''); if (!chat || chat === 'status@broadcast') return; const emitGroupUpdate = (update) => { conn.ev.emit('groups.update', [{ id: chat, ...update }]); }; switch (m.messageStubType) { case WAMessageStubType.REVOKE: case WAMessageStubType.GROUP_CHANGE_INVITE_LINK: if (Array.isArray(m.messageStubParameters) && m.messageStubParameters.length > 0) { emitGroupUpdate({ revoke: m.messageStubParameters[0] }); } break; case WAMessageStubType.GROUP_CHANGE_ICON: if (Array.isArray(m.messageStubParameters) && m.messageStubParameters.length > 0) { emitGroupUpdate({ icon: m.messageStubParameters[0] }); } break; default: { console.log({ messageStubType: m.messageStubType, messageStubParameters: m.messageStubParameters || [], type: WAMessageStubType[m.messageStubType] }); break; } } const isGroup = chat.endsWith('@g.us'); if (!isGroup) return; let chats = conn.chats[chat]; if (!chats) chats = conn.chats[chat] = { id: chat }; chats.isChats = true; const metadata = await conn.groupMetadata(chat).catch(() => null); if (!metadata) return; chats.subject = metadata.subject; chats.metadata = metadata; } }, insertAllGroup: { async value() { const groups = await conn.groupFetchAllParticipating().catch((_) => null) || {}; for (const group in groups) conn.chats[group] = {...(conn.chats[group] || {}), id: group, subject: groups[group].subject, isChats: true, metadata: groups[group]}; return conn.chats; }, }, pushMessage: { /** * pushMessage * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo[]} m */ async value(m) { if (!m) return; if (!Array.isArray(m)) m = [m]; for (const message of m) { try { // if (!(message instanceof proto.WebMessageInfo)) continue // https://github.com/adiwajshing/Baileys/pull/696/commits/6a2cb5a4139d8eb0a75c4c4ea7ed52adc0aec20f if (!message) continue; if (message.messageStubType && message.messageStubType != WAMessageStubType.CIPHERTEXT) conn.processMessageStubType(message).catch(console.error); const _mtype = Object.keys(message.message || {}); const mtype = (!['senderKeyDistributionMessage', 'messageContextInfo'].includes(_mtype[0]) && _mtype[0]) || (_mtype.length >= 3 && _mtype[1] !== 'messageContextInfo' && _mtype[1]) || _mtype[_mtype.length - 1]; const chat = conn.decodeJid(message.key.remoteJid || message.message?.senderKeyDistributionMessage?.groupId || ''); if (message.message?.[mtype]?.contextInfo?.quotedMessage) { /** * @type {import('@whiskeysockets/baileys').proto.IContextInfo} */ const context = message.message[mtype].contextInfo; let participant = conn.decodeJid(context.participant); const remoteJid = conn.decodeJid(context.remoteJid || participant); /** * @type {import('@whiskeysockets/baileys').proto.IMessage} * */ const quoted = message.message[mtype].contextInfo.quotedMessage; if ((remoteJid && remoteJid !== 'status@broadcast') && quoted) { let qMtype = Object.keys(quoted)[0]; if (qMtype == 'conversation') { quoted.extendedTextMessage = {text: quoted[qMtype]}; delete quoted.conversation; qMtype = 'extendedTextMessage'; } if (!quoted[qMtype].contextInfo) quoted[qMtype].contextInfo = {}; quoted[qMtype].contextInfo.mentionedJid = context.mentionedJid || quoted[qMtype].contextInfo.mentionedJid || []; const isGroup = remoteJid.endsWith('g.us'); if (isGroup && !participant) participant = remoteJid; const qM = { key: { remoteJid, fromMe: areJidsSameUser(conn.user.jid, remoteJid), id: context.stanzaId, participant, }, message: JSON.parse(JSON.stringify(quoted)), ...(isGroup ? {participant} : {}), }; let qChats = conn.chats[participant]; if (!qChats) qChats = conn.chats[participant] = {id: participant, isChats: !isGroup}; if (!qChats.messages) qChats.messages = {}; if (!qChats.messages[context.stanzaId] && !qM.key.fromMe) qChats.messages[context.stanzaId] = qM; let qChatsMessages; if ((qChatsMessages = Object.entries(qChats.messages)).length > 40) qChats.messages = Object.fromEntries(qChatsMessages.slice(30, qChatsMessages.length)); // maybe avoid memory leak } } if (!chat || chat === 'status@broadcast') continue; const isGroup = chat.endsWith('@g.us'); let chats = conn.chats[chat]; if (!chats) { if (isGroup) await conn.insertAllGroup().catch(console.error); chats = conn.chats[chat] = {id: chat, isChats: true, ...(conn.chats[chat] || {})}; } let metadata; let sender; if (isGroup) { if (!chats.subject || !chats.metadata) { metadata = await conn.groupMetadata(chat).catch((_) => ({})) || {}; if (!chats.subject) chats.subject = metadata.subject || ''; if (!chats.metadata) chats.metadata = metadata; } sender = conn.decodeJid(message.key?.fromMe && conn.user.id || message.participant || message.key?.participant || chat || ''); if (sender !== chat) { let chats = conn.chats[sender]; if (!chats) chats = conn.chats[sender] = {id: sender}; if (!chats.name) chats.name = message.pushName || chats.name || ''; } } else if (!chats.name) chats.name = message.pushName || chats.name || ''; if (['senderKeyDistributionMessage', 'messageContextInfo'].includes(mtype)) continue; chats.isChats = true; if (!chats.messages) chats.messages = {}; const fromMe = message.key.fromMe || areJidsSameUser(sender || chat, conn.user.id); if (!['protocolMessage'].includes(mtype) && !fromMe && message.messageStubType != WAMessageStubType.CIPHERTEXT && message.message) { delete message.message.messageContextInfo; delete message.message.senderKeyDistributionMessage; chats.messages[message.key.id] = JSON.parse(JSON.stringify(message, null, 2)); let chatsMessages; if ((chatsMessages = Object.entries(chats.messages)).length > 40) chats.messages = Object.fromEntries(chatsMessages.slice(30, chatsMessages.length)); } } catch (e) { console.error(e); } } }, }, serializeM: { /** * Serialize Message, so it easier to manipulate * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo} m */ value(m) { return smsg(conn, m); }, }, ...(typeof conn.chatRead !== 'function' ? { chatRead: { /** * Read message * @param {String} jid * @param {String|undefined|null} participant * @param {String} messageID */ value(jid, participant = conn.user.jid, messageID) { return conn.sendReadReceipt(jid, participant, [messageID]); }, enumerable: true, }, } : {}), ...(typeof conn.setStatus !== 'function' ? { setStatus: { /** * setStatus bot * @param {String} status */ value(status) { return conn.query({ tag: 'iq', attrs: { to: S_WHATSAPP_NET, type: 'set', xmlns: 'status', }, content: [ { tag: 'status', attrs: {}, content: Buffer.from(status, 'utf-8'), }, ], }); }, enumerable: true, }, } : {}), }); if (sock.user?.id) sock.user.jid = sock.decodeJid(sock.user.id); store.bind(sock); return sock; } /** * Serialize Message * @param {ReturnType} conn * @param {import('@whiskeysockets/baileys').proto.WebMessageInfo} m * @param {Boolean} hasParent */ export function smsg(conn, m, hasParent) { if (!m) return m; /** * @type {import('@whiskeysockets/baileys').proto.WebMessageInfo} */ const M = proto.WebMessageInfo; m = M.fromObject(m); m.conn = conn; let protocolMessageKey; if (m.message) { if (m.mtype == 'protocolMessage' && m.msg.key) { protocolMessageKey = m.msg.key; if (protocolMessageKey == 'status@broadcast') protocolMessageKey.remoteJid = m.chat; if (!protocolMessageKey.participant || protocolMessageKey.participant == 'status_me') protocolMessageKey.participant = m.sender; protocolMessageKey.fromMe = conn.decodeJid(protocolMessageKey.participant) === conn.decodeJid(conn.user.id); if (!protocolMessageKey.fromMe && protocolMessageKey.remoteJid === conn.decodeJid(conn.user.id)) protocolMessageKey.remoteJid = m.sender; } if (m.quoted) if (!m.quoted.mediaMessage) delete m.quoted.download; } if (!m.mediaMessage) delete m.download; try { if (protocolMessageKey && m.mtype == 'protocolMessage') conn.ev.emit('message.delete', protocolMessageKey); } catch (e) { console.error(e); } return m; } // https://github.com/Nurutomo/wabot-aq/issues/490 export function serialize() { const MediaType = ['imageMessage', 'videoMessage', 'audioMessage', 'stickerMessage', 'documentMessage']; return Object.defineProperties(proto.WebMessageInfo.prototype, { conn: { value: undefined, enumerable: false, writable: true, }, id: { get() { return this.key?.id; }, }, isBaileys: { get() { return (this?.fromMe || areJidsSameUser(this.conn?.user.id, this.sender)) && this.id.startsWith('3EB0') && (this.id.length === 20 || this.id.length === 22 || this.id.length === 12) || false }, }, chat: { get() { const senderKeyDistributionMessage = this.message?.senderKeyDistributionMessage?.groupId; return ( this.key?.remoteJid || (senderKeyDistributionMessage && senderKeyDistributionMessage !== 'status@broadcast' ) || '' ).decodeJid(); }, }, isGroup: { get() { return this.chat.endsWith('@g.us'); }, enumerable: true, }, sender: { get() { return this.conn?.decodeJid(this.key?.fromMe && this.conn?.user.id || this.participant || this.key.participant || this.chat || '') }, enumerable: true }, fromMe: { get() { return this.key?.fromMe || areJidsSameUser(this.conn?.user.id, this.sender) || false } }, mtype: { get() { if (!this.message) return ''; const type = Object.keys(this.message); return (!['senderKeyDistributionMessage', 'messageContextInfo'].includes(type[0]) && type[0]) || // Sometimes message in the front (type.length >= 3 && type[1] !== 'messageContextInfo' && type[1]) || // Sometimes message in midle if mtype length is greater than or equal to 3 type[type.length - 1]; // common case }, enumerable: true, }, msg: { get() { if (!this.message) return null; return this.message[this.mtype]; }, }, mediaMessage: { get() { if (!this.message) return null; const Message = ((this.msg?.url || this.msg?.directPath) ? {...this.message} : extractMessageContent(this.message)) || null; if (!Message) return null; const mtype = Object.keys(Message)[0]; return MediaType.includes(mtype) ? Message : null; }, enumerable: true, }, mediaType: { get() { let message; if (!(message = this.mediaMessage)) return null; return Object.keys(message)[0]; }, enumerable: true, }, quoted: { get() { /** * @type {ReturnType} */ const self = this; const msg = self.msg; const contextInfo = msg?.contextInfo; const quoted = contextInfo?.quotedMessage; if (!msg || !contextInfo || !quoted) return null; const type = Object.keys(quoted)[0]; const q = quoted[type]; const text = typeof q === 'string' ? q : q.text; return Object.defineProperties(JSON.parse(JSON.stringify(typeof q === 'string' ? {text: q} : q)), { mtype: { get() { return type; }, enumerable: true, }, mediaMessage: { get() { const Message = ((q.url || q.directPath) ? {...quoted} : extractMessageContent(quoted)) || null; if (!Message) return null; const mtype = Object.keys(Message)[0]; return MediaType.includes(mtype) ? Message : null; }, enumerable: true, }, mediaType: { get() { let message; if (!(message = this.mediaMessage)) return null; return Object.keys(message)[0]; }, enumerable: true, }, id: { get() { return contextInfo.stanzaId; }, enumerable: true, }, chat: { get() { return contextInfo.remoteJid || self.chat; }, enumerable: true, }, isBaileys: { get() { return (this?.fromMe || areJidsSameUser(this.conn?.user.id, this.sender)) && this.id.startsWith('3EB0') && (this.id.length === 20 || this.id.length === 22 || this.id.length === 12) || false }, enumerable: true, }, sender: { get() { return (contextInfo.participant || this.chat || '').decodeJid(); }, enumerable: true, }, fromMe: { get() { return areJidsSameUser(this.sender, self.conn?.user.jid); }, enumerable: true, }, text: { get() { return text || this.caption || this.contentText || this.selectedDisplayText || ''; }, enumerable: true, }, mentionedJid: { get() { return q.contextInfo?.mentionedJid || self.getQuotedObj()?.mentionedJid || []; }, enumerable: true, }, name: { get() { const sender = this.sender; return sender ? self.conn?.getName(sender) : null; }, enumerable: true, }, vM: { get() { return proto.WebMessageInfo.fromObject({ key: { fromMe: this.fromMe, remoteJid: this.chat, id: this.id, }, message: quoted, ...(self.isGroup ? {participant: this.sender} : {}), }); }, }, fakeObj: { get() { return this.vM; }, }, download: { value(saveToFile = false) { const mtype = this.mediaType; return self.conn?.downloadM(this.mediaMessage[mtype], mtype.replace(/message/i, ''), saveToFile); }, enumerable: true, configurable: true, }, reply: { /** * Reply to quoted message * @param {String|Object} text * @param {String|false} chatId * @param {Object} options */ value(text, chatId, options) { return self.conn?.reply(chatId ? chatId : this.chat, text, this.vM, options); }, enumerable: true, }, copy: { /** * Copy quoted message */ value() { const M = proto.WebMessageInfo; return smsg(conn, M.fromObject(M.toObject(this.vM))); }, enumerable: true, }, forward: { /** * Forward quoted message * @param {String} jid * @param {Boolean} forceForward */ value(jid, force = false, options) { return self.conn?.sendMessage(jid, { forward: this.vM, force, ...options, }, {...options}); }, enumerable: true, }, copyNForward: { /** * Exact Forward quoted message * @param {String} jid * @param {Boolean|Number} forceForward * @param {Object} options */ value(jid, forceForward = false, options) { return self.conn?.copyNForward(jid, this.vM, forceForward, options); }, enumerable: true, }, cMod: { /** * Modify quoted Message * @param {String} jid * @param {String} text * @param {String} sender * @param {Object} options */ value(jid, text = '', sender = this.sender, options = {}) { return self.conn?.cMod(jid, this.vM, text, sender, options) }, enumerable: true, }, delete: { /** * Delete quoted message */ value() { return self.conn?.sendMessage(this.chat, { delete: this.vM.key }) }, enumerable: true, }, //react react: { value(text) { return self.conn?.sendMessage(this.chat, { react: { text, key: this.vM.key } }) }, enumerable: true, } // }) }, enumerable: true }, _text: { value: null, writable: true, }, text: { get() { const msg = this.msg const text = (typeof msg === 'string' ? msg : msg?.text) || msg?.caption || msg?.contentText || '' return typeof this._text === 'string' ? this._text : '' || (typeof text === 'string' ? text : ( text?.selectedDisplayText || text?.hydratedTemplate?.hydratedContentText || text )) || '' }, set(str) { return this._text = str }, enumerable: true }, mentionedJid: { get() { return this.msg?.contextInfo?.mentionedJid?.length && this.msg.contextInfo.mentionedJid || [] }, enumerable: true }, name: { get() { return !nullish(this.pushName) && this.pushName || this.conn?.getName(this.sender) }, enumerable: true }, download: { value(saveToFile = false) { const mtype = this.mediaType return this.conn?.downloadM(this.mediaMessage[mtype], mtype.replace(/message/i, ''), saveToFile) }, enumerable: true, configurable: true }, reply: { value(text, chatId, options) { return this.conn?.reply(chatId ? chatId : this.chat, text, this, options) } }, copy: { value() { const M = proto.WebMessageInfo return smsg(this.conn, M.fromObject(M.toObject(this))) }, enumerable: true }, forward: { value(jid, force = false, options = {}) { return this.conn?.sendMessage(jid, { forward: this, force, ...options }, { ...options }) }, enumerable: true }, copyNForward: { value(jid, forceForward = false, options = {}) { return this.conn?.copyNForward(jid, this, forceForward, options) }, enumerable: true }, cMod: { value(jid, text = '', sender = this.sender, options = {}) { return this.conn?.cMod(jid, this, text, sender, options) }, enumerable: true }, getQuotedObj: { value() { if (!this.quoted.id) return null const q = proto.WebMessageInfo.fromObject(this.conn?.loadMessage(this.quoted.id) || this.quoted.vM) return smsg(this.conn, q) }, enumerable: true }, getQuotedMessage: { get() { return this.getQuotedObj } }, delete: { value() { return this.conn?.sendMessage(this.chat, { delete: this.key }) }, enumerable: true }, //react react: { value(text) { return this.conn?.sendMessage(this.chat, { react: { text, key: this.key } }) }, enumerable: true } // }) } export function logic(check, inp, out) { if (inp.length !== out.length) throw new Error('Input and Output must have same length'); for (const i in inp) if (util.isDeepStrictEqual(check, inp[i])) return out[i]; return null; } export function protoType() { Buffer.prototype.toArrayBuffer = function toArrayBufferV2() { const ab = new ArrayBuffer(this.length); const view = new Uint8Array(ab); for (let i = 0; i < this.length; ++i) { view[i] = this[i]; } return ab; }; /** * @return {ArrayBuffer} */ Buffer.prototype.toArrayBufferV2 = function toArrayBuffer() { return this.buffer.slice(this.byteOffset, this.byteOffset + this.byteLength); }; /** * @return {Buffer} */ ArrayBuffer.prototype.toBuffer = function toBuffer() { return Buffer.from(new Uint8Array(this)); }; // /** // * @returns {String} // */ // Buffer.prototype.toUtilFormat = ArrayBuffer.prototype.toUtilFormat = Object.prototype.toUtilFormat = Array.prototype.toUtilFormat = function toUtilFormat() { // return util.format(this) // } Uint8Array.prototype.getFileType = ArrayBuffer.prototype.getFileType = Buffer.prototype.getFileType = async function getFileType() { return await fileTypeFromBuffer(this); }; /** * @returns {Boolean} */ String.prototype.isNumber = Number.prototype.isNumber = isNumber; /** * * @return {String} */ String.prototype.capitalize = function capitalize() { return this.charAt(0).toUpperCase() + this.slice(1, this.length); }; /** * @return {String} */ String.prototype.capitalizeV2 = function capitalizeV2() { const str = this.split(' '); return str.map((v) => v.capitalize()).join(' '); }; String.prototype.decodeJid = function decodeJid() { if (/:\d+@/gi.test(this)) { const decode = jidDecode(this) || {}; return (decode.user && decode.server && decode.user + '@' + decode.server || this).trim(); } else return this.trim(); }; /** * number must be milliseconds * @return {string} */ Number.prototype.toTimeString = function toTimeString() { // const milliseconds = this % 1000 const seconds = Math.floor((this / 1000) % 60); const minutes = Math.floor((this / (60 * 1000)) % 60); const hours = Math.floor((this / (60 * 60 * 1000)) % 24); const days = Math.floor((this / (24 * 60 * 60 * 1000))); return ( (days ? `${days} day(s) ` : '') + (hours ? `${hours} hour(s) ` : '') + (minutes ? `${minutes} minute(s) ` : '') + (seconds ? `${seconds} second(s)` : '') ).trim(); }; Number.prototype.getRandom = String.prototype.getRandom = Array.prototype.getRandom = getRandom; } function isNumber() { const int = parseInt(this); return typeof int === 'number' && !isNaN(int); } function getRandom() { if (Array.isArray(this) || this instanceof String) return this[Math.floor(Math.random() * this.length)]; return Math.floor(Math.random() * this); } /** * ?? * @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator * @return {boolean} */ function nullish(args) { return !(args !== null && args !== undefined); }