jagproject 26.3.20 → 26.3.23

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.
@@ -9,7 +9,7 @@ const libsignal_1 = require("../Signal/libsignal");
9
9
  const browser_utils_1 = require("../Utils/browser-utils");
10
10
  const logger_1 = __importDefault(require("../Utils/logger"));
11
11
  const waVer = require("./wileys-version.json");
12
- exports.version = waVer?.version || [2, 3000, 1035247988];
12
+ exports.version = waVer?.version || [2, 3000, 1036687490];
13
13
  exports.UNAUTHORIZED_CODES = [401, 403, 419];
14
14
  exports.DEFAULT_ORIGIN = 'https://web.whatsapp.com';
15
15
  exports.CALL_VIDEO_PREFIX = 'https://call.whatsapp.com/video/';
@@ -85,7 +85,9 @@ exports.MEDIA_PATH_MAP = {
85
85
  'product-catalog-image': '/product/image',
86
86
  'md-app-state': '',
87
87
  'md-msg-hist': '/mms/md-app-state',
88
- 'biz-cover-photo': '/pps/biz-cover-photo'
88
+ 'biz-cover-photo': '/pps/biz-cover-photo',
89
+ 'sticker-pack': '/mms/sticker',
90
+ 'thumbnail-sticker-pack': '/mms/image'
89
91
  };
90
92
  exports.MEDIA_HKDF_KEY_MAPPING = {
91
93
  audio: 'Audio',
@@ -106,7 +108,9 @@ exports.MEDIA_HKDF_KEY_MAPPING = {
106
108
  'product-catalog-image': '',
107
109
  'payment-bg-image': 'Payment Background',
108
110
  ptv: 'Video',
109
- 'biz-cover-photo': 'Image'
111
+ 'biz-cover-photo': 'Image',
112
+ 'sticker-pack': 'Sticker Pack',
113
+ 'thumbnail-sticker-pack': 'Image Thumbnail'
110
114
  };
111
115
  exports.MEDIA_KEYS = Object.keys(exports.MEDIA_PATH_MAP);
112
116
  exports.MIN_PREKEY_COUNT = 5;
@@ -2,7 +2,7 @@
2
2
  "version": [
3
3
  2,
4
4
  3000,
5
- 1035247988
5
+ 1036687490
6
6
  ],
7
- "raw": "2.3000.1035247988"
7
+ "raw": "2.3000.1036687490"
8
8
  }
@@ -634,6 +634,9 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
634
634
  else if (message.stickerMessage) {
635
635
  return 'sticker';
636
636
  }
637
+ else if (message.stickerPackMessage) {
638
+ return 'sticker_pack';
639
+ }
637
640
  else if (message.listMessage) {
638
641
  return 'list';
639
642
  }
@@ -444,6 +444,354 @@ const generateWAMessageContent = async (message, options) => {
444
444
  }
445
445
  }
446
446
  }
