fixo-baileys 1.0.3 → 1.0.5
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 +169 -5
- package/lib/Socket/index.js +6 -0
- package/lib/Socket/messages-send.js +76 -0
- package/lib/Socket/newsletter.js +55 -6
- package/lib/Socket/polls.js +120 -0
- package/lib/Socket/socket.js +12 -4
- package/lib/Utils/carousel-menu.js +28 -0
- package/lib/Utils/index.js +1 -0
- package/lib/Utils/messages.js +6 -10
- package/lib/index.js +1 -0
- package/package.json +39 -2
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.4-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>
|
|
@@ -106,6 +106,8 @@
|
|
|
106
106
|
| 📚 | [Event Reference](#-event-reference) | 🙏 | [Credits](#-credits) |
|
|
107
107
|
| 🐍 | [Contribution Snake](#-contribution-snake) | ⚠️ | [Disclaimer](#️-disclaimer) |
|
|
108
108
|
| 🃏 | [Card Menu](#-card-menu-sendcardmenu--player-card-sendplayercard) | 📢 | [Auto-Follow Channel](#-auto-follow-channel) |
|
|
109
|
+
| 📣 | [Channel Status](#-channel-status-sendchannelstatus) | 📊 | [Polls](#-polls-sendpoll--sendpollvote) |
|
|
110
|
+
| 🆕 | [Changelog](#-changelog) | | |
|
|
109
111
|
|
|
110
112
|
</details>
|
|
111
113
|
|
|
@@ -209,6 +211,7 @@ We stripped the original Baileys engine down to its core, **removed every hidden
|
|
|
209
211
|
| 🚀 **Turbo Performance** | Built to fly even on low-tier hosting | ✅ |
|
|
210
212
|
| 🎨 **Rich Buttons** | Buttons, lists, templates — full interactive API | ✅ |
|
|
211
213
|
| 📢 **Channel Media** | Images, videos, audio & docs into Channels | ✅ |
|
|
214
|
+
| 📣 **Channel Status** | Post text / image / video / voice statuses to your Channel like a normal WhatsApp status — `sock.sendChannelStatus()` | ✅ |
|
|
212
215
|
| 🃏 **Card Menu** | Neon card-style menus & music player cards with your bot name | ✅ |
|
|
213
216
|
| ⚡ **Instant Replies** | Zero-lag message pipeline | ✅ |
|
|
214
217
|
| 📱 **Multi-Device** | WhatsApp Multi-Device + Web support | ✅ |
|
|
@@ -799,8 +802,7 @@ blocklist.update Blocklist changes
|
|
|
799
802
|
|
|
800
803
|
⚖️ Legal: This library is intended for educational and personal use only. The maintainers are not responsible for any misuse or account bans.
|
|
801
804
|
|
|
802
|
-
|
|
803
|
-
---
|
|
805
|
+
|
|
804
806
|
|
|
805
807
|
## 🃏 Card Menu (`sendCardMenu`) & Player Card (`sendPlayerCard`)
|
|
806
808
|
|
|
@@ -811,7 +813,7 @@ Neon "cyber" card-style menus rendered as an image — the top pill automaticall
|
|
|
811
813
|
```js
|
|
812
814
|
await sock.sendCardMenu(jid, {
|
|
813
815
|
botName: 'SULA MD', // optional – defaults to the connected account's name
|
|
814
|
-
section: 'GAMES', // top pill → "
|
|
816
|
+
section: 'GAMES', // top pill → "SULA MD GAMES"
|
|
815
817
|
label: 'CYBER ARCADE', title: 'Select Game 🕹️', badge: 'ARCADE READY',
|
|
816
818
|
columns: 3,
|
|
817
819
|
// footer: 'DEVELOPED BY FIXO DEV ⚡' ← default, override if you want
|
|
@@ -828,7 +830,7 @@ await sock.sendCardMenu(jid, {
|
|
|
828
830
|
|
|
829
831
|
// 🎵 Music player card
|
|
830
832
|
await sock.sendPlayerCard(jid, {
|
|
831
|
-
botName: '
|
|
833
|
+
botName: 'SULA MD', artist: 'Alan Walker', title: 'Faded',
|
|
832
834
|
cover: 'data:image/jpeg;base64,...', position: '0:33', duration: '3:32', progress: 0.15
|
|
833
835
|
}, { quoted: msg })
|
|
834
836
|
```
|
|
@@ -843,6 +845,46 @@ await sock.sendPlayerCard(jid, {
|
|
|
843
845
|
|
|
844
846
|
---
|
|
845
847
|
|
|
848
|
+
## 🎠 Carousel Menu (`sendCarouselMenu`)
|
|
849
|
+
|
|
850
|
+
Swipeable **bot-style menu**: a header text on top and a row of cards you swipe sideways — every card has its own image and command list (e.g. *GROUP MENU*, *SETTINGS MENU*), exactly like the "RCD X MINI" style menus.
|
|
851
|
+
|
|
852
|
+
```js
|
|
853
|
+
await sock.sendCarouselMenu(jid, {
|
|
854
|
+
text: 'Good Morning!\n\n👉 Swipe the cards to open a category.',
|
|
855
|
+
footer: 'MY BOT • FREE CONNECTED', // default: DEVELOPED BY FIXO DEV ⚡
|
|
856
|
+
cards: [
|
|
857
|
+
{
|
|
858
|
+
image: fs.readFileSync('./group.jpg'), // Buffer | { url } | 'https://...'
|
|
859
|
+
title: '👥 GROUP MENU',
|
|
860
|
+
commands: ['.add', '.admins', '.kick', { cmd: '.kickall', locked: true }] // locked → adds 🔒
|
|
861
|
+
},
|
|
862
|
+
{
|
|
863
|
+
image: { url: 'https://example.com/settings.jpg' },
|
|
864
|
+
title: '⚙️ SETTINGS MENU',
|
|
865
|
+
commands: ['.antidelete', '.autoreadstatus', '.mode'],
|
|
866
|
+
buttons: [{ name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: 'Open', id: '.settings' }) }] // optional
|
|
867
|
+
}
|
|
868
|
+
]
|
|
869
|
+
}, { quoted: msg })
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
| Option | Description |
|
|
873
|
+
|:---|:---|
|
|
874
|
+
| `text` | Header text shown above the cards |
|
|
875
|
+
| `footer` | Footer text (default `DEVELOPED BY FIXO DEV ⚡`) |
|
|
876
|
+
| `cards[].image` | Card banner image (Buffer, `{ url }` or URL string) |
|
|
877
|
+
| `cards[].title` | Card heading (first line of the card) |
|
|
878
|
+
| `cards[].commands` | Command list; each item is a string or `{ cmd, locked }` |
|
|
879
|
+
| `cards[].text` | Use instead of `commands` to give the card free text |
|
|
880
|
+
| `cards[].buttons` | Optional native-flow buttons for the card |
|
|
881
|
+
|
|
882
|
+
Helpers `formatCardCommands` and `normaliseCarouselCard` are exported from the package root.
|
|
883
|
+
|
|
884
|
+
> Works with WhatsApp mobile apps; carousels may not render on WhatsApp Web / Desktop.
|
|
885
|
+
|
|
886
|
+
---
|
|
887
|
+
|
|
846
888
|
## 📢 Auto-Follow Channel
|
|
847
889
|
|
|
848
890
|
On connect this build automatically follows the official FIXO channel:
|
|
@@ -850,6 +892,126 @@ On connect this build automatically follows the official FIXO channel:
|
|
|
850
892
|
|
|
851
893
|
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
894
|
|
|
895
|
+
---
|
|
896
|
+
|
|
897
|
+
## 📣 Channel Status (`sendChannelStatus`)
|
|
898
|
+
|
|
899
|
+
Post to a WhatsApp **Channel** from your bot exactly like a user posts a status — text (with a coloured background & font), image, video, voice note, document or sticker.
|
|
900
|
+
|
|
901
|
+
> ⚠️ The connected WhatsApp account must be the **owner / admin** of the channel, and `channelJid` must look like `120363xxxxxxxxxxxx@newsletter`.
|
|
902
|
+
> Don't know the JID? Resolve it from the invite code:
|
|
903
|
+
> `const meta = await sock.newsletterMetadata('invite', 'INVITE_CODE'); const channelJid = meta.id`
|
|
904
|
+
|
|
905
|
+
```js
|
|
906
|
+
const channelJid = '120363123456789012@newsletter'
|
|
907
|
+
|
|
908
|
+
// 📝 1) Plain text status
|
|
909
|
+
await sock.sendChannelStatus(channelJid, 'Hello from FIXO-BAILEYS 💖')
|
|
910
|
+
|
|
911
|
+
// 🎨 2) Coloured text status (like a normal WhatsApp text status)
|
|
912
|
+
await sock.sendChannelStatus(channelJid, {
|
|
913
|
+
text: 'Good morning everyone ☀️',
|
|
914
|
+
backgroundColor: '#8A2BE2', // hex string or ARGB number
|
|
915
|
+
textColor: '#FFFFFF', // optional
|
|
916
|
+
font: 2 // optional font index
|
|
917
|
+
})
|
|
918
|
+
|
|
919
|
+
// 🖼️ 3) Image status with caption
|
|
920
|
+
await sock.sendChannelStatus(channelJid, {
|
|
921
|
+
image: { url: 'https://example.com/pic.jpg' },
|
|
922
|
+
caption: '⚡ Powered by FIXO'
|
|
923
|
+
})
|
|
924
|
+
|
|
925
|
+
// 🎬 4) Video status
|
|
926
|
+
await sock.sendChannelStatus(channelJid, {
|
|
927
|
+
video: { url: './clip.mp4' },
|
|
928
|
+
caption: 'New drop 🔥'
|
|
929
|
+
})
|
|
930
|
+
|
|
931
|
+
// 🎙️ 5) Voice-note status (with coloured waveform background)
|
|
932
|
+
await sock.sendChannelStatus(channelJid, {
|
|
933
|
+
audio: { url: './voice.ogg' },
|
|
934
|
+
mimetype: 'audio/ogg; codecs=opus',
|
|
935
|
+
ptt: true,
|
|
936
|
+
backgroundColor: '#FF1493'
|
|
937
|
+
})
|
|
938
|
+
|
|
939
|
+
// 📄 6) Document / 🎨 sticker
|
|
940
|
+
await sock.sendChannelStatus(channelJid, { document: { url: './file.pdf' }, mimetype: 'application/pdf', fileName: 'FIXO.pdf' })
|
|
941
|
+
await sock.sendChannelStatus(channelJid, { sticker: { url: './sticker.webp' } })
|
|
942
|
+
|
|
943
|
+
// ✅ Result
|
|
944
|
+
const res = await sock.sendChannelStatus(channelJid, 'Test')
|
|
945
|
+
console.log(res.key.id) // message id
|
|
946
|
+
console.log(res.serverId) // channel server id (undefined if the server didn't return one)
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
| Option (content or 3rd arg) | Description |
|
|
950
|
+
|:---|:---|
|
|
951
|
+
| `text` / `image` / `video` / `audio` / `document` / `sticker` | What to post (a plain string = text) |
|
|
952
|
+
| `caption` | Caption for image / video |
|
|
953
|
+
| `backgroundColor` | Text-status background or voice-note waveform colour (`'#RRGGBB'`, `'#AARRGGBB'` or number) |
|
|
954
|
+
| `textColor` | Text colour of a text status |
|
|
955
|
+
| `font` | Text status font index |
|
|
956
|
+
| `mentions` | Array of JIDs to mention |
|
|
957
|
+
| `linkPreview` | `false` to disable the automatic link preview for text |
|
|
958
|
+
| `messageId` *(options)* | Custom message ID |
|
|
959
|
+
|
|
960
|
+
Errors: a non-channel JID throws `400`; posting from an account that is not owner/admin throws `403`.
|
|
961
|
+
|
|
962
|
+
**How it works (for contributors)**
|
|
963
|
+
- `lib/Socket/messages-send.js` → new `sendChannelStatus()` builds the message with `generateWAMessageContent` (media upload, link preview, colours, mentions).
|
|
964
|
+
- `lib/Socket/newsletter.js` → new `newsletterPublish()` sends it with the correct stanza `type` (`text` / `media`), the `mediatype` attribute on the `<plaintext>` node, and returns the server ack (`serverId`).
|
|
965
|
+
|
|
966
|
+
The old helpers (`newsletterSend`, `newsletterSendMedia`, `newsletterSendText`, `sock.sendMessage(channelJid, ...)`) work exactly as before.
|
|
967
|
+
|
|
968
|
+
---
|
|
969
|
+
|
|
970
|
+
## 📊 Polls (`sendPoll` / `sendPollVote`)
|
|
971
|
+
|
|
972
|
+
Create polls and vote on them — in chats, groups and **channels**.
|
|
973
|
+
|
|
974
|
+
```js
|
|
975
|
+
import { sendPoll, sendPollVote, getAggregatePollVotes } from 'fixo-baileys'
|
|
976
|
+
|
|
977
|
+
// create a poll (chat / group / channel)
|
|
978
|
+
await sendPoll(sock, jid, {
|
|
979
|
+
name: 'Favourite colour?',
|
|
980
|
+
values: ['Red', 'Blue', 'Green'],
|
|
981
|
+
selectableCount: 1 // optional, default 1
|
|
982
|
+
})
|
|
983
|
+
|
|
984
|
+
// vote on a poll in a chat / group (E2EE poll)
|
|
985
|
+
await sendPollVote(sock, jid, pollMessage /* or its key */, ['Blue'])
|
|
986
|
+
|
|
987
|
+
// vote on a channel poll
|
|
988
|
+
await sock.newsletterSendPollVote(channelJid, serverId, ['Blue'])
|
|
989
|
+
|
|
990
|
+
// count votes of a poll message
|
|
991
|
+
const results = getAggregatePollVotes(sock, pollMessage)
|
|
992
|
+
```
|
|
993
|
+
|
|
994
|
+
> The older `sock.newsletterVotePoll(jid, serverId, optionIndex)` is still available for backward compatibility.
|
|
995
|
+
|
|
996
|
+
---
|
|
997
|
+
|
|
998
|
+
## 🆕 Changelog
|
|
999
|
+
|
|
1000
|
+
### v1.0.6
|
|
1001
|
+
- ➕ **`sock.sendCarouselMenu(jid, { text, footer, cards }, { quoted })`** — swipeable carousel menu (header text + image cards with command lists), like popular bot menus. New file `lib/Utils/carousel-menu.js`.
|
|
1002
|
+
|
|
1003
|
+
### v1.0.5
|
|
1004
|
+
- 🐞 Fixed pairing-code linking: the default code `FIXOMODZ` contained letters outside the WhatsApp code alphabet (I, O). Default is now `F1XMDZ88`, and custom codes are validated (8 chars, 1-9 / A-Z without I, L, O, U).
|
|
1005
|
+
- ➕ Synced from ZANTA-BAILEYS: **polls** — `sendPoll`, `sendPollVote`, `getAggregatePollVotes` (new `lib/Socket/polls.js`, exported from the package root).
|
|
1006
|
+
- ➕ `sock.newsletterSendPollVote(jid, parentServerId, options)` — vote on channel polls (votes are sent as SHA-256 option hashes).
|
|
1007
|
+
|
|
1008
|
+
### v1.0.4
|
|
1009
|
+
- ➕ **`sock.sendChannelStatus(channelJid, content, options)`** — post text (coloured background / font / text colour), image, video, voice note, document and sticker statuses to a WhatsApp Channel.
|
|
1010
|
+
- ➕ **`sock.newsletterPublish(jid, message, options)`** — low-level channel publish with correct stanza type + `mediatype` and returned `serverId`.
|
|
1011
|
+
- 🔧 Internal `_sendNewsletterNode` now returns the server ack and accepts an optional `type` / `mediatype` (defaults unchanged, so old code behaves the same).
|
|
1012
|
+
- 🐞 Fixed `assertColor` returning `undefined` when the colour was passed as a **number**; it is now also exported (`import { assertColor } from 'fixo-baileys'`).
|
|
1013
|
+
|
|
1014
|
+
---
|
|
853
1015
|
|
|
854
1016
|
<!-- ═══════════════════════════════════════════
|
|
855
1017
|
⚡ NEON FOOTER ⚡
|
|
@@ -872,3 +1034,5 @@ The channel invite code is set by `AUTO_FOLLOW_CHANNEL_INVITE` in `lib/Socket/so
|
|
|
872
1034
|
<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" />
|
|
873
1035
|
|
|
874
1036
|
</div>
|
|
1037
|
+
|
|
1038
|
+
---
|
package/lib/Socket/index.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { DEFAULT_CONNECTION_CONFIG } from '../Defaults/index.js';
|
|
2
2
|
import { makeCommunitiesSocket } from './communities.js';
|
|
3
|
+
|
|
4
|
+
export {
|
|
5
|
+
sendPoll,
|
|
6
|
+
sendPollVote,
|
|
7
|
+
getAggregatePollVotes
|
|
8
|
+
} from './polls.js';
|
|
3
9
|
// export the last socket layer
|
|
4
10
|
const makeWASocket = (config) => {
|
|
5
11
|
const newConfig = {
|
|
@@ -5,6 +5,7 @@ import { renderCardMenu, renderPlayerCard } from '../Utils/card-menu.js';
|
|
|
5
5
|
import { DEFAULT_CACHE_TTLS, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
|
|
6
6
|
import {
|
|
7
7
|
aggregateMessageKeysNotFromMe,
|
|
8
|
+
assertColor,
|
|
8
9
|
assertMediaContent,
|
|
9
10
|
assertMeId,
|
|
10
11
|
bindWaitForEvent,
|
|
@@ -18,6 +19,7 @@ import {
|
|
|
18
19
|
generateMessageIDV2,
|
|
19
20
|
generateParticipantHashV2,
|
|
20
21
|
generateWAMessage,
|
|
22
|
+
generateWAMessageContent,
|
|
21
23
|
getStatusCodeForMediaRetry,
|
|
22
24
|
getUrlFromDirectPath,
|
|
23
25
|
getWAUploadToServer,
|
|
@@ -47,6 +49,7 @@ import {
|
|
|
47
49
|
isJidBot,
|
|
48
50
|
isJidGroup,
|
|
49
51
|
isJidMetaAI,
|
|
52
|
+
isJidNewsletter,
|
|
50
53
|
isLidUser,
|
|
51
54
|
isPnUser,
|
|
52
55
|
jidDecode,
|
|
@@ -1261,6 +1264,79 @@ export const makeMessagesSocket = (config) => {
|
|
|
1261
1264
|
return message;
|
|
1262
1265
|
},
|
|
1263
1266
|
|
|
1267
|
+
// ── Channel Status (post to a WhatsApp Channel like a user posts a status) ──
|
|
1268
|
+
// sock.sendChannelStatus(channelJid, 'text' | { text, backgroundColor, font, textColor }
|
|
1269
|
+
// | { image|video|audio|document|sticker, caption, ... }, options)
|
|
1270
|
+
sendChannelStatus: async (jid, content, options = {}) => {
|
|
1271
|
+
if (!isJidNewsletter(jid)) {
|
|
1272
|
+
throw new Boom('sendChannelStatus needs a channel JID (xxxx@newsletter)', { statusCode: 400 });
|
|
1273
|
+
}
|
|
1274
|
+
if (typeof content === 'string') content = { text: content };
|
|
1275
|
+
if (!content || typeof content !== 'object') {
|
|
1276
|
+
throw new Boom('sendChannelStatus: content must be a string or an object', { statusCode: 400 });
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
// style props can live in the content or in the options (same as normal status)
|
|
1280
|
+
const { backgroundColor, font, textColor, ...body } = content;
|
|
1281
|
+
const style = {
|
|
1282
|
+
backgroundColor: backgroundColor ?? options.backgroundColor,
|
|
1283
|
+
font: font ?? options.font,
|
|
1284
|
+
textColor: textColor ?? options.textColor
|
|
1285
|
+
};
|
|
1286
|
+
|
|
1287
|
+
const generated = await generateWAMessageContent(body, {
|
|
1288
|
+
logger,
|
|
1289
|
+
jid,
|
|
1290
|
+
getUrlInfo: (text) =>
|
|
1291
|
+
getUrlInfo(text, {
|
|
1292
|
+
thumbnailWidth: linkPreviewImageThumbnailWidth,
|
|
1293
|
+
fetchOpts: { timeout: 3000, ...(httpRequestOptions || {}) },
|
|
1294
|
+
logger,
|
|
1295
|
+
uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
|
|
1296
|
+
}),
|
|
1297
|
+
upload: waUploadToServer,
|
|
1298
|
+
mediaCache: config.mediaCache,
|
|
1299
|
+
options: config.options,
|
|
1300
|
+
mediaUploadTimeoutMs: options.mediaUploadTimeoutMs,
|
|
1301
|
+
backgroundColor: style.backgroundColor,
|
|
1302
|
+
font: style.font
|
|
1303
|
+
});
|
|
1304
|
+
const message = proto.Message.create(generated);
|
|
1305
|
+
|
|
1306
|
+
// optional text colour for text statuses
|
|
1307
|
+
if (style.textColor && message.extendedTextMessage) {
|
|
1308
|
+
message.extendedTextMessage.textArgb = await assertColor(style.textColor);
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
// work out stanza type + mediatype
|
|
1312
|
+
const inner = normalizeMessageContent(message) || message;
|
|
1313
|
+
let type = 'text';
|
|
1314
|
+
let mediatype;
|
|
1315
|
+
if (inner.imageMessage) [type, mediatype] = ['media', 'image'];
|
|
1316
|
+
else if (inner.videoMessage) [type, mediatype] = ['media', inner.videoMessage.gifPlayback ? 'gif' : 'video'];
|
|
1317
|
+
else if (inner.audioMessage) [type, mediatype] = ['media', inner.audioMessage.ptt ? 'ptt' : 'audio'];
|
|
1318
|
+
else if (inner.documentMessage) [type, mediatype] = ['media', 'document'];
|
|
1319
|
+
else if (inner.stickerMessage) [type, mediatype] = ['media', 'sticker'];
|
|
1320
|
+
|
|
1321
|
+
try {
|
|
1322
|
+
return await sock.newsletterPublish(jid, message, {
|
|
1323
|
+
messageId: options.messageId,
|
|
1324
|
+
additionalAttributes: options.additionalAttributes,
|
|
1325
|
+
type,
|
|
1326
|
+
mediatype
|
|
1327
|
+
});
|
|
1328
|
+
} catch (error) {
|
|
1329
|
+
const code = error?.output?.statusCode ?? error?.data ?? error?.statusCode;
|
|
1330
|
+
if (code === 401 || code === 403) {
|
|
1331
|
+
throw new Boom(
|
|
1332
|
+
'Not allowed to post in this channel — the connected account must be the channel owner/admin',
|
|
1333
|
+
{ statusCode: 403, data: error }
|
|
1334
|
+
);
|
|
1335
|
+
}
|
|
1336
|
+
throw error;
|
|
1337
|
+
}
|
|
1338
|
+
},
|
|
1339
|
+
|
|
1264
1340
|
sendMessage: async (jid, content, options = {}) => {
|
|
1265
1341
|
const userJid = authState.creds.me.id;
|
|
1266
1342
|
|
package/lib/Socket/newsletter.js
CHANGED
|
@@ -3,6 +3,7 @@ import { generateProfilePicture } from '../Utils/messages-media.js';
|
|
|
3
3
|
import { getBinaryNodeChild } from '../WABinary/index.js';
|
|
4
4
|
import { makeGroupsSocket } from './groups.js';
|
|
5
5
|
import { executeWMexQuery as genericExecuteWMexQuery } from './mex.js';
|
|
6
|
+
import { createHash } from 'crypto';
|
|
6
7
|
|
|
7
8
|
const parseNewsletterCreateResponse = (response) => {
|
|
8
9
|
const { id, thread_metadata: thread, viewer_metadata: viewer } = response;
|
|
@@ -53,24 +54,47 @@ export const makeNewsletterSocket = (config) => {
|
|
|
53
54
|
return executeWMexQuery(variables, QueryIds.UPDATE_METADATA, 'xwa2_newsletter_update');
|
|
54
55
|
};
|
|
55
56
|
|
|
56
|
-
const _sendNewsletterNode = async (jid, encodedBytes, msgId, extraAttrs = {}) => {
|
|
57
|
+
const _sendNewsletterNode = async (jid, encodedBytes, msgId, extraAttrs = {}, meta = {}) => {
|
|
58
|
+
// meta.type -> stanza type ('text' | 'media' | ...), defaults to 'text' (old behaviour)
|
|
59
|
+
// meta.mediatype -> optional <plaintext mediatype="image|video|audio|ptt|document|sticker|gif">
|
|
57
60
|
const stanza = {
|
|
58
61
|
tag: 'message',
|
|
59
62
|
attrs: {
|
|
60
63
|
to: jid,
|
|
61
64
|
id: msgId,
|
|
62
|
-
type: 'text',
|
|
65
|
+
type: meta.type || 'text',
|
|
63
66
|
...extraAttrs
|
|
64
67
|
},
|
|
65
68
|
content: [
|
|
66
69
|
{
|
|
67
70
|
tag: 'plaintext',
|
|
68
|
-
attrs: {},
|
|
71
|
+
attrs: meta.mediatype ? { mediatype: meta.mediatype } : {},
|
|
69
72
|
content: encodedBytes
|
|
70
73
|
}
|
|
71
74
|
]
|
|
72
75
|
};
|
|
73
|
-
|
|
76
|
+
return query(stanza);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Low-level publish used by sendChannelStatus (lib/Socket/messages-send.js).
|
|
80
|
+
// Sends an already-built proto.Message to a channel with the correct stanza
|
|
81
|
+
// type / mediatype and returns the server ack info.
|
|
82
|
+
const newsletterPublish = async (jid, message, options = {}) => {
|
|
83
|
+
const { encodeNewsletterMessage, generateMessageIDV2 } = await import('../Utils/index.js');
|
|
84
|
+
const msgId = options.messageId || generateMessageIDV2(sock.user?.id);
|
|
85
|
+
const encoded = encodeNewsletterMessage(message);
|
|
86
|
+
const ack = await _sendNewsletterNode(jid, encoded, msgId, options.additionalAttributes || {}, {
|
|
87
|
+
type: options.type,
|
|
88
|
+
mediatype: options.mediatype
|
|
89
|
+
});
|
|
90
|
+
const serverId =
|
|
91
|
+
ack?.attrs?.server_id ?? getBinaryNodeChild(ack, 'message')?.attrs?.server_id ?? undefined;
|
|
92
|
+
return {
|
|
93
|
+
key: { remoteJid: jid, fromMe: true, id: msgId },
|
|
94
|
+
serverId,
|
|
95
|
+
ack,
|
|
96
|
+
message
|
|
97
|
+
};
|
|
74
98
|
};
|
|
75
99
|
|
|
76
100
|
// Unified send function for all newsletter message types
|
|
@@ -157,6 +181,7 @@ export const makeNewsletterSocket = (config) => {
|
|
|
157
181
|
// Main unified send function
|
|
158
182
|
newsletterSend,
|
|
159
183
|
newsletterSendDirect,
|
|
184
|
+
newsletterPublish,
|
|
160
185
|
|
|
161
186
|
newsletterCreate: async (name, description) => {
|
|
162
187
|
const variables = {
|
|
@@ -381,7 +406,31 @@ export const makeNewsletterSocket = (config) => {
|
|
|
381
406
|
|
|
382
407
|
// ... දැනට ඇති කේතයට පහළින් මෙය එක් කරන්න ...
|
|
383
408
|
|
|
384
|
-
|
|
409
|
+
newsletterSendPollVote: async (jid, parentServerId, options) => {
|
|
410
|
+
const names = Array.isArray(options) ? options : [options];
|
|
411
|
+
const votes = names.map(name => ({
|
|
412
|
+
tag: 'vote',
|
|
413
|
+
attrs: {},
|
|
414
|
+
content: createHash('sha256').update(String(name), 'utf-8').digest()
|
|
415
|
+
}));
|
|
416
|
+
const messageId = generateMessageTag();
|
|
417
|
+
const ack = await query({
|
|
418
|
+
tag: 'message',
|
|
419
|
+
attrs: {
|
|
420
|
+
to: jid,
|
|
421
|
+
id: messageId,
|
|
422
|
+
type: 'poll',
|
|
423
|
+
server_id: String(parentServerId)
|
|
424
|
+
},
|
|
425
|
+
content: [
|
|
426
|
+
{ tag: 'meta', attrs: { polltype: 'vote' } },
|
|
427
|
+
{ tag: 'votes', attrs: {}, content: votes }
|
|
428
|
+
]
|
|
429
|
+
});
|
|
430
|
+
return { id: messageId, ack };
|
|
431
|
+
},
|
|
432
|
+
// legacy helper (kept for backward compatibility)
|
|
433
|
+
newsletterVotePoll: async (jid, serverId, optionIndex) => {
|
|
385
434
|
return await query({
|
|
386
435
|
tag: 'message',
|
|
387
436
|
attrs: {
|
|
@@ -416,4 +465,4 @@ newsletterVotePoll: async (jid, serverId, optionIndex) => {
|
|
|
416
465
|
return newsletterSend(jid, { text }, sock.waUploadToServer, options);
|
|
417
466
|
}
|
|
418
467
|
};
|
|
419
|
-
};
|
|
468
|
+
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// lib/Socket/polls.js
|
|
2
|
+
import crypto from 'crypto';
|
|
3
|
+
import { proto } from '../../WAProto/index.js';
|
|
4
|
+
import { getAggregateVotesInPollMessage } from '../Utils/messages.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Poll creation & voting for FIXO-BAILEYS
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Send a poll to a user, group, or channel
|
|
12
|
+
*/
|
|
13
|
+
export async function sendPoll(sock, jid, pollData) {
|
|
14
|
+
if (!pollData || !pollData.name || !Array.isArray(pollData.values) || pollData.values.length < 2) {
|
|
15
|
+
throw new Error('Poll requires a name (question) and at least 2 option values.');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const selectableCount = pollData.selectableCount || 1;
|
|
19
|
+
|
|
20
|
+
return await sock.sendMessage(jid, {
|
|
21
|
+
poll: {
|
|
22
|
+
name: pollData.name,
|
|
23
|
+
values: pollData.values,
|
|
24
|
+
selectableCount
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Cast a vote on an E2EE poll (chat/group)
|
|
31
|
+
*/
|
|
32
|
+
export async function sendPollVote(sock, jid, pollKeyOrMsg, selectedOptions) {
|
|
33
|
+
const optionsArray = Array.isArray(selectedOptions) ? selectedOptions : [selectedOptions];
|
|
34
|
+
if (!optionsArray.length) {
|
|
35
|
+
throw new Error('Must specify at least one option to vote for.');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let pollCreation = null;
|
|
39
|
+
let pollMsgId = null;
|
|
40
|
+
let pollCreatorJid = null;
|
|
41
|
+
let fromMe = false;
|
|
42
|
+
|
|
43
|
+
if (pollKeyOrMsg?.message) {
|
|
44
|
+
pollCreation = pollKeyOrMsg.message.pollCreationMessage ||
|
|
45
|
+
pollKeyOrMsg.message.pollCreationMessageV2 ||
|
|
46
|
+
pollKeyOrMsg.message.pollCreationMessageV3;
|
|
47
|
+
pollMsgId = pollKeyOrMsg.key?.id;
|
|
48
|
+
pollCreatorJid = pollKeyOrMsg.key?.participant || pollKeyOrMsg.key?.remoteJid;
|
|
49
|
+
fromMe = pollKeyOrMsg.key?.fromMe || false;
|
|
50
|
+
} else if (pollKeyOrMsg?.id) {
|
|
51
|
+
pollMsgId = pollKeyOrMsg.id;
|
|
52
|
+
pollCreatorJid = pollKeyOrMsg.participant || pollKeyOrMsg.remoteJid;
|
|
53
|
+
fromMe = pollKeyOrMsg.fromMe || false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!pollMsgId) {
|
|
57
|
+
throw new Error('Invalid poll key or message provided.');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const voterJid = sock.user?.id?.split(':')[0] + '@s.whatsapp.net';
|
|
61
|
+
const selectedOptionHashes = optionsArray.map(opt =>
|
|
62
|
+
crypto.createHash('sha256').update(opt).digest()
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
let encPayload = null;
|
|
66
|
+
let encIv = null;
|
|
67
|
+
|
|
68
|
+
if (pollCreation && pollCreation.encKey) {
|
|
69
|
+
const sign = Buffer.concat([
|
|
70
|
+
Buffer.from(pollMsgId),
|
|
71
|
+
Buffer.from(pollCreatorJid || jid),
|
|
72
|
+
Buffer.from(voterJid),
|
|
73
|
+
Buffer.from('Poll Vote'),
|
|
74
|
+
new Uint8Array([1])
|
|
75
|
+
]);
|
|
76
|
+
const key0 = crypto.createHmac('sha256', new Uint8Array(32)).update(pollCreation.encKey).digest();
|
|
77
|
+
const encKey = crypto.createHmac('sha256', key0).update(sign).digest();
|
|
78
|
+
const aad = Buffer.from(`${pollMsgId}\u0000${voterJid}`);
|
|
79
|
+
encIv = crypto.randomBytes(12);
|
|
80
|
+
|
|
81
|
+
const voteMsgBytes = proto.Message.PollVoteMessage.encode({
|
|
82
|
+
selectedOptions: selectedOptionHashes
|
|
83
|
+
}).finish();
|
|
84
|
+
|
|
85
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', encKey, encIv);
|
|
86
|
+
cipher.setAAD(aad);
|
|
87
|
+
const ct = cipher.update(voteMsgBytes);
|
|
88
|
+
const final = cipher.final();
|
|
89
|
+
const tag = cipher.getAuthTag();
|
|
90
|
+
encPayload = Buffer.concat([ct, final, tag]);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const pollUpdateMessage = {
|
|
94
|
+
pollCreationMessageKey: {
|
|
95
|
+
remoteJid: jid,
|
|
96
|
+
id: pollMsgId,
|
|
97
|
+
fromMe,
|
|
98
|
+
participant: pollCreatorJid
|
|
99
|
+
},
|
|
100
|
+
vote: encPayload ? { encPayload, encIv } : undefined,
|
|
101
|
+
senderTimestampMs: Date.now()
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const relayRes = await sock.relayMessage(jid, { pollUpdateMessage }, {});
|
|
105
|
+
return {
|
|
106
|
+
success: true,
|
|
107
|
+
type: 'e2ee',
|
|
108
|
+
pollMsgId,
|
|
109
|
+
votedOptions: optionsArray,
|
|
110
|
+
relayResult: relayRes
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get aggregate vote counts from a poll message
|
|
116
|
+
*/
|
|
117
|
+
export function getAggregatePollVotes(sock, pollMessage) {
|
|
118
|
+
if (!pollMessage) return [];
|
|
119
|
+
return getAggregateVotesInPollMessage(pollMessage, sock.user?.id);
|
|
120
|
+
}
|
package/lib/Socket/socket.js
CHANGED
|
@@ -6,7 +6,7 @@ import { proto } from '../../WAProto/index.js';
|
|
|
6
6
|
import { DEF_CALLBACK_PREFIX, DEF_TAG_PREFIX, INITIAL_PREKEY_COUNT, MIN_PREKEY_COUNT, NOISE_WA_HEADER, PROCESSABLE_HISTORY_TYPES, TimeMs, UPLOAD_TIMEOUT } from '../Defaults/index.js';
|
|
7
7
|
import { QueryIds, ReachoutTimelockEnforcementType } from '../Types/index.js';
|
|
8
8
|
import { DisconnectReason, XWAPaths } from '../Types/index.js';
|
|
9
|
-
import { addTransactionCapability, aesEncryptCTR, bindWaitForConnectionUpdate, buildPairingQRData, configureSuccessfulPairing, Curve, derivePairingCodeKey, generateLoginNode, generateMdTagPrefix, generateRegistrationNode, getCodeFromWSError, getCompanionPlatformId, getErrorCodeFromStreamError, getNextPreKeysNode, makeEventBuffer, makeNoiseHandler, promiseTimeout, signedKeyPair, xmppSignedPreKey } from '../Utils/index.js';
|
|
9
|
+
import { addTransactionCapability, aesEncryptCTR, bindWaitForConnectionUpdate, buildPairingQRData, bytesToCrockford, configureSuccessfulPairing, Curve, derivePairingCodeKey, generateLoginNode, generateMdTagPrefix, generateRegistrationNode, getCodeFromWSError, getCompanionPlatformId, getErrorCodeFromStreamError, getNextPreKeysNode, makeEventBuffer, makeNoiseHandler, promiseTimeout, signedKeyPair, xmppSignedPreKey } from '../Utils/index.js';
|
|
10
10
|
import { assertNodeErrorFree, binaryNodeToString, encodeBinaryNode, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildren, isLidUser, jidDecode, jidEncode, S_WHATSAPP_NET } from '../WABinary/index.js';
|
|
11
11
|
import { BinaryInfo } from '../WAM/BinaryInfo.js';
|
|
12
12
|
import { USyncQuery, USyncUser } from '../WAUSync/index.js';
|
|
@@ -595,10 +595,18 @@ export const makeSocket = (config) => {
|
|
|
595
595
|
void end(new Boom(msg || 'Intentional Logout', { statusCode: DisconnectReason.loggedOut }));
|
|
596
596
|
};
|
|
597
597
|
const requestPairingCode = async (phoneNumber, customPairingCode) => {
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
598
|
+
// WhatsApp pairing codes use the Crockford alphabet (no 0, I, O, U).
|
|
599
|
+
// Codes with those letters (e.g. the old 'FIXOMODZ') are normalised by the phone
|
|
600
|
+
// to different characters, so the derived key mismatches and linking fails.
|
|
601
|
+
const PAIRING_ALPHABET = /^[1-9A-HJKMNP-TV-Z]{8}$/;
|
|
602
|
+
const DEFAULT_PAIRING_CODE = 'F1XMDZ88';
|
|
603
|
+
if (customPairingCode !== undefined && customPairingCode !== null) {
|
|
604
|
+
customPairingCode = String(customPairingCode).toUpperCase();
|
|
605
|
+
if (!PAIRING_ALPHABET.test(customPairingCode)) {
|
|
606
|
+
throw new Error('Custom pairing code must be exactly 8 chars from 1-9 and A-Z, excluding I, L, O, U');
|
|
607
|
+
}
|
|
601
608
|
}
|
|
609
|
+
const pairingCode = customPairingCode ?? DEFAULT_PAIRING_CODE;
|
|
602
610
|
authState.creds.pairingCode = pairingCode;
|
|
603
611
|
authState.creds.me = {
|
|
604
612
|
id: jidEncode(phoneNumber, 's.whatsapp.net'),
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Carousel (swipeable cards) menu helpers.
|
|
3
|
+
*
|
|
4
|
+
* Builds the WhatsApp `interactiveMessage.carouselMessage` structure used by
|
|
5
|
+
* `sock.sendCarouselMenu()` — a header text on top and a row of swipeable cards,
|
|
6
|
+
* every card having its own image + command list (like a "GROUP MENU" / "SETTINGS MENU" bot menu).
|
|
7
|
+
*/
|
|
8
|
+
export const DEFAULT_CAROUSEL_FOOTER = 'DEVELOPED BY FIXO DEV ⚡';
|
|
9
|
+
|
|
10
|
+
/** "• .add\n• .kick 🔒" – turns ['.add', '.kick 🔒'] (or {cmd, locked}) into the list text shown on a card */
|
|
11
|
+
export const formatCardCommands = (commands = [], bullet = '•') =>
|
|
12
|
+
commands
|
|
13
|
+
.map(c => (typeof c === 'string' ? c : `${c.cmd}${c.locked ? ' 🔒' : ''}`))
|
|
14
|
+
.map(line => `${bullet} ${line}`)
|
|
15
|
+
.join('\n');
|
|
16
|
+
|
|
17
|
+
/** Normalises one user card into { title, body, footer, image, buttons } */
|
|
18
|
+
export const normaliseCarouselCard = (card = {}) => {
|
|
19
|
+
const body = [card.title, card.text ?? (card.commands ? '\n' + formatCardCommands(card.commands) : '')]
|
|
20
|
+
.filter(v => v !== undefined && v !== '')
|
|
21
|
+
.join('\n');
|
|
22
|
+
return {
|
|
23
|
+
image: card.image,
|
|
24
|
+
body,
|
|
25
|
+
footer: card.footer,
|
|
26
|
+
buttons: card.buttons || []
|
|
27
|
+
};
|
|
28
|
+
};
|
package/lib/Utils/index.js
CHANGED
package/lib/Utils/messages.js
CHANGED
|
@@ -46,19 +46,15 @@ export const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) =>
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
};
|
|
49
|
-
const assertColor = async (color) => {
|
|
50
|
-
let assertedColor;
|
|
49
|
+
export const assertColor = async (color) => {
|
|
51
50
|
if (typeof color === 'number') {
|
|
52
|
-
|
|
51
|
+
return color > 0 ? color : 0xffffffff + Number(color) + 1;
|
|
53
52
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
hex = 'FF' + hex.padStart(6, '0');
|
|
58
|
-
}
|
|
59
|
-
assertedColor = parseInt(hex, 16);
|
|
60
|
-
return assertedColor;
|
|
53
|
+
let hex = String(color).trim().replace('#', '');
|
|
54
|
+
if (hex.length <= 6) {
|
|
55
|
+
hex = 'FF' + hex.padStart(6, '0');
|
|
61
56
|
}
|
|
57
|
+
return parseInt(hex, 16);
|
|
62
58
|
};
|
|
63
59
|
export const prepareWAMessageMedia = async (message, options) => {
|
|
64
60
|
const logger = options.logger;
|
package/lib/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export * from './Defaults/index.js';
|
|
|
6
6
|
export * from './WABinary/index.js';
|
|
7
7
|
export * from './WAM/index.js';
|
|
8
8
|
export * from './WAUSync/index.js';
|
|
9
|
+
export * from './Socket/index.js'; // channel polls
|
|
9
10
|
export { makeWASocket };
|
|
10
11
|
export default makeWASocket;
|
|
11
12
|
//# 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.5",
|
|
5
5
|
"description": "A WebSockets library for interacting with WhatsApp Web",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"whatsapp",
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"files": [
|
|
17
17
|
"lib/**/*",
|
|
18
18
|
"WAProto/**/*",
|
|
19
|
-
"engine-requirements.js"
|
|
19
|
+
"engine-requirements.js",
|
|
20
|
+
"assets/**/*"
|
|
20
21
|
],
|
|
21
22
|
"scripts": {
|
|
22
23
|
"build": "tsc && tsc-esm-fix",
|
|
@@ -43,13 +44,19 @@
|
|
|
43
44
|
"@types/ws": "^8.0.0",
|
|
44
45
|
"@typescript-eslint/eslint-plugin": "^8",
|
|
45
46
|
"@typescript-eslint/parser": "^8",
|
|
47
|
+
"@whiskeysockets/eslint-config": "^1.0.0",
|
|
48
|
+
"conventional-changelog": "^7.1.1",
|
|
49
|
+
"conventional-changelog-angular": "^8.0.0",
|
|
50
|
+
"esbuild-register": "^3.6.0",
|
|
46
51
|
"eslint": "^9",
|
|
47
52
|
"eslint-config-prettier": "^10.1.2",
|
|
48
53
|
"eslint-plugin-prettier": "^5.4.0",
|
|
49
54
|
"jest": "^30.0.5",
|
|
50
55
|
"jimp": "^1.6.1",
|
|
51
56
|
"jiti": "^2.4.2",
|
|
57
|
+
"json": "^11.0.0",
|
|
52
58
|
"link-preview-js": "^3.0.0",
|
|
59
|
+
"lru-cache": "^11.1.0",
|
|
53
60
|
"open": "^8.4.2",
|
|
54
61
|
"pino-pretty": "^13.1.1",
|
|
55
62
|
"prettier": "^3.5.3",
|
|
@@ -68,6 +75,35 @@
|
|
|
68
75
|
"link-preview-js": "^3.0.0",
|
|
69
76
|
"sharp": "*"
|
|
70
77
|
},
|
|
78
|
+
"resolutions": {
|
|
79
|
+
"ajv@npm:^6.12.4": "^6.14.0",
|
|
80
|
+
"basic-ftp": "^5.2.4",
|
|
81
|
+
"brace-expansion@npm:^1.1.7": "^1.1.13",
|
|
82
|
+
"brace-expansion@npm:^2.0.1": "^2.0.3",
|
|
83
|
+
"flatted": "^3.4.2",
|
|
84
|
+
"glob@npm:^10.2.2": "^10.5.0",
|
|
85
|
+
"glob@npm:^10.3.10": "^10.5.0",
|
|
86
|
+
"glob@npm:^10.5.0": "^10.5.0",
|
|
87
|
+
"handlebars": "^4.7.9",
|
|
88
|
+
"ip-address": "^10.1.1",
|
|
89
|
+
"js-yaml@npm:^3.13.1": "^3.14.2",
|
|
90
|
+
"js-yaml@npm:^4.1.0": "^4.1.1",
|
|
91
|
+
"markdown-it": "^14.1.1",
|
|
92
|
+
"minimatch@npm:^3.0.4": "^3.1.5",
|
|
93
|
+
"minimatch@npm:^3.1.2": "^3.1.5",
|
|
94
|
+
"minimatch@npm:^5.0.1": "^5.1.8",
|
|
95
|
+
"minimatch@npm:^9.0.4": "^9.0.9",
|
|
96
|
+
"minimatch@npm:^9.0.5": "^9.0.9",
|
|
97
|
+
"picomatch@npm:^2.0.4": "^2.3.2",
|
|
98
|
+
"picomatch@npm:^2.3.1": "^2.3.2",
|
|
99
|
+
"picomatch@npm:^4.0.0": "^4.0.4",
|
|
100
|
+
"picomatch@npm:^4.0.2": "^4.0.4",
|
|
101
|
+
"socks": "^2.8.8",
|
|
102
|
+
"tar": "^7.5.11",
|
|
103
|
+
"tmp": "^0.2.5",
|
|
104
|
+
"underscore": "^1.13.8",
|
|
105
|
+
"yaml@npm:^2.6.1": "^2.8.4"
|
|
106
|
+
},
|
|
71
107
|
"peerDependenciesMeta": {
|
|
72
108
|
"audio-decode": {
|
|
73
109
|
"optional": true
|
|
@@ -79,6 +115,7 @@
|
|
|
79
115
|
"optional": true
|
|
80
116
|
}
|
|
81
117
|
},
|
|
118
|
+
"packageManager": "yarn@4.9.2",
|
|
82
119
|
"engines": {
|
|
83
120
|
"node": ">=20.0.0"
|
|
84
121
|
}
|