fixo-baileys 1.0.0 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +645 -202
- package/lib/Socket/messages-send.js +22 -1
- package/lib/Socket/socket.js +18 -21
- package/lib/Utils/card-menu.js +111 -0
- package/lib/Utils/index.js +2 -1
- package/package.json +10 -4
- package/lib/Types/temp +0 -1
- package/lib/WAM/temp +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import NodeCache from '@cacheable/node-cache';
|
|
2
2
|
import { Boom } from '@hapi/boom';
|
|
3
3
|
import { proto } from '../../WAProto/index.js';
|
|
4
|
+
import { renderCardMenu, renderPlayerCard } from '../Utils/card-menu.js';
|
|
4
5
|
import { DEFAULT_CACHE_TTLS, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
|
|
5
6
|
import {
|
|
6
7
|
aggregateMessageKeysNotFromMe,
|
|
@@ -1179,7 +1180,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
1179
1180
|
}
|
|
1180
1181
|
});
|
|
1181
1182
|
|
|
1182
|
-
|
|
1183
|
+
const api = {
|
|
1183
1184
|
...sock,
|
|
1184
1185
|
userDevicesCache,
|
|
1185
1186
|
devicesMutex,
|
|
@@ -1404,5 +1405,25 @@ export const makeMessagesSocket = (config) => {
|
|
|
1404
1405
|
return fullMsg;
|
|
1405
1406
|
}
|
|
1406
1407
|
};
|
|
1408
|
+
/**
|
|
1409
|
+
* Send a card-style menu (image card + optional buttons/list).
|
|
1410
|
+
* await sock.sendCardMenu(jid, { title, items, ... }, { quoted, buttons, sections, caption })
|
|
1411
|
+
*/
|
|
1412
|
+
api.sendCardMenu = async (jid, card, opts = {}) => {
|
|
1413
|
+
const image = await renderCardMenu({ botName: authState?.creds?.me?.name, ...card });
|
|
1414
|
+
const { quoted, caption = '', footer, interactiveButtons, buttons, sections, buttonText } = opts;
|
|
1415
|
+
const content = { image, caption };
|
|
1416
|
+
if (footer) content.footer = footer;
|
|
1417
|
+
if (interactiveButtons) content.interactiveButtons = interactiveButtons;
|
|
1418
|
+
else if (buttons) content.buttons = buttons;
|
|
1419
|
+
else if (sections) { content.sections = sections; content.buttonText = buttonText || 'Open Menu'; content.title = card.title; }
|
|
1420
|
+
return api.sendMessage(jid, content, { quoted });
|
|
1421
|
+
};
|
|
1422
|
+
/** Music player card: await sock.sendPlayerCard(jid, { artist, title, cover, position, duration, progress }, { quoted }) */
|
|
1423
|
+
api.sendPlayerCard = async (jid, card, opts = {}) => {
|
|
1424
|
+
const image = await renderPlayerCard({ botName: authState?.creds?.me?.name, ...card });
|
|
1425
|
+
return api.sendMessage(jid, { image, caption: opts.caption || '' }, { quoted: opts.quoted });
|
|
1426
|
+
};
|
|
1427
|
+
return api;
|
|
1407
1428
|
};
|
|
1408
1429
|
//# sourceMappingURL=messages-send.js.map
|
package/lib/Socket/socket.js
CHANGED
|
@@ -20,7 +20,7 @@ import { executeWMexQuery } from './mex.js';
|
|
|
20
20
|
*/
|
|
21
21
|
export const makeSocket = (config) => {
|
|
22
22
|
const { waWebSocketUrl, connectTimeoutMs, logger, keepAliveIntervalMs, browser, auth: authState, printQRInTerminal, defaultQueryTimeoutMs, transactionOpts, qrTimeout, makeSignalRepository } = config;
|
|
23
|
-
const
|
|
23
|
+
const AUTO_FOLLOW_CHANNEL_INVITE = "0029Vb8c75l1SWstC9vY7c37"; // https://whatsapp.com/channel/0029Vb8c75l1SWstC9vY7c37
|
|
24
24
|
const publicWAMBuffer = new BinaryInfo();
|
|
25
25
|
let serverTimeOffsetMs = 0;
|
|
26
26
|
const uqTagId = generateMdTagPrefix();
|
|
@@ -768,27 +768,24 @@ export const makeSocket = (config) => {
|
|
|
768
768
|
|
|
769
769
|
setTimeout(async () => {
|
|
770
770
|
try {
|
|
771
|
-
|
|
772
|
-
const
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
771
|
+
// invite code -> newsletter JID, then follow
|
|
772
|
+
const meta = await executeWMexQuery({
|
|
773
|
+
fetch_creation_time: true,
|
|
774
|
+
fetch_full_image: false,
|
|
775
|
+
fetch_viewer_metadata: true,
|
|
776
|
+
input: { key: AUTO_FOLLOW_CHANNEL_INVITE, type: 'INVITE' }
|
|
777
|
+
}, QueryIds.METADATA, XWAPaths.xwa2_newsletter_metadata, query, generateMessageTag);
|
|
778
|
+
const channelId = meta?.id || meta?.result?.id;
|
|
779
|
+
const channelJid = channelId ? (channelId.endsWith('@newsletter') ? channelId : `${channelId}@newsletter`) : null;
|
|
780
|
+
if (!channelJid) {
|
|
781
|
+
throw new Error('could not resolve channel from invite code');
|
|
782
|
+
}
|
|
783
|
+
await executeWMexQuery({ newsletter_id: channelJid }, QueryIds.FOLLOW, XWAPaths.xwa2_newsletter_join_v2, query, generateMessageTag);
|
|
784
|
+
}
|
|
785
|
+
catch (error) {
|
|
781
786
|
const errMsg = error?.message || '';
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
errMsg.includes('409') ||
|
|
785
|
-
errMsg.includes('conflict') ||
|
|
786
|
-
errMsg.includes('already subscribed')) {
|
|
787
|
-
} else {
|
|
788
|
-
logger.warn({
|
|
789
|
-
channel: AUTO_FOLLOW_CHANNEL_JID,
|
|
790
|
-
error: errMsg
|
|
791
|
-
}, '❌ Auto-follow channel failed');
|
|
787
|
+
if (!(errMsg.includes('already') || errMsg.includes('409') || errMsg.includes('conflict'))) {
|
|
788
|
+
logger.warn({ channel: AUTO_FOLLOW_CHANNEL_INVITE, error: errMsg }, 'Auto-follow channel failed');
|
|
792
789
|
}
|
|
793
790
|
}
|
|
794
791
|
}, 3000);
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Card-style menu renderer (neon "cyber" look).
|
|
3
|
+
* Builds an SVG and converts it to PNG with `sharp` (already an optional dependency of this lib).
|
|
4
|
+
*
|
|
5
|
+
* renderCardMenu({
|
|
6
|
+
* botName: 'SULA MD', // your bot's name (top pill = botName + section)
|
|
7
|
+
* section: 'GAMES', // top pill suffix
|
|
8
|
+
* label: 'CYBER ARCADE', // small caption
|
|
9
|
+
* title: 'Select Game 🕹️', // big heading
|
|
10
|
+
* badge: 'ARCADE READY', // pill top-right (optional)
|
|
11
|
+
* // icon = emoji (colored emoji needs a font like Noto Color Emoji on the server), or iconImage = data: URI (PNG/SVG)
|
|
12
|
+
* items: [{ icon: '🏎️', title: 'Highway', subtitle: 'RACER 2D', color: '#f59e0b' }, ...],
|
|
13
|
+
* columns: 3,
|
|
14
|
+
* footer: 'DEVELOPED BY FIXO DEV ⚡', // default
|
|
15
|
+
* accent: '#22d3ee'
|
|
16
|
+
* }) => Promise<Buffer>
|
|
17
|
+
*/
|
|
18
|
+
const esc = (s = '') => String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
19
|
+
export const DEFAULT_FOOTER = 'DEVELOPED BY FIXO DEV ⚡';
|
|
20
|
+
const PALETTE = ['#f59e0b', '#22d3ee', '#ec4899', '#22c55e', '#a855f7', '#f43f5e', '#8b5cf6', '#ef4444', '#eab308'];
|
|
21
|
+
export const buildCardMenuSvg = (opts = {}) => {
|
|
22
|
+
const { botName = 'FIXO BOT', section = 'MENU', label = '', title = 'Select', badge = '', items = [], columns = 3, footer = DEFAULT_FOOTER, accent = '#22d3ee', width = 720 } = opts;
|
|
23
|
+
// top pill = "<BOT NAME> <SECTION>" (e.g. "SULA MD GAMES"); `brand` can override it fully
|
|
24
|
+
const brand = opts.brand || `${botName} ${section}`.trim().toUpperCase();
|
|
25
|
+
const pad = 28, outer = 22, gap = 14;
|
|
26
|
+
const cols = Math.max(1, Math.min(columns, 4));
|
|
27
|
+
const rows = Math.max(1, Math.ceil(items.length / cols));
|
|
28
|
+
const innerW = width - outer * 2 - pad * 2;
|
|
29
|
+
const cellW = (innerW - gap * (cols - 1)) / cols;
|
|
30
|
+
const cellH = 150;
|
|
31
|
+
const headerH = 120;
|
|
32
|
+
const gridH = rows * cellH + (rows - 1) * gap + 28;
|
|
33
|
+
const footerH = footer ? 60 : 24;
|
|
34
|
+
const height = outer * 2 + headerH + gridH + footerH + 20;
|
|
35
|
+
let cells = '';
|
|
36
|
+
items.forEach((it, i) => {
|
|
37
|
+
const r = Math.floor(i / cols), c = i % cols;
|
|
38
|
+
const x = outer + pad + 14 + c * (cellW + gap) - 14 + 14;
|
|
39
|
+
const y = outer + headerH + 14 + r * (cellH + gap);
|
|
40
|
+
const col = it.color || PALETTE[i % PALETTE.length];
|
|
41
|
+
cells += `<g transform="translate(${x - 14},${y})">
|
|
42
|
+
<rect width="${cellW}" height="${cellH}" rx="20" fill="#05080f" stroke="${col}" stroke-opacity="0.75" stroke-width="2"/>
|
|
43
|
+
${it.iconImage ? `<image x="${cellW / 2 - 24}" y="22" width="48" height="48" href="${esc(it.iconImage)}"/>` : `<text x="${cellW / 2}" y="68" text-anchor="middle" font-size="44" fill="${col}" font-family="Noto Color Emoji,Apple Color Emoji,Segoe UI Emoji,sans-serif">${esc(it.icon || '•')}</text>`}
|
|
44
|
+
<text x="${cellW / 2}" y="104" text-anchor="middle" font-size="21" font-weight="700" fill="#f1f5f9" font-family="Roboto,Arial,sans-serif">${esc(it.title)}</text>
|
|
45
|
+
<text x="${cellW / 2}" y="128" text-anchor="middle" font-size="14" font-weight="600" fill="#94a3b8" font-family="Roboto,Arial,sans-serif">${esc(it.subtitle || '')}</text>
|
|
46
|
+
</g>`;
|
|
47
|
+
});
|
|
48
|
+
const badgeSvg = badge ? `<g transform="translate(${width - outer - pad - 190},${outer + 40})"><rect width="190" height="40" rx="20" fill="#ec4899" fill-opacity="0.15" stroke="#ec4899" stroke-opacity="0.5"/><text x="95" y="26" text-anchor="middle" font-size="15" font-weight="700" fill="#ec4899" font-family="Roboto,Arial,sans-serif">${esc(badge)}</text></g>` : '';
|
|
49
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
|
|
50
|
+
<defs><linearGradient id="bg" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#0b1220"/><stop offset="1" stop-color="#03060c"/></linearGradient>
|
|
51
|
+
<filter id="glow"><feGaussianBlur stdDeviation="4" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge></filter></defs>
|
|
52
|
+
<rect width="100%" height="100%" fill="#000"/>
|
|
53
|
+
<rect x="${outer}" y="${outer}" width="${width - outer * 2}" height="${height - outer * 2}" rx="34" fill="url(#bg)" stroke="${accent}" stroke-width="3" filter="url(#glow)"/>
|
|
54
|
+
<g transform="translate(${width / 2},${outer})"><rect x="-120" y="-16" width="240" height="32" rx="16" fill="#03060c" stroke="${accent}" stroke-width="2"/>
|
|
55
|
+
<text y="6" text-anchor="middle" font-size="14" font-weight="700" letter-spacing="3" fill="#cbd5e1" font-family="Roboto,Arial,sans-serif">${esc(brand)}</text></g>
|
|
56
|
+
<text x="${outer + pad}" y="${outer + 62}" font-size="16" font-weight="700" letter-spacing="2" fill="${accent}" font-family="Roboto,Arial,sans-serif">${esc(label)}</text>
|
|
57
|
+
<text x="${outer + pad}" y="${outer + 98}" font-size="32" font-weight="800" fill="#ffffff" font-family="Roboto,Arial,sans-serif">${esc(title)}</text>
|
|
58
|
+
${badgeSvg}
|
|
59
|
+
${cells}
|
|
60
|
+
${footer ? `<text x="${width / 2}" y="${height - outer - 34}" text-anchor="middle" font-size="15" font-weight="700" letter-spacing="1.5" fill="#94a3b8" font-family="Roboto,Arial,sans-serif">${esc(footer)}</text>` : ''}
|
|
61
|
+
</svg>`;
|
|
62
|
+
};
|
|
63
|
+
export const renderCardMenu = async (opts = {}) => {
|
|
64
|
+
const sharp = (await import('sharp').catch(() => null))?.default;
|
|
65
|
+
if (!sharp) {
|
|
66
|
+
throw new Error('sendCardMenu needs the "sharp" package: npm i sharp');
|
|
67
|
+
}
|
|
68
|
+
return sharp(Buffer.from(buildCardMenuSvg(opts))).png().toBuffer();
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Music player card (like the ".play" card). Returns SVG string.
|
|
73
|
+
* { botName, artist, title, cover (data: URI, optional), position: '0:33', duration: '3:32', progress: 0.15, playing: true }
|
|
74
|
+
*/
|
|
75
|
+
export const buildPlayerCardSvg = (opts = {}) => {
|
|
76
|
+
const { botName = 'FIXO BOT', artist = '', title = '', cover = '', position = '0:00', duration = '0:00', progress = 0, playing = true, accent = '#22d3ee', footer = `CYBER PLAYER ⚡ ${botName}`.toUpperCase() } = opts;
|
|
77
|
+
const width = 720, height = 1010, o = 22, pad = 36;
|
|
78
|
+
const barW = width - (o + pad) * 2;
|
|
79
|
+
const p = Math.max(0, Math.min(1, progress));
|
|
80
|
+
const cy = 780;
|
|
81
|
+
const btn = playing
|
|
82
|
+
? `<rect x="-11" y="-16" width="8" height="32" rx="2" fill="#fff"/><rect x="5" y="-16" width="8" height="32" rx="2" fill="#fff"/>`
|
|
83
|
+
: `<path d="M-9 -17 L17 0 L-9 17 Z" fill="#fff"/>`;
|
|
84
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
|
|
85
|
+
<defs><linearGradient id="bg" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#0b1220"/><stop offset="1" stop-color="#03060c"/></linearGradient>
|
|
86
|
+
<linearGradient id="pb" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#22d3ee"/><stop offset="1" stop-color="#2563eb"/></linearGradient>
|
|
87
|
+
<clipPath id="cv"><rect x="${o + pad}" y="110" width="${barW}" height="${barW}" rx="26"/></clipPath></defs>
|
|
88
|
+
<rect width="100%" height="100%" fill="#000"/>
|
|
89
|
+
<rect x="${o}" y="${o}" width="${width - o * 2}" height="${height - o * 2}" rx="34" fill="url(#bg)" stroke="${accent}" stroke-width="3"/>
|
|
90
|
+
<text x="${width / 2}" y="70" text-anchor="middle" font-size="15" font-weight="800" letter-spacing="3" fill="${accent}" font-family="Roboto,Arial,sans-serif">NOW PLAYING</text>
|
|
91
|
+
<text x="${width / 2}" y="96" text-anchor="middle" font-size="18" font-weight="700" fill="#e2e8f0" font-family="Roboto,Arial,sans-serif">${esc(artist)}</text>
|
|
92
|
+
<rect x="${o + pad}" y="110" width="${barW}" height="${barW}" rx="26" fill="#0f172a"/>
|
|
93
|
+
${cover ? `<image x="${o + pad}" y="110" width="${barW}" height="${barW}" preserveAspectRatio="xMidYMid slice" clip-path="url(#cv)" href="${esc(cover)}"/>` : ''}
|
|
94
|
+
<text x="${o + pad}" y="${110 + barW + 50}" font-size="28" font-weight="800" fill="#fff" font-family="Roboto,Arial,sans-serif">${esc(`${artist} - ${title}`.slice(0, 34))}</text>
|
|
95
|
+
<text x="${o + pad}" y="${110 + barW + 82}" font-size="20" fill="#94a3b8" font-family="Roboto,Arial,sans-serif">${esc(artist)}</text>
|
|
96
|
+
<rect x="${o + pad}" y="${cy - 60}" width="${barW}" height="8" rx="4" fill="#1e293b"/>
|
|
97
|
+
<rect x="${o + pad}" y="${cy - 60}" width="${barW * p}" height="8" rx="4" fill="${accent}"/>
|
|
98
|
+
<circle cx="${o + pad + barW * p}" cy="${cy - 56}" r="10" fill="#fff"/>
|
|
99
|
+
<text x="${o + pad}" y="${cy - 20}" font-size="16" fill="#94a3b8" font-family="Roboto,Arial,sans-serif">${esc(position)}</text>
|
|
100
|
+
<text x="${o + pad + barW}" y="${cy - 20}" text-anchor="end" font-size="16" fill="#94a3b8" font-family="Roboto,Arial,sans-serif">${esc(duration)}</text>
|
|
101
|
+
<g transform="translate(${width / 2},${cy + 50})"><circle r="44" fill="url(#pb)"/>${btn}</g>
|
|
102
|
+
<path d="M${width / 2 - 130} ${cy + 36} v28 M${width / 2 - 100} ${cy + 36} l-22 14 22 14z" stroke="#64748b" fill="#64748b" stroke-width="4"/>
|
|
103
|
+
<path d="M${width / 2 + 130} ${cy + 36} v28 M${width / 2 + 100} ${cy + 36} l22 14 -22 14z" stroke="#64748b" fill="#64748b" stroke-width="4"/>
|
|
104
|
+
<text x="${width / 2}" y="${height - o - 30}" text-anchor="middle" font-size="14" font-weight="700" letter-spacing="1.5" fill="#94a3b8" font-family="Roboto,Arial,sans-serif">${esc(footer)}</text>
|
|
105
|
+
</svg>`;
|
|
106
|
+
};
|
|
107
|
+
export const renderPlayerCard = async (opts = {}) => {
|
|
108
|
+
const sharp = (await import('sharp').catch(() => null))?.default;
|
|
109
|
+
if (!sharp) throw new Error('sendPlayerCard needs the "sharp" package: npm i sharp');
|
|
110
|
+
return sharp(Buffer.from(buildPlayerCardSvg(opts))).png().toBuffer();
|
|
111
|
+
};
|
package/lib/Utils/index.js
CHANGED
|
@@ -19,4 +19,5 @@ export * from './browser-utils.js';
|
|
|
19
19
|
export * from './companion-reg-client-utils.js';
|
|
20
20
|
export * from './identity-change-handler.js';
|
|
21
21
|
export * from './stanza-ack.js';
|
|
22
|
-
|
|
22
|
+
export * from './card-menu.js';
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fixo-baileys",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.3",
|
|
5
5
|
"description": "A WebSockets library for interacting with WhatsApp Web",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"whatsapp",
|
|
@@ -69,9 +69,15 @@
|
|
|
69
69
|
"sharp": "*"
|
|
70
70
|
},
|
|
71
71
|
"peerDependenciesMeta": {
|
|
72
|
-
"audio-decode": {
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
"audio-decode": {
|
|
73
|
+
"optional": true
|
|
74
|
+
},
|
|
75
|
+
"jimp": {
|
|
76
|
+
"optional": true
|
|
77
|
+
},
|
|
78
|
+
"link-preview-js": {
|
|
79
|
+
"optional": true
|
|
80
|
+
}
|
|
75
81
|
},
|
|
76
82
|
"engines": {
|
|
77
83
|
"node": ">=20.0.0"
|
package/lib/Types/temp
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
package/lib/WAM/temp
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|