447
+ else if ('stickerPack' in message && !!message.stickerPack) {
448
+ const { zip } = require('fflate');
449
+ const { stickers, cover, name, publisher, packId, description } = message.stickerPack;
450
+ const coverSource = cover || ((stickers[0] && (stickers[0].data || stickers[0].sticker)) || null);
451
+ if (!Array.isArray(stickers)) {
452
+ throw new boom_1.Boom('stickerPack.stickers must be an array', { statusCode: 400 });
453
+ }
454
+ if (stickers.length > 60) {
455
+ throw new boom_1.Boom('Sticker pack exceeds the maximum limit of 60 stickers', { statusCode: 400 });
456
+ }
457
+ if (stickers.length === 0) {
458
+ throw new boom_1.Boom('Sticker pack must contain at least one sticker', { statusCode: 400 });
459
+ }
460
+ const stickerPackId = packId || (0, generics_1.generateMessageIDV2)();
461
+ let sharpLib = null;
462
+ let jimpLib = null;
463
+ try {
464
+ sharpLib = require('sharp');
465
+ }
466
+ catch (_sharpError) { }
467
+ try {
468
+ jimpLib = require('jimp');
469
+ }
470
+ catch (_jimpError) { }
471
+ if (!sharpLib && !jimpLib) {
472
+ throw new boom_1.Boom('No image processing library available (install sharp or jimp)', { statusCode: 400 });
473
+ }
474
+ const isWebPBuffer = (buf) => (buf.length >= 12
475
+ && buf[0] === 0x52
476
+ && buf[1] === 0x49
477
+ && buf[2] === 0x46
478
+ && buf[3] === 0x46
479
+ && buf[8] === 0x57
480
+ && buf[9] === 0x45
481
+ && buf[10] === 0x42
482
+ && buf[11] === 0x50);
483
+ const isAnimatedWebP = (buf) => {
484
+ if (!isWebPBuffer(buf)) {
485
+ return false;
486
+ }
487
+ let offset = 12;
488
+ while (offset < buf.length - 8) {
489
+ const fourCC = buf.toString('ascii', offset, offset + 4);
490
+ const chunkSize = buf.readUInt32LE(offset + 4);
491
+ if (fourCC === 'VP8X') {
492
+ const flagsOffset = offset + 8;
493
+ if (flagsOffset < buf.length && (buf[flagsOffset] & 0x02)) {
494
+ return true;
495
+ }
496
+ }
497
+ else if (fourCC === 'ANIM' || fourCC === 'ANMF') {
498
+ return true;
499
+ }
500
+ offset += 8 + chunkSize + (chunkSize % 2);
501
+ }
502
+ return false;
503
+ };
504
+ const getJimpBuffer = async (image, mime) => {
505
+ if (typeof image.getBufferAsync === 'function') {
506
+ return await image.getBufferAsync(mime);
507
+ }
508
+ return await new Promise((resolve, reject) => {
509
+ image.getBuffer(mime, (err, result) => {
510
+ if (err) {
511
+ reject(err);
512
+ }
513
+ else {
514
+ resolve(result);
515
+ }
516
+ });
517
+ });
518
+ };
519
+ const { spawn } = require('child_process');
520
+ const { promises: fsPromises } = require('fs');
521
+ const { join } = require('path');
522
+ const { tmpdir } = require('os');
523
+ const isPngBuffer = (buf) => (buf.length >= 8
524
+ && buf[0] === 0x89
525
+ && buf[1] === 0x50
526
+ && buf[2] === 0x4e
527
+ && buf[3] === 0x47);
528
+ const isJpegBuffer = (buf) => (buf.length >= 3
529
+ && buf[0] === 0xff
530
+ && buf[1] === 0xd8
531
+ && buf[2] === 0xff);
532
+ const isGifBuffer = (buf) => (buf.length >= 4 && buf.slice(0, 4).toString() === 'GIF8');
533
+ const isWebmBuffer = (buf) => (buf.length >= 4
534
+ && buf[0] === 0x1a
535
+ && buf[1] === 0x45
536
+ && buf[2] === 0xdf
537
+ && buf[3] === 0xa3);
538
+ const isMp4Buffer = (buf) => (buf.length >= 8
539
+ && buf[4] === 0x66
540
+ && buf[5] === 0x74
541
+ && buf[6] === 0x79
542
+ && buf[7] === 0x70);
543
+ const looksLikeGzip = (buf) => (buf.length >= 2 && buf[0] === 0x1f && buf[1] === 0x8b);
544
+ const inferSourceKind = (buffer, source, stickerItem) => {
545
+ var _c, _d, _e, _f;
546
+ if (isAnimatedWebP(buffer)) {
547
+ return 'animated-webp';
548
+ }
549
+ if (isWebPBuffer(buffer)) {
550
+ return 'webp';
551
+ }
552
+ if (isPngBuffer(buffer)) {
553
+ return 'png';
554
+ }
555
+ if (isJpegBuffer(buffer)) {
556
+ return 'jpg';
557
+ }
558
+ if (isGifBuffer(buffer)) {
559
+ return 'gif';
560
+ }
561
+ if (isWebmBuffer(buffer)) {
562
+ return 'webm';
563
+ }
564
+ if (isMp4Buffer(buffer)) {
565
+ return 'mp4';
566
+ }
567
+ if (looksLikeGzip(buffer)) {
568
+ return 'tgs';
569
+ }
570
+ const hint = String(((_c = stickerItem === null || stickerItem === void 0 ? void 0 : stickerItem.format) !== null && _c !== void 0 ? _c : ((_d = stickerItem === null || stickerItem === void 0 ? void 0 : stickerItem.mimetype) !== null && _d !== void 0 ? _d : ((_f = (_e = source) === null || _e === void 0 ? void 0 : _e.url) === null || _f === void 0 ? void 0 : _f.toString()))) || '').toLowerCase();
571
+ if (hint.includes('webm')) {
572
+ return 'webm';
573
+ }
574
+ if (hint.includes('mp4') || hint.includes('mov') || hint.includes('mkv') || hint.includes('avi')) {
575
+ return 'mp4';
576
+ }
577
+ if (hint.includes('gif')) {
578
+ return 'gif';
579
+ }
580
+ if (hint.includes('png')) {
581
+ return 'png';
582
+ }
583
+ if (hint.includes('jpg') || hint.includes('jpeg')) {
584
+ return 'jpg';
585
+ }
586
+ if (hint.includes('tgs') || hint.includes('lottie') || hint.includes('json')) {
587
+ return 'tgs';
588
+ }
589
+ return 'unknown';
590
+ };
591
+ const convertStaticToWebp = async (buffer, label) => {
592
+ if (isWebPBuffer(buffer)) {
593
+ return buffer;
594
+ }
595
+ if (sharpLib) {
596
+ return await sharpLib(buffer)
597
+ .resize(512, 512, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } })
598
+ .webp({ quality: 80, lossless: false })
599
+ .toBuffer();
600
+ }
601
+ if (jimpLib) {
602
+ const JimpCtor = jimpLib.Jimp || jimpLib.default || jimpLib;
603
+ const image = await JimpCtor.read(buffer);
604
+ image.contain(512, 512);
605
+ return await getJimpBuffer(image, 'image/webp');
606
+ }
607
+ throw new boom_1.Boom(`No image processing library available for converting ${label} to WebP`, { statusCode: 400 });
608
+ };
609
+ const runFfmpegToWebp = async (inputBuffer, inputExt, animated, label) => {
610
+ const baseName = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
611
+ const inputPath = join(tmpdir(), `sticker-pack-${baseName}.${inputExt || 'bin'}`);
612
+ const outputPath = join(tmpdir(), `sticker-pack-${baseName}.webp`);
613
+ await fsPromises.writeFile(inputPath, inputBuffer);
614
+ const args = animated
615
+ ? [
616
+ '-y',
617
+ '-i', inputPath,
618
+ '-vf', 'fps=12,scale=512:512:force_original_aspect_ratio=decrease:flags=lanczos,pad=512:512:-1:-1:color=0x00000000',
619
+ '-loop', '0',
620
+ '-an',
621
+ '-vsync', '0',
622
+ '-s', '512:512',
623
+ '-vcodec', 'libwebp_anim',
624
+ '-lossless', '0',
625
+ '-compression_level', '6',
626
+ '-q:v', '55',
627
+ outputPath,
628
+ ]
629
+ : [
630
+ '-y',
631
+ '-i', inputPath,
632
+ '-vf', 'scale=512:512:force_original_aspect_ratio=decrease:flags=lanczos,pad=512:512:-1:-1:color=0x00000000',
633
+ '-frames:v', '1',
634
+ '-vcodec', 'libwebp',
635
+ '-lossless', '0',
636
+ '-compression_level', '6',
637
+ '-q:v', '70',
638
+ outputPath,
639
+ ];
640
+ await new Promise((resolve, reject) => {
641
+ const ff = spawn('ffmpeg', args);
642
+ let stderr = '';
643
+ ff.stderr.on('data', (chunk) => {
644
+ stderr += chunk.toString();
645
+ });
646
+ ff.on('error', (err) => reject(new boom_1.Boom(`ffmpeg not available for ${label}: ${err.message}`, { statusCode: 400 })));
647
+ ff.on('close', (code) => {
648
+ if (code === 0) {
649
+ resolve(undefined);
650
+ }
651
+ else {
652
+ reject(new boom_1.Boom(`ffmpeg failed while converting ${label} (code ${code})\n${stderr}`, { statusCode: 400 }));
653
+ }
654
+ });
655
+ });
656
+ try {
657
+ return await fsPromises.readFile(outputPath);
658
+ }
659
+ finally {
660
+ await Promise.allSettled([
661
+ fsPromises.unlink(inputPath),
662
+ fsPromises.unlink(outputPath)
663
+ ]);
664
+ }
665
+ };
666
+ const toWebpBuffer = async (buffer, label, source, stickerItem) => {
667
+ const sourceKind = inferSourceKind(buffer, source, stickerItem);
668
+ if (sourceKind === 'animated-webp' || sourceKind === 'webp') {
669
+ return buffer;
670
+ }
671
+ if (sourceKind === 'png' || sourceKind === 'jpg') {
672
+ return await convertStaticToWebp(buffer, label);
673
+ }
674
+ if (sourceKind === 'gif' || sourceKind === 'webm' || sourceKind === 'mp4') {
675
+ return await runFfmpegToWebp(buffer, sourceKind, true, label);
676
+ }
677
+ if (sourceKind === 'tgs') {
678
+ throw new boom_1.Boom(`TGS/Lottie source for ${label} is not supported in this runtime`, { statusCode: 400 });
679
+ }
680
+ return await convertStaticToWebp(buffer, label);
681
+ };
682
+ const toStaticWebpCoverBuffer = async (buffer, label, source, stickerItem) => {
683
+ const sourceKind = inferSourceKind(buffer, source, stickerItem);
684
+ if (sourceKind === 'gif' || sourceKind === 'webm' || sourceKind === 'mp4' || sourceKind === 'animated-webp') {
685
+ return await runFfmpegToWebp(buffer, sourceKind === 'animated-webp' ? 'webp' : sourceKind, false, label);
686
+ }
687
+ return await convertStaticToWebp(buffer, label);
688
+ };
689
+ const stickerData = {};
690
+ const stickerMetadata = await Promise.all(stickers.map(async (stickerItem, index) => {
691
+ const mediaSource = (stickerItem === null || stickerItem === void 0 ? void 0 : stickerItem.data) || (stickerItem === null || stickerItem === void 0 ? void 0 : stickerItem.sticker);
692
+ if (!mediaSource) {
693
+ throw new boom_1.Boom(`Sticker at index ${index} is missing data/sticker`, { statusCode: 400 });
694
+ }
695
+ const { stream } = await (0, messages_media_1.getStream)(mediaSource);
696
+ const buffer = await (0, messages_media_1.toBuffer)(stream);
697
+ const webpBuffer = await toWebpBuffer(buffer, `sticker at index ${index}`, mediaSource, stickerItem);
698
+ if (webpBuffer.length > 1024 * 1024) {
699
+ throw new boom_1.Boom(`Sticker at index ${index} exceeds the 1MB size limit`, { statusCode: 400 });
700
+ }
701
+ const hash = (0, crypto_2.sha256)(webpBuffer).toString('base64').replace(/\//g, '-');
702
+ const fileName = `${hash}.webp`;
703
+ stickerData[fileName] = [new Uint8Array(webpBuffer), { level: 0 }];
704
+ return {
705
+ fileName,
706
+ mimetype: 'image/webp',
707
+ isAnimated: isAnimatedWebP(webpBuffer),
708
+ emojis: stickerItem.emojis || [],
709
+ accessibilityLabel: stickerItem.accessibilityLabel || ''
710
+ };
711
+ }));
712
+ const trayIconFileName = `${stickerPackId}.webp`;
713
+ if (!coverSource) {
714
+ throw new boom_1.Boom('stickerPack.cover is required when no valid sticker media is available', { statusCode: 400 });
715
+ }
716
+ const { stream: coverStream } = await (0, messages_media_1.getStream)(coverSource);
717
+ const coverBuffer = await (0, messages_media_1.toBuffer)(coverStream);
718
+ const coverWebpBuffer = await toStaticWebpCoverBuffer(coverBuffer, 'cover', coverSource, stickers[0]);
719
+ stickerData[trayIconFileName] = [new Uint8Array(coverWebpBuffer), { level: 0 }];
720
+ const zipBuffer = await new Promise((resolve, reject) => {
721
+ zip(stickerData, (err, data) => {
722
+ if (err) {
723
+ reject(err);
724
+ }
725
+ else {
726
+ resolve(Buffer.from(data));
727
+ }
728
+ });
729
+ });
730
+ const stickerPackUpload = await (0, messages_media_1.encryptedStream)(zipBuffer, 'sticker-pack', {
731
+ logger: options.logger,
732
+ opts: options.options
733
+ });
734
+ const stickerPackUploadResult = await options.upload(stickerPackUpload.encWriteStream, {
735
+ fileEncSha256B64: stickerPackUpload.fileEncSha256.toString('base64'),
736
+ mediaType: 'sticker-pack',
737
+ timeoutMs: options.mediaUploadTimeoutMs
738
+ });
739
+ m.stickerPackMessage = {
740
+ name,
741
+ publisher,
742
+ stickerPackId,
743
+ packDescription: description,
744
+ stickerPackOrigin: WAProto_1.proto.Message.StickerPackMessage.StickerPackOrigin.THIRD_PARTY,
745
+ stickerPackSize: zipBuffer.length,
746
+ stickers: stickerMetadata,
747
+ fileSha256: stickerPackUpload.fileSha256,
748
+ fileEncSha256: stickerPackUpload.fileEncSha256,
749
+ mediaKey: stickerPackUpload.mediaKey,
750
+ directPath: stickerPackUploadResult.directPath,
751
+ fileLength: stickerPackUpload.fileLength,
752
+ mediaKeyTimestamp: (0, generics_1.unixTimestampSeconds)(),
753
+ trayIconFileName
754
+ };
755
+ try {
756
+ let thumbnailBuffer;
757
+ if (sharpLib) {
758
+ thumbnailBuffer = await sharpLib(coverWebpBuffer).resize(252, 252).jpeg().toBuffer();
759
+ }
760
+ else if (jimpLib) {
761
+ const JimpCtor = jimpLib.Jimp || jimpLib.default || jimpLib;
762
+ const image = await JimpCtor.read(coverWebpBuffer);
763
+ thumbnailBuffer = await getJimpBuffer(image.resize({ w: 252, h: 252 }), 'image/jpeg');
764
+ }
765
+ if (thumbnailBuffer && thumbnailBuffer.length) {
766
+ const thumbUpload = await (0, messages_media_1.encryptedStream)(thumbnailBuffer, 'thumbnail-sticker-pack', {
767
+ logger: options.logger,
768
+ opts: options.options,
769
+ mediaKey: stickerPackUpload.mediaKey
770
+ });
771
+ const thumbUploadResult = await options.upload(thumbUpload.encWriteStream, {
772
+ fileEncSha256B64: thumbUpload.fileEncSha256.toString('base64'),
773
+ mediaType: 'thumbnail-sticker-pack',
774
+ timeoutMs: options.mediaUploadTimeoutMs
775
+ });
776
+ Object.assign(m.stickerPackMessage, {
777
+ thumbnailDirectPath: thumbUploadResult.directPath,
778
+ thumbnailSha256: thumbUpload.fileSha256,
779
+ thumbnailEncSha256: thumbUpload.fileEncSha256,
780
+ thumbnailHeight: 252,
781
+ thumbnailWidth: 252,
782
+ imageDataHash: (0, crypto_2.sha256)(thumbnailBuffer).toString('base64')
783
+ });
784
+ }
785
+ }
786
+ catch (error) {
787
+ var _r, _s;
788
+ (_s = (_r = options.logger) === null || _r === void 0 ? void 0 : _r.warn) === null || _s === void 0 ? void 0 : _s.call(_r, { err: error }, 'sticker pack thumbnail generation failed');
789
+ }
790
+ m.stickerPackMessage.contextInfo = {
791
+ ...((message.contextInfo) || {}),
792
+ ...((message.mentions) ? { mentionedJid: message.mentions } : {})
793
+ };
794
+ }
447
795
  else if ('event' in message) {
448
796
  m.messageContextInfo = {
449
797
  messageSecret: message.event.messageSecret || (0, crypto_1.randomBytes)(32),
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "jagproject",
3
- "version": "26.03.20",
4
- "update": "17 Maret 2026",
3
+ "version": "26.03.23",
4
+ "update": "22 Maret 2026",
5
5
  "description": "WhatsApp Web API Library",
6
6
  "keywords": [
7
7
  "jagoan",
@@ -12,7 +12,7 @@
12
12
  "automation",
13
13
  "multi-device"
14
14
  ],
15
- "homepage": "https://whatsapp.com/channel/0029VanhMDo42DcjiRGJOc2m",
15
+ "homepage": "https://whatsapp.com/channel/0029VanhMDo42DcjiRGJOc2m",
16
16
  "repository": {
17
17
  "type": "git",
18
18
  "url": "git+https://github.com/ChandraGO/jagoans.git"
@@ -65,7 +65,8 @@
65
65
  "pino": "^9.6",
66
66
  "protobufjs": "^6.11.3",
67
67
  "uuid": "^10.0.0",
68
- "ws": "^8.13.0"
68
+ "ws": "^8.13.0",
69
+ "fflate": "^0.8.2"
69
70
  },
70
71
  "devDependencies": {
71
72
  "@adiwajshing/eslint-config": "github:adiwajshing/eslint-config",
package/readme.md CHANGED
@@ -1,7 +1,7 @@
1
1
  <h1 align="center">Baileys Modification</h1>
2
2
 
3
3
  <p align="center">
4
- <sub>Last update: <strong>15 Februari 2026</strong></sub>
4
+ <sub>Last update: <strong>22 Maret 2026</strong></sub>
5
5
  </p>
6
6
 
7
7
  <div align="center">
@@ -68,6 +68,78 @@ yarn add jagproject
68
68
  ---
69
69
 
70
70
 
71
+ # Cara Pakai Cepat
72
+
73
+ ## Instalasi
74
+
75
+ ```bash
76
+ npm install jagproject
77
+ ```
78
+
79
+ ## Pemakaian Dasar
80
+
81
+ ```javascript
82
+ const { default: makeWASocket, useMultiFileAuthState } = require('jagproject')
83
+
84
+ async function startBot() {
85
+ const { state, saveCreds } = await useMultiFileAuthState('./session')
86
+
87
+ const sock = makeWASocket({
88
+ auth: state,
89
+ printQRInTerminal: true
90
+ })
91
+
92
+ sock.ev.on('creds.update', saveCreds)
93
+
94
+ sock.ev.on('connection.update', ({ connection }) => {
95
+ if (connection === 'open') {
96
+ console.log('Bot connected')
97
+ }
98
+ })
99
+ }
100
+
101
+ startBot()
102
+ ```
103
+
104
+ ## Kirim Sticker Pack
105
+
106
+ ```javascript
107
+ await sock.sendMessage('6281234567890@s.whatsapp.net', {
108
+ stickerPack: {
109
+ name: 'Baileys Wileyss',
110
+ publisher: 'Project Pemuda',
111
+ description: 'Sticker pack custom dari Wileyss',
112
+ cover: { url: './assets/cover.png' },
113
+ stickers: [
114
+ { sticker: { url: './assets/sticker-1.webp' }, emojis: ['😀'] },
115
+ { sticker: { url: './assets/sticker-2.webp' }, emojis: ['🔥'] },
116
+ { sticker: { url: './assets/sticker-3.png' }, emojis: ['🚀'] }
117
+ ]
118
+ }
119
+ })
120
+ ```
121
+
122
+ ### Format object `stickerPack`
123
+
124
+ - `name`: nama pack sticker
125
+ - `publisher`: nama author/publisher
126
+ - `description`: deskripsi pack
127
+ - `cover`: cover/tray icon, bisa file path, buffer, stream, atau URL
128
+ - `stickers`: array isi sticker, minimal 1 maksimal 60
129
+ - `sticker`: media tiap sticker, bisa WebP/PNG/JPG. Jika bukan WebP akan dicoba dikonversi otomatis
130
+ - `emojis`: emoji untuk sticker terkait
131
+ - `accessibilityLabel`: label opsional
132
+ - `packId`: opsional, jika tidak diisi akan dibuat otomatis
133
+
134
+ ### Catatan
135
+
136
+ - Sangat disarankan install `sharp` agar konversi gambar ke WebP lebih stabil.
137
+ - Jika `sharp` tidak ada, library akan mencoba memakai `jimp`.
138
+ - Ukuran tiap sticker maksimal 1 MB.
139
+ - Jumlah sticker maksimal 60 per pack.
140
+
141
+ ---
142
+
71
143
  # Documentation
72
144
 
73
145
  - [Connecting Account](#connecting-account)
@@ -119,6 +191,7 @@ yarn add jagproject
119
191
  - [Audio Message](#audio-message)
120
192
  - [Image Message](#image-message)
121
193
  - [ViewOnce Message](#view-once-message)
194
+ - [Sticker Pack Message](#kirim-sticker-pack)
122
195
  - [Modify Messages](#modify-messages)
123
196
  - [Delete Messages (for everyone)](#deleting-messages-for-everyone)
124
197
  - [Edit Messages](#editing-messages)
@@ -1873,3 +1946,27 @@ Gunakan secara bertanggung jawab dan hindari aktivitas ilegal atau penyalahgunaa
1873
1946
 
1874
1947
  📘 *Documentation powered by jagoan project*
1875
1948
  🧑‍💻 **Modified and Presented by Jagoan Project**
1949
+
1950
+ <a id="kirim-sticker-pack"></a>
1951
+ ## Sticker Pack Message
1952
+
1953
+ Gunakan field `stickerPack` pada `sendMessage` untuk mengirim satu paket sticker sekaligus.
1954
+
1955
+ Catatan penting: `stickerPack` sekarang bisa menerima campuran sticker statis dan video/animated (`webp/png/jpg/jpeg/gif/webm/mp4`) lalu mengonversinya ke WebP otomatis saat proses kirim, selama `ffmpeg` tersedia di server. Format `tgs`/Lottie tetap butuh preview/konverter tambahan di luar runtime ini.
1956
+
1957
+ ```javascript
1958
+ await sock.sendMessage(jid, {
1959
+ stickerPack: {
1960
+ name: 'Nama Pack',
1961
+ publisher: 'Nama Publisher',
1962
+ description: 'Deskripsi singkat',
1963
+ cover: { url: './cover.png' },
1964
+ stickers: [
1965
+ { sticker: { url: './1.webp' }, emojis: ['🙂'] },
1966
+ { sticker: { url: './2.png' }, emojis: ['🎉'] }
1967
+ ]
1968
+ }
1969
+ })
1970
+ ```
1971
+
1972
+ `cover` dan setiap item `sticker` menerima format media yang sama seperti pengiriman media biasa: path lokal, URL, Buffer, atau stream.