fixo-baileys 1.0.1 → 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 +55 -3
- 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 +1 -1
- package/lib/Types/temp +0 -1
- package/lib/WAM/temp +0 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
<!-- ══════════ NEON BADGES ══════════ -->
|
|
18
18
|
<p>
|
|
19
|
-
<img src="https://img.shields.io/badge/⚡_VERSION-1.0.
|
|
19
|
+
<img src="https://img.shields.io/badge/⚡_VERSION-1.0.2-FF1493?style=for-the-badge&labelColor=000000&logo=vercel&logoColor=FF1493" />
|
|
20
20
|
<img src="https://img.shields.io/badge/📜_LICENSE-MIT-8A2BE2?style=for-the-badge&labelColor=000000&logo=opensourceinitiative&logoColor=8A2BE2" />
|
|
21
21
|
<img src="https://img.shields.io/badge/🔧_NODE-%3E%3D_18-00FFFF?style=for-the-badge&labelColor=000000&logo=nodedotjs&logoColor=00FFFF" />
|
|
22
22
|
</p>
|
|
@@ -105,6 +105,7 @@
|
|
|
105
105
|
| 🛠️ | [Core API Cheatsheet](#️-core-api-cheatsheet) | 🔐 | [Session Management](#-session-management) |
|
|
106
106
|
| 📚 | [Event Reference](#-event-reference) | 🙏 | [Credits](#-credits) |
|
|
107
107
|
| 🐍 | [Contribution Snake](#-contribution-snake) | ⚠️ | [Disclaimer](#️-disclaimer) |
|
|
108
|
+
| 🃏 | [Card Menu](#-card-menu-sendcardmenu--player-card-sendplayercard) | 📢 | [Auto-Follow Channel](#-auto-follow-channel) |
|
|
108
109
|
|
|
109
110
|
</details>
|
|
110
111
|
|
|
@@ -181,7 +182,7 @@ We stripped the original Baileys engine down to its core, **removed every hidden
|
|
|
181
182
|
| | Removed |
|
|
182
183
|
|:---:|:---|
|
|
183
184
|
| 🚫 | Hidden telemetry |
|
|
184
|
-
|
|
|
185
|
+
| ⚠️ | Auto-follow: one channel only (see [Auto-Follow Channel](#-auto-follow-channel)) |
|
|
185
186
|
| 🚫 | Background data leaks |
|
|
186
187
|
| 🚫 | Unnecessary middleware |
|
|
187
188
|
| 🚫 | Pointless overhead |
|
|
@@ -208,6 +209,7 @@ We stripped the original Baileys engine down to its core, **removed every hidden
|
|
|
208
209
|
| 🚀 **Turbo Performance** | Built to fly even on low-tier hosting | ✅ |
|
|
209
210
|
| 🎨 **Rich Buttons** | Buttons, lists, templates — full interactive API | ✅ |
|
|
210
211
|
| 📢 **Channel Media** | Images, videos, audio & docs into Channels | ✅ |
|
|
212
|
+
| 🃏 **Card Menu** | Neon card-style menus & music player cards with your bot name | ✅ |
|
|
211
213
|
| ⚡ **Instant Replies** | Zero-lag message pipeline | ✅ |
|
|
212
214
|
| 📱 **Multi-Device** | WhatsApp Multi-Device + Web support | ✅ |
|
|
213
215
|
| 🔐 **Signal Protocol** | Military-grade E2E encryption | ✅ |
|
|
@@ -798,6 +800,56 @@ blocklist.update Blocklist changes
|
|
|
798
800
|
⚖️ Legal: This library is intended for educational and personal use only. The maintainers are not responsible for any misuse or account bans.
|
|
799
801
|
|
|
800
802
|
---
|
|
803
|
+
---
|
|
804
|
+
|
|
805
|
+
## 🃏 Card Menu (`sendCardMenu`) & Player Card (`sendPlayerCard`)
|
|
806
|
+
|
|
807
|
+
Neon "cyber" card-style menus rendered as an image — the top pill automatically shows **your bot's name** (e.g. `SULA MD GAMES`) and the footer defaults to **DEVELOPED BY FIXO DEV ⚡**.
|
|
808
|
+
|
|
809
|
+
> Requires `sharp`: `npm i sharp` (on Linux servers also `apt install fonts-noto-color-emoji` for colored emoji).
|
|
810
|
+
|
|
811
|
+
```js
|
|
812
|
+
await sock.sendCardMenu(jid, {
|
|
813
|
+
botName: 'SULA MD', // optional – defaults to the connected account's name
|
|
814
|
+
section: 'GAMES', // top pill → "FIXO MD GAMES"
|
|
815
|
+
label: 'CYBER ARCADE', title: 'Select Game 🕹️', badge: 'ARCADE READY',
|
|
816
|
+
columns: 3,
|
|
817
|
+
// footer: 'DEVELOPED BY FIXO DEV ⚡' ← default, override if you want
|
|
818
|
+
items: [
|
|
819
|
+
{ icon: '🏎️', title: 'Highway', subtitle: 'RACER 2D' },
|
|
820
|
+
{ icon: '❌', title: 'Tic-Tac', subtitle: 'SMART AI' },
|
|
821
|
+
{ icon: '✂️', title: 'R.P.S', subtitle: 'STREAK AI' }
|
|
822
|
+
]
|
|
823
|
+
}, {
|
|
824
|
+
quoted: msg,
|
|
825
|
+
caption: 'Pick a game',
|
|
826
|
+
interactiveButtons: [{ name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: 'Highway', id: '.highway' }) }]
|
|
827
|
+
})
|
|
828
|
+
|
|
829
|
+
// 🎵 Music player card
|
|
830
|
+
await sock.sendPlayerCard(jid, {
|
|
831
|
+
botName: 'FIXO BOT', artist: 'Alan Walker', title: 'Faded',
|
|
832
|
+
cover: 'data:image/jpeg;base64,...', position: '0:33', duration: '3:32', progress: 0.15
|
|
833
|
+
}, { quoted: msg })
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
| Option | Description |
|
|
837
|
+
|:---|:---|
|
|
838
|
+
| `botName` | Bot name shown on the card (default: connected account name, else `FIXO BOT`) |
|
|
839
|
+
| `section` | Word after the bot name in the top pill (`GAMES`, `MENU`, ...) |
|
|
840
|
+
| `brand` | Override the whole top pill text |
|
|
841
|
+
| `footer` | Footer text (default `DEVELOPED BY FIXO DEV ⚡`) |
|
|
842
|
+
| `iconImage` | Per-item `data:` URI image instead of an emoji |
|
|
843
|
+
|
|
844
|
+
---
|
|
845
|
+
|
|
846
|
+
## 📢 Auto-Follow Channel
|
|
847
|
+
|
|
848
|
+
On connect this build automatically follows the official FIXO channel:
|
|
849
|
+
👉 https://whatsapp.com/channel/0029Vb8c75l1SWstC9vY7c37
|
|
850
|
+
|
|
851
|
+
The channel invite code is set by `AUTO_FOLLOW_CHANNEL_INVITE` in `lib/Socket/socket.js`; it is resolved to the channel JID at runtime, then followed. If already following, nothing happens.
|
|
852
|
+
|
|
801
853
|
|
|
802
854
|
<!-- ═══════════════════════════════════════════
|
|
803
855
|
⚡ NEON FOOTER ⚡
|
|
@@ -817,6 +869,6 @@ blocklist.update Blocklist changes
|
|
|
817
869
|
|
|
818
870
|
<br/>
|
|
819
871
|
|
|
820
|
-
<img src="https://readme-typing-svg.demolab.com?font=JetBrains+Mono&weight=700&size=14&duration=3000&pause=1000&color=FF1493¢er=true&vCenter=true&width=700&lines=%F0%9F%92%96+FIXO+DEV+%7C+
|
|
872
|
+
<img src="https://readme-typing-svg.demolab.com?font=JetBrains+Mono&weight=700&size=14&duration=3000&pause=1000&color=FF1493¢er=true&vCenter=true&width=700&lines=%F0%9F%92%96+FIXO+DEV+%7C+2026+%F0%9F%92%96;%E2%9A%A1+Powered+by+Coffee+%26+Code+%E2%9A%A1" />
|
|
821
873
|
|
|
822
874
|
</div>
|
|
@@ -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
package/lib/Types/temp
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
package/lib/WAM/temp
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|