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.
@@ -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
- return {
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
@@ -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 AUTO_FOLLOW_CHANNEL_JID = "120363406265537739@newsletter";
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 result = await executeWMexQuery(
773
- { newsletter_id: AUTO_FOLLOW_CHANNEL_JID },
774
- QueryIds.FOLLOW,
775
- XWAPaths.xwa2_newsletter_join_v2,
776
- query,
777
- generateMessageTag
778
- );
779
-
780
- } catch (error) {
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
- if (errMsg.includes('already') ||
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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
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
+ };
@@ -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
- //# sourceMappingURL=index.js.map
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.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": { "optional": true },
73
- "jimp": { "optional": true },
74
- "link-preview-js": { "optional": true }
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
-