@blurose/baileys 1.1.13 → 1.1.15

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 CHANGED
@@ -116,19 +116,27 @@ const sock = makeWASocket({
116
116
 
117
117
  If the connection is successful, you will see a QR code printed on your terminal screen. Scan it with WhatsApp on your phone to log in.
118
118
 
119
- ### Starting socket with **Pairing Code**
119
+ ### Starting socket with **Custom Pairing Code**
120
+
121
+ If you prefer letting users input their phone number via the terminal rather than scanning a QR code, you can build a custom pairing logic:
120
122
 
121
123
  ```ts
122
124
  import makeWASocket from '@blurose/baileys'
125
+ import readline from 'readline'
126
+
127
+ const question = (text) => {
128
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
129
+ return new Promise((resolve) => rl.question(text, resolve))
130
+ }
123
131
 
124
132
  const sock = makeWASocket({
125
133
  printQRInTerminal: false
126
134
  })
127
135
 
128
136
  if (!sock.authState.creds.registered) {
129
- const number = '628XXXXXXXXX' // Country code + phone number without + or spacing
130
- const code = await sock.requestPairingCode(number)
131
- console.log(code)
137
+ const number = await question('Masukkan nomor WhatsApp (contoh: 628xxx):\n')
138
+ const code = await sock.requestPairingCode(number.trim())
139
+ console.log(`Kode Pairing Kamu: ${code}`)
132
140
  }
133
141
  ```
134
142
 
@@ -189,6 +197,15 @@ sock.ev.on('messages.upsert', ({ messages }) => {
189
197
  })
190
198
  ```
191
199
 
200
+ ### Auto-Heal Pre-Key
201
+ ```ts
202
+ sock.ev.on('messages.upsert', async ({ messages }) => {
203
+ for (const msg of messages) {
204
+ if (msg.messageStubType === 2) sock.ws.close()
205
+ }
206
+ })
207
+ ```
208
+
192
209
  ---
193
210
 
194
211
  ## Implementing a Data Store
@@ -252,14 +269,24 @@ await sock.sendMessage(
252
269
  )
253
270
  ```
254
271
 
255
- ### Media Messages & Newsletter Sending
256
- To send media to WhatsApp Channels (Newsletters) using this fork, use the standard `sendMessage` function. The library will automatically bypass E2E encryption and route media to the correct CDN endpoints:
272
+ ### Upload Media to Channel
273
+
274
+ #### Without Waveform
275
+ ```ts
276
+ await sock.sendMessage(channelJid, {
277
+ audio: fs.readFileSync('media.ogg'),
278
+ mimetype: 'audio/ogg; codecs=opus',
279
+ ptt: false
280
+ })
281
+ ```
257
282
 
283
+ #### With Waveform
258
284
  ```ts
259
285
  await sock.sendMessage(channelJid, {
260
- audio: fs.readFileSync('media/audio.ogg'),
286
+ audio: fs.readFileSync('media.ogg'),
261
287
  mimetype: 'audio/ogg; codecs=opus',
262
- ptt: false // Set to false for channel media compatibility
288
+ ptt: true,
289
+ waveform: new Uint8Array(64)
263
290
  })
264
291
  ```
265
292
 
@@ -1,6 +1,6 @@
1
1
  import { proto } from '../../WAProto/index.js';
2
2
  import { WAMessageStubType } from '../Types/index.js';
3
- import { generateMessageID, generateMessageIDV2, unixTimestampSeconds } from '../Utils/index.js';
3
+ import { generateMessageID, unixTimestampSeconds } from '../Utils/index.js';
4
4
  import logger from '../Utils/logger.js';
5
5
  import { getBinaryNodeChild, getBinaryNodeChildren, getBinaryNodeChildString, jidEncode, jidNormalizedUser } from '../WABinary/index.js';
6
6
  import { makeBusinessSocket } from './business.js';
@@ -122,7 +122,7 @@ export const makeCommunitiesSocket = (config) => {
122
122
  return await parseGroupResult(result);
123
123
  },
124
124
  communityCreateGroup: async (subject, participants, parentCommunityJid) => {
125
- const key = generateMessageIDV2();
125
+ const key = generateMessageID();
126
126
  const result = await communityQuery('@g.us', 'set', [
127
127
  {
128
128
  tag: 'create',
@@ -350,7 +350,7 @@ export const makeCommunitiesSocket = (config) => {
350
350
  await upsertMessage({
351
351
  key: {
352
352
  remoteJid: inviteMessage.groupJid,
353
- id: generateMessageIDV2(sock.user?.id),
353
+ id: generateMessageID(sock.user?.id),
354
354
  fromMe: false,
355
355
  participant: key.remoteJid // TODO: investigate if this makes any sense at all
356
356
  },
@@ -1,7 +1,7 @@
1
1
  import { Boom } from '@hapi/boom';
2
2
  import { proto } from '../../WAProto/index.js';
3
3
  import { WAMessageAddressingMode, WAMessageStubType } from '../Types/index.js';
4
- import { generateMessageIDV2, unixTimestampSeconds } from '../Utils/index.js';
4
+ import { generateMessageID, unixTimestampSeconds } from '../Utils/index.js';
5
5
  import { getBinaryNodeChild, getBinaryNodeChildren, getBinaryNodeChildString, isLidUser, isPnUser, jidEncode, jidNormalizedUser } from '../WABinary/index.js';
6
6
  import { makeChatsSocket } from './chats.js';
7
7
  export const makeGroupsSocket = (config) => {
@@ -68,7 +68,7 @@ export const makeGroupsSocket = (config) => {
68
68
  ...sock,
69
69
  groupMetadata,
70
70
  groupCreate: async (subject, participants) => {
71
- const key = generateMessageIDV2();
71
+ const key = generateMessageID();
72
72
  const result = await groupQuery('@g.us', 'set', [
73
73
  {
74
74
  tag: 'create',
@@ -161,7 +161,7 @@ export const makeGroupsSocket = (config) => {
161
161
  {
162
162
  tag: 'description',
163
163
  attrs: {
164
- ...(description ? { id: generateMessageIDV2() } : { delete: 'true' }),
164
+ ...(description ? { id: generateMessageID() } : { delete: 'true' }),
165
165
  ...(prev ? { prev } : {})
166
166
  },
167
167
  content: description ? [{ tag: 'body', attrs: {}, content: Buffer.from(description, 'utf-8') }] : undefined
@@ -234,7 +234,7 @@ export const makeGroupsSocket = (config) => {
234
234
  await upsertMessage({
235
235
  key: {
236
236
  remoteJid: inviteMessage.groupJid,
237
- id: generateMessageIDV2(sock.user?.id),
237
+ id: generateMessageID(sock.user?.id),
238
238
  fromMe: false,
239
239
  participant: key.remoteJid
240
240
  },
@@ -2,7 +2,7 @@ import NodeCache from '@cacheable/node-cache';
2
2
  import { Boom } from '@hapi/boom';
3
3
  import { proto } from '../../WAProto/index.js';
4
4
  import { DEFAULT_CACHE_TTLS, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
5
- import { aggregateMessageKeysNotFromMe, assertMediaContent, assertMeId, bindWaitForEvent, decryptMediaRetryData, DEF_MEDIA_HOST, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateMessageIDV2, generateParticipantHashV2, generateWAMessage, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, MessageRetryManager, normalizeMessageContent, parseAndInjectE2ESessions, unixTimestampSeconds } from '../Utils/index.js';
5
+ import { aggregateMessageKeysNotFromMe, assertMediaContent, assertMeId, bindWaitForEvent, decryptMediaRetryData, DEF_MEDIA_HOST, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateMessageID, generateParticipantHashV2, generateWAMessage, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, MessageRetryManager, normalizeMessageContent, parseAndInjectE2ESessions, unixTimestampSeconds } from '../Utils/index.js';
6
6
  import { getUrlInfo } from '../Utils/link-preview.js';
7
7
  import { makeKeyedMutex, makeMutex } from '../Utils/make-mutex.js';
8
8
  import { getMessageReportingToken, shouldIncludeReportingToken } from '../Utils/reporting-utils.js';
@@ -438,7 +438,7 @@ export const makeMessagesSocket = (config) => {
438
438
  const isNewsletter = server === 'newsletter';
439
439
  const isGroupOrStatus = isGroup || isStatus;
440
440
  const finalJid = jid;
441
- msgId = msgId || generateMessageIDV2(meId);
441
+ msgId = msgId || generateMessageID(meId);
442
442
  useUserDevicesCache = useUserDevicesCache !== false;
443
443
  useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus;
444
444
  const participants = [];
@@ -1083,7 +1083,7 @@ export const makeMessagesSocket = (config) => {
1083
1083
  upload: waUploadToServer,
1084
1084
  mediaCache: config.mediaCache,
1085
1085
  options: config.options,
1086
- messageId: generateMessageIDV2(sock.user?.id),
1086
+ messageId: generateMessageID(sock.user?.id),
1087
1087
  ...options
1088
1088
  });
1089
1089
  const isEventMsg = 'event' in content && !!content.event;
@@ -4,7 +4,7 @@ import { createWriteStream, promises as fs } from 'fs';
4
4
  import { tmpdir } from 'os';
5
5
  import { join } from 'path';
6
6
  import { getBinaryNodeChild, getBinaryNodeChildren, getBinaryNodeChildString } from '../WABinary/index.js';
7
- import { generateMessageIDV2 } from './generics.js';
7
+ import { generateMessageID } from './generics.js';
8
8
  import { getStream, getUrlFromDirectPath } from './messages-media.js';
9
9
  export const parseCatalogNode = (node) => {
10
10
  const catalogNode = getBinaryNodeChild(node, 'product_catalog');
@@ -197,7 +197,7 @@ export const uploadingNecessaryImages = async (images, waUploadToServer, timeout
197
197
  }
198
198
  const { stream } = await getStream(img);
199
199
  const hasher = createHash('sha256');
200
- const filePath = join(tmpdir(), 'img' + generateMessageIDV2());
200
+ const filePath = join(tmpdir(), 'img' + generateMessageID());
201
201
  const encFileWriteStream = createWriteStream(filePath);
202
202
  for await (const block of stream) {
203
203
  hasher.update(block);
@@ -30,8 +30,7 @@ export declare const delayCancellable: (ms: number) => {
30
30
  cancel: () => void;
31
31
  };
32
32
  export declare function promiseTimeout<T>(ms: number | undefined, promise: (resolve: (v: T) => void, reject: (error: any) => void) => void): Promise<T>;
33
- export declare const generateMessageIDV2: (userId?: string) => string;
34
- export declare const generateMessageID: () => string;
33
+ export declare const generateMessageID: (userId?: string) => string;
35
34
  export declare function bindWaitForEvent<T extends keyof BaileysEventMap>(ev: BaileysEventEmitter, event: T): (check: (u: BaileysEventMap[T]) => Promise<boolean | undefined>, timeoutMs?: number) => Promise<void>;
36
35
  export declare const bindWaitForConnectionUpdate: (ev: BaileysEventEmitter) => (check: (u: Partial<ConnectionState>) => Promise<boolean | undefined>, timeoutMs?: number) => Promise<void>;
37
36
  /**
@@ -131,23 +131,22 @@ export async function promiseTimeout(ms, promise) {
131
131
  }
132
132
  // inspired from whatsmeow code
133
133
  // https://github.com/tulir/whatsmeow/blob/64bc969fbe78d31ae0dd443b8d4c80a5d026d07a/send.go#L42
134
- export const generateMessageIDV2 = (userId) => {
135
- const data = Buffer.alloc(8 + 20 + 16);
136
- data.writeBigUInt64BE(BigInt(Math.floor(Date.now() / 1000)));
134
+ export const generateMessageID = (userId) => {
137
135
  if (userId) {
136
+ const data = Buffer.alloc(8 + 20 + 16);
137
+ data.writeBigUInt64BE(BigInt(Math.floor(Date.now() / 1000)));
138
138
  const id = jidDecode(userId);
139
139
  if (id?.user) {
140
140
  data.write(id.user, 8);
141
141
  data.write('@c.us', 8 + id.user.length);
142
142
  }
143
+ const random = randomBytes(16);
144
+ random.copy(data, 28);
145
+ const hash = createHash('sha256').update(data).digest();
146
+ return '3EB0' + hash.toString('hex').toUpperCase().substring(0, 18);
143
147
  }
144
- const random = randomBytes(16);
145
- random.copy(data, 28);
146
- const hash = createHash('sha256').update(data).digest();
147
- return '3EB0' + hash.toString('hex').toUpperCase().substring(0, 18);
148
+ return '3EB0' + randomBytes(18).toString('hex').toUpperCase();
148
149
  };
149
- // generate a random ID to attach to a message
150
- export const generateMessageID = () => '3EB0' + randomBytes(18).toString('hex').toUpperCase();
151
150
  export function bindWaitForEvent(ev, event) {
152
151
  return async (check, timeoutMs) => {
153
152
  let listener;
@@ -11,7 +11,7 @@ import { proto } from '../../WAProto/index.js';
11
11
  import { DEFAULT_ORIGIN, MEDIA_HKDF_KEY_MAPPING, MEDIA_PATH_MAP } from '../Defaults/index.js';
12
12
  import { getBinaryNodeChild, getBinaryNodeChildBuffer, jidNormalizedUser } from '../WABinary/index.js';
13
13
  import { aesDecryptGCM, aesEncryptGCM, hkdf } from './crypto.js';
14
- import { generateMessageIDV2 } from './generics.js';
14
+ import { generateMessageID } from './generics.js';
15
15
  const getTmpFilesDirectory = () => tmpdir();
16
16
  const getImageProcessingLibrary = async () => {
17
17
  //@ts-ignore
@@ -32,7 +32,7 @@ export const getRawMediaUploadData = async (media, mediaType, logger) => {
32
32
  const { stream } = await getStream(media);
33
33
  logger?.debug('got stream for raw upload');
34
34
  const hasher = Crypto.createHash('sha256');
35
- const filePath = join(tmpdir(), mediaType + generateMessageIDV2());
35
+ const filePath = join(tmpdir(), mediaType + generateMessageID());
36
36
  const fileWriteStream = createWriteStream(filePath);
37
37
  let fileLength = 0;
38
38
  try {
@@ -287,7 +287,7 @@ export async function generateThumbnail(file, mediaType, options) {
287
287
  }
288
288
  }
289
289
  else if (mediaType === 'video') {
290
- const imgFilename = join(getTmpFilesDirectory(), generateMessageIDV2() + '.jpg');
290
+ const imgFilename = join(getTmpFilesDirectory(), generateMessageID() + '.jpg');
291
291
  try {
292
292
  await extractVideoThumb(file, imgFilename, '00:00:00', { width: 32, height: 32 });
293
293
  const buff = await fs.readFile(imgFilename);
@@ -320,12 +320,12 @@ export const encryptedStream = async (media, mediaType, { logger, saveOriginalFi
320
320
  logger?.debug('fetched media stream');
321
321
  const mediaKey = Crypto.randomBytes(32);
322
322
  const { cipherKey, iv, macKey } = await getMediaKeys(mediaKey, mediaType);
323
- const encFilePath = join(getTmpFilesDirectory(), mediaType + generateMessageIDV2() + '-enc');
323
+ const encFilePath = join(getTmpFilesDirectory(), mediaType + generateMessageID() + '-enc');
324
324
  const encFileWriteStream = createWriteStream(encFilePath);
325
325
  let originalFileStream;
326
326
  let originalFilePath;
327
327
  if (saveOriginalFileIfRequired) {
328
- originalFilePath = join(getTmpFilesDirectory(), mediaType + generateMessageIDV2() + '-original');
328
+ originalFilePath = join(getTmpFilesDirectory(), mediaType + generateMessageID() + '-original');
329
329
  originalFileStream = createWriteStream(originalFilePath);
330
330
  }
331
331
  let fileLength = 0;
@@ -7,7 +7,7 @@ import { CALL_AUDIO_PREFIX, CALL_VIDEO_PREFIX, MEDIA_KEYS, URL_REGEX, WA_DEFAULT
7
7
  import { WAMessageStatus, WAProto } from '../Types/index.js';
8
8
  import { isJidGroup, isJidNewsletter, isJidStatusBroadcast, jidNormalizedUser } from '../WABinary/index.js';
9
9
  import { sha256 } from './crypto.js';
10
- import { generateMessageIDV2, getKeyAuthor, unixTimestampSeconds } from './generics.js';
10
+ import { generateMessageID, getKeyAuthor, unixTimestampSeconds } from './generics.js';
11
11
  import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getRawMediaUploadData } from './messages-media.js';
12
12
  import { shouldIncludeReportingToken } from './reporting-utils.js';
13
13
  const MIMETYPE_MAP = {
@@ -692,7 +692,7 @@ export const generateWAMessageFromContent = (jid, message, options) => {
692
692
  key: {
693
693
  remoteJid: jid,
694
694
  fromMe: true,
695
- id: options?.messageId || generateMessageIDV2()
695
+ id: options?.messageId || generateMessageID()
696
696
  },
697
697
  message: message,
698
698
  messageTimestamp: timestamp,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blurose/baileys",
3
3
  "type": "module",
4
- "version": "1.1.13",
4
+ "version": "1.1.15",
5
5
  "description": "A WebSockets library for interacting with WhatsApp Web",
6
6
  "keywords": [
7
7
  "whatsapp",