@blckrose/baileys 1.0.0 → 1.1.1

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.
@@ -0,0 +1,54 @@
1
+ import EventEmitter from 'events';
2
+ import { createReadStream } from 'fs';
3
+ import { writeFile } from 'fs/promises';
4
+ import { createInterface } from 'readline';
5
+ import { delay } from './generics.js';
6
+ import { makeMutex } from './make-mutex.js';
7
+
8
+ /**
9
+ * Captures events from a baileys event emitter & stores them in a file
10
+ * @param ev The event emitter to read events from
11
+ * @param filename File to save to
12
+ */
13
+ export const captureEventStream = (ev, filename) => {
14
+ const oldEmit = ev.emit;
15
+ // write mutex so data is appended in order
16
+ const writeMutex = makeMutex();
17
+ // monkey patch eventemitter to capture all events
18
+ ev.emit = function (...args) {
19
+ const content = JSON.stringify({ timestamp: Date.now(), event: args[0], data: args[1] }) + '\n';
20
+ const result = oldEmit.apply(ev, args);
21
+ writeMutex.mutex(async () => {
22
+ await writeFile(filename, content, { flag: 'a' });
23
+ });
24
+ return result;
25
+ };
26
+ };
27
+
28
+ /**
29
+ * Read event file and emit events from there
30
+ * @param filename filename containing event data
31
+ * @param delayIntervalMs delay between each event emit
32
+ */
33
+ export const readAndEmitEventStream = (filename, delayIntervalMs = 0) => {
34
+ const ev = new EventEmitter();
35
+ const fireEvents = async () => {
36
+ const fileStream = createReadStream(filename);
37
+ const rl = createInterface({
38
+ input: fileStream,
39
+ crlfDelay: Infinity
40
+ });
41
+ for await (const line of rl) {
42
+ if (line) {
43
+ const { event, data } = JSON.parse(line);
44
+ ev.emit(event, data);
45
+ delayIntervalMs && await delay(delayIntervalMs);
46
+ }
47
+ }
48
+ fileStream.close();
49
+ };
50
+ return {
51
+ ev,
52
+ task: fireEvents()
53
+ };
54
+ };
@@ -1,28 +1,41 @@
1
- import { platform, release } from 'os';
1
+ import { platform } from 'os';
2
2
  import { proto } from '../../WAProto/index.js';
