| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143 |
- 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<fs.PathLike | fs.promises.FileHandle | Buffer>}
- */
- 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<String>}
- */
- 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<typeof makeWASocket>} 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<typeof makeWASocket>}
- */
- 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);
- }
|