3
- const PLATFORM_MAP = {
4
- aix: 'AIX',
5
- darwin: 'Mac OS',
6
- win32: 'Windows',
7
- android: 'Android',
8
- freebsd: 'FreeBSD',
9
- openbsd: 'OpenBSD',
10
- sunos: 'Solaris',
11
- linux: undefined,
12
- haiku: undefined,
13
- cygwin: undefined,
14
- netbsd: undefined
3
+ export const PLATFORM_MAP = {
4
+ 'aix':'AIX','darwin':'Mac OS','win32':'Windows','android':'Android',
5
+ 'freebsd':'FreeBSD','openbsd':'OpenBSD','sunos':'Solaris','linux':'Linux',
6
+ 'ubuntu':'Ubuntu','ios':'iOS','baileys':'Baileys','chromeos':'Chrome OS',
7
+ 'tizen':'Tizen','watchos':'watchOS','wearos':'Wear OS','harmonyos':'HarmonyOS',
8
+ 'kaios':'KaiOS','smarttv':'Smart TV','raspberrypi':'Raspberry Pi OS',
9
+ 'symbian':'Symbian','blackberry':'Blackberry OS','windowsphone':'Windows Phone'
10
+ };
11
+ export const PLATFORM_VERSIONS = {
12
+ 'ubuntu':'22.04.4','darwin':'18.5','win32':'10.0.22631','android':'14.0.0',
13
+ 'freebsd':'13.2','openbsd':'7.3','sunos':'11','linux':'6.5','ios':'18.2',
14
+ 'baileys':'6.5.0','chromeos':'117.0.5938.132','tizen':'6.5','watchos':'10.1',
15
+ 'wearos':'4.1','harmonyos':'4.0.0','kaios':'3.1','smarttv':'23.3.1',
16
+ 'raspberrypi':'11 (Bullseye)','symbian':'3','blackberry':'10.3.3','windowsphone':'8.1'
15
17
  };
16
18
  export const Browsers = {
17
- ubuntu: browser => ['Ubuntu', browser, '22.04.4'],
18
- macOS: browser => ['Mac OS', browser, '14.4.1'],
19
- baileys: browser => ['Baileys', browser, '6.5.0'],
20
- windows: browser => ['Windows', browser, '10.0.22631'],
21
- /** The appropriate browser based on your OS & release */
22
- appropriate: browser => [PLATFORM_MAP[platform()] || 'Ubuntu', browser, release()]
19
+ ubuntu: (browser) => [PLATFORM_MAP['ubuntu'], browser, PLATFORM_VERSIONS['ubuntu']],
20
+ macOS: (browser) => [PLATFORM_MAP['darwin'], browser, PLATFORM_VERSIONS['darwin']],
21
+ windows: (browser) => [PLATFORM_MAP['win32'], browser, PLATFORM_VERSIONS['win32']],
22
+ linux: (browser) => [PLATFORM_MAP['linux'], browser, PLATFORM_VERSIONS['linux']],
23
+ solaris: (browser) => [PLATFORM_MAP['sunos'], browser, PLATFORM_VERSIONS['sunos']],
24
+ baileys: (browser) => [PLATFORM_MAP['baileys'], browser, PLATFORM_VERSIONS['baileys']],
25
+ android: (browser) => [PLATFORM_MAP['android'], browser, PLATFORM_VERSIONS['android']],
26
+ iOS: (browser) => [PLATFORM_MAP['ios'], browser, PLATFORM_VERSIONS['ios']],
27
+ kaiOS: (browser) => [PLATFORM_MAP['kaios'], browser, PLATFORM_VERSIONS['kaios']],
28
+ chromeOS: (browser) => [PLATFORM_MAP['chromeos'], browser, PLATFORM_VERSIONS['chromeos']],
29
+ appropriate: (browser) => {
30
+ const p = platform();
31
+ return [PLATFORM_MAP[p] || 'Unknown OS', browser, PLATFORM_VERSIONS[p] || 'latest'];
32
+ },
33
+ custom: (p, browser, version) => {
34
+ return [PLATFORM_MAP[p.toLowerCase()] || p, browser, version || PLATFORM_VERSIONS[p] || 'latest'];
35
+ }
23
36
  };
24
37
  export const getPlatformId = (browser) => {
25
38
  const platformType = proto.DeviceProps.PlatformType[browser.toUpperCase()];
26
- return platformType ? platformType.toString() : '1'; //chrome
39
+ return platformType ? platformType.toString() : '1';
27
40
  };
28
41
  //# sourceMappingURL=browser-utils.js.map
@@ -1,5 +1,6 @@
1
1
  import { Boom } from '@hapi/boom';
2
2
  import { createHash, randomBytes } from 'crypto';
3
+ import axios from 'axios';
3
4
  import { proto } from '../../WAProto/index.js';
4
5
  const baileysVersion = [2, 3000, 1033105955];
5
6
  import { DisconnectReason } from '../Types/index.js';
@@ -381,4 +382,30 @@ export function bytesToCrockford(buffer) {
381
382
  export function encodeNewsletterMessage(message) {
382
383
  return proto.Message.encode(message).finish();
383
384
  }
385
+
386
+ export const COMPANION_PLATFORM_MAP = {
387
+ 'Chrome': '49', 'Edge': '50', 'Firefox': '51', 'Opera': '53', 'Safari': '54',
388
+ 'Brave': '1.79.112', 'Vivaldi': '6.2.3105.58', 'Tor': '12.5.3',
389
+ 'Yandex': '23.7.1', 'Falkon': '22.08.3', 'Epiphany': '44.2'
390
+ };
391
+ // Browsers, PLATFORM_MAP, PLATFORM_VERSIONS, getPlatformId moved to browser-utils.js
392
+ export const printQRIfNecessaryListener = (ev, logger) => {
393
+ ev.on('connection.update', async ({ qr }) => {
394
+ if (qr) {
395
+ const QR = await import('qrcode-terminal').then(m => m.default || m).catch(() => {
396
+ logger.error('QR code terminal not added as dependency');
397
+ });
398
+ QR?.generate(qr, { small: true });
399
+ }
400
+ });
401
+ };
402
+ export const toUnicodeEscape = (text) =>
403
+ text.split('').map(c => '\\u' + c.charCodeAt(0).toString(16).padStart(4, '0')).join('');
404
+ export const fromUnicodeEscape = (escapedText) =>
405
+ escapedText.replace(/\\u[\dA-Fa-f]{4}/g, m => String.fromCharCode(parseInt(m.slice(2), 16)));
406
+ export const asciiEncode = (text) => text.split('').map(c => c.charCodeAt(0));
407
+ export const asciiDecode = (...codes) => {
408
+ const arr = Array.isArray(codes[0]) ? codes[0] : codes;
409
+ return arr.map(c => String.fromCharCode(c)).join('');
410
+ };
384
411
  //# sourceMappingURL=generics.js.map
@@ -1,4 +1,6 @@
1
1
  export * from './generics.js';
2
+ export * from './audioToBuffer.js';
3
+ export * from './streamToBuffer.js';
2
4
  export * from './decode-wa-message.js';
3
5
  export * from './messages.js';
4
6
  export * from './messages-media.js';
@@ -11,6 +13,9 @@ export * from './chat-utils.js';
11
13
  export * from './lt-hash.js';
12
14
  export * from './auth-utils.js';
13
15
  export * from './use-multi-file-auth-state.js';
16
+ export * from './use-single-file-auth-state.js';
17
+ export * from './use-mongo-file-auth-state.js';
18
+ export * from './baileys-event-stream.js';
14
19
  export * from './link-preview.js';
15
20
  export * from './event-buffer.js';
16
21
  export * from './process-message.js';
@@ -1,7 +1,9 @@
1
+ import { spawn } from 'child_process';
2
+ import { once } from 'events';
3
+ import { unlink as unlinkFs } from 'fs/promises';
1
4
  import { Boom } from '@hapi/boom';
2
5
  import { exec } from 'child_process';
3
6
  import * as Crypto from 'crypto';
4
- import { once } from 'events';
5
7
  import { createReadStream, createWriteStream, promises as fs, WriteStream } from 'fs';
6
8
  import { tmpdir } from 'os';
7
9
  import { join } from 'path';
@@ -36,7 +38,7 @@ export const getRawMediaUploadData = async (media, mediaType, logger) => {
36
38
  const fileWriteStream = createWriteStream(filePath);
37
39
  let fileLength = 0;
38
40
  try {
39
- for await (const data of stream) {
41
+ for await (const data of finalStream) {
40
42
  fileLength += data.length;
41
43
  hasher.update(data);
42
44
  if (!fileWriteStream.write(data)) {
@@ -306,9 +308,53 @@ export const getHttpStream = async (url, options = {}) => {
306
308
  // @ts-ignore Node18+ Readable.fromWeb exists
307
309
  return response.body instanceof Readable ? response.body : Readable.fromWeb(response.body);
308
310
  };
309
- export const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
311
+
312
+ // ── convertToOpusBuffer (ported from elaina) ─────────────────────────────────
313
+ const convertToOpusBuffer = (buffer, logger) => new Promise((resolve, reject) => {
314
+ const args = [
315
+ '-i', 'pipe:0',
316
+ '-c:a', 'libopus',
317
+ '-b:a', '64k',
318
+ '-vbr', 'on',
319
+ '-compression_level', '10',
320
+ '-frame_duration', '20',
321
+ '-application', 'voip',
322
+ '-f', 'ogg',
323
+ 'pipe:1'
324
+ ];
325
+ const ffmpeg = spawn('ffmpeg', args);
326
+ const chunks = [];
327
+ ffmpeg.stdin.write(buffer);
328
+ ffmpeg.stdin.end();
329
+ ffmpeg.stdout.on('data', chunk => chunks.push(chunk));
330
+ ffmpeg.stderr.on('data', () => {});
331
+ ffmpeg.on('close', code => {
332
+ if (code === 0) resolve(Buffer.concat(chunks));
333
+ else reject(new Error(`FFmpeg Opus conversion exited with code ${code}`));
334
+ });
335
+ ffmpeg.on('error', err => reject(err));
336
+ });
337
+ // ── End convertToOpusBuffer ───────────────────────────────────────────────────
338
+
339
+ export const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts, isPtt, forceOpus } = {}) => {
310
340
  const { stream, type } = await getStream(media, opts);
311
341
  logger?.debug('fetched media stream');
342
+ let finalStream = stream;
343
+ let opusConverted = false;
344
+ // Auto-convert to Opus if PTT or forceOpus
345
+ if (mediaType === 'audio' && (isPtt === true || forceOpus === true)) {
346
+ try {
347
+ const buf = await toBuffer(finalStream);
348
+ const opusBuf = await convertToOpusBuffer(buf, logger);
349
+ finalStream = toReadable(opusBuf);
350
+ opusConverted = true;
351
+ logger?.debug('converted audio to Opus for PTT');
352
+ } catch (error) {
353
+ logger?.error('failed to convert audio to Opus, fallback to original');
354
+ const { stream: newStream } = await getStream(media, opts);
355
+ finalStream = newStream;
356
+ }
357
+ }
312
358
  const mediaKey = Crypto.randomBytes(32);
313
359
  const { cipherKey, iv, macKey } = await getMediaKeys(mediaKey, mediaType);
314
360
  const encFilePath = join(getTmpFilesDirectory(), mediaType + generateMessageIDV2() + '-enc');
@@ -373,7 +419,8 @@ export const encryptedStream = async (media, mediaType, { logger, saveOriginalFi
373
419
  mac,
374
420
  fileEncSha256,
375
421
  fileSha256,
376
- fileLength
422
+ fileLength,
423
+ opusConverted
377
424
  };
378
425
  }
379
426
  catch (error) {
@@ -771,4 +818,56 @@ const MEDIA_RETRY_STATUS_MAP = {
771
818
  [proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
772
819
  [proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418
773
820
  };
821
+
822
+ export const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts, isPtt, forceOpus } = {}) => {
823
+ const { stream, type } = await getStream(media, opts);
824
+ logger?.debug('fetched media stream');
825
+ let opusConverted = false;
826
+ let buffer = await toBuffer(stream);
827
+ // Auto-convert to Opus if PTT or forceOpus
828
+ if (mediaType === 'audio' && (isPtt === true || forceOpus === true)) {
829
+ try {
830
+ const opusBuf = await convertToOpusBuffer(buffer, logger);
831
+ buffer = opusBuf;
832
+ opusConverted = true;
833
+ logger?.debug('converted audio to Opus for newsletter PTT');
834
+ } catch (e) {
835
+ logger?.error('failed to convert audio for newsletter PTT');
836
+ }
837
+ }
838
+ const encFilePath = join(tmpdir(), mediaType + generateMessageID() + '-plain');
839
+ const encFileWriteStream = createWriteStream(encFilePath);
840
+ let originalFilePath;
841
+ let originalFileStream;
842
+ if (type === 'file') {
843
+ originalFilePath = media.url.toString();
844
+ } else if (saveOriginalFileIfRequired) {
845
+ originalFilePath = join(tmpdir(), mediaType + generateMessageID() + '-original');
846
+ originalFileStream = createWriteStream(originalFilePath);
847
+ }
848
+ let fileLength = 0;
849
+ const hashCtx = createHashCrypto('sha256');
850
+ try {
851
+ // Use buffer (possibly opus-converted) directly
852
+ fileLength = buffer.length;
853
+ hashCtx.update(buffer);
854
+ encFileWriteStream.write(buffer);
855
+ if (originalFileStream) {
856
+ originalFileStream.write(buffer);
857
+ }
858
+ const fileSha256 = hashCtx.digest();
859
+ encFileWriteStream.end();
860
+ originalFileStream?.end?.call(originalFileStream);
861
+ logger?.debug('prepared plain stream successfully');
862
+ return { mediaKey: undefined, originalFilePath, encFilePath, mac: undefined, fileEncSha256: undefined, fileSha256, fileLength, opusConverted };
863
+ } catch (error) {
864
+ encFileWriteStream.destroy();
865
+ originalFileStream?.destroy?.call(originalFileStream);
866
+ stream.destroy();
867
+ try {
868
+ await unlinkFs(encFilePath);
869
+ } catch (err) { logger?.error({ err }, 'failed deleting tmp files'); }
870
+ throw error;
871
+ }
872
+ };
774
873
  //# sourceMappingURL=messages-media.js.map