@alannxd/baileys 6.1.4 → 6.1.7

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.
Files changed (55) hide show
  1. package/LICENSE +4 -1
  2. package/README.md +205 -281
  3. package/lib/Defaults/index.js +1 -1
  4. package/lib/Socket/chats.js +5 -0
  5. package/lib/Socket/graphql.js +716 -0
  6. package/lib/Socket/index.js +9 -1
  7. package/lib/Socket/interop.js +463 -0
  8. package/lib/Socket/message-builder.js +475 -0
  9. package/lib/Socket/messages-recv.js +1 -1
  10. package/lib/Socket/messages-send.js +106 -30
  11. package/lib/Socket/mex.js +51 -39
  12. package/lib/Socket/newsletter.js +729 -242
  13. package/lib/Socket/privacy.js +318 -0
  14. package/lib/Socket/socket.js +163 -16
  15. package/lib/Socket/username.js +225 -136
  16. package/lib/Store/make-in-memory-store.js +47 -1
  17. package/lib/Types/Mex.js +76 -2
  18. package/lib/Utils/index.js +1 -0
  19. package/lib/Utils/messages-media.js +82 -0
  20. package/lib/Utils/messages.js +143 -2
  21. package/lib/Utils/sticker.js +133 -0
  22. package/lib/Utils/use-sqlite-auth-state.js +162 -0
  23. package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +53 -0
  24. package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +43 -0
  25. package/lib/WAUSync/Protocols/USyncContactProtocol.js +52 -50
  26. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +55 -52
  27. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +28 -25
  28. package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +58 -0
  29. package/lib/WAUSync/Protocols/USyncLIDProtocol.js +30 -0
  30. package/lib/WAUSync/Protocols/USyncPictureProtocol.js +32 -0
  31. package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +28 -0
  32. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +37 -36
  33. package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +38 -0
  34. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -23
  35. package/lib/WAUSync/Protocols/index.js +12 -6
  36. package/lib/WAUSync/USyncQuery.js +149 -96
  37. package/lib/WAUSync/USyncUser.js +54 -29
  38. package/lib/WAUSync/index.js +3 -4
  39. package/lib/index.js +2 -3
  40. package/package.json +20 -21
  41. package/WAProto/fix-import.js +0 -29
  42. package/lib/Defaults/baileys-version.json +0 -3
  43. package/lib/Defaults/phonenumber-mcc.json +0 -223
  44. package/lib/Signal/Group/queue-job.js +0 -57
  45. package/lib/Socket/Client/abstract-socket-client.js +0 -13
  46. package/lib/Socket/Client/mobile-socket-client.js +0 -65
  47. package/lib/Socket/Client/web-socket-client.js +0 -62
  48. package/lib/Socket/community.js +0 -392
  49. package/lib/Socket/dugong.js +0 -637
  50. package/lib/Socket/registration.js +0 -167
  51. package/lib/Socket/usync.js +0 -69
  52. package/lib/Types/Newsletter.js +0 -38
  53. package/lib/Utils/baileys-event-stream.js +0 -63
  54. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +0 -51
  55. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +0 -29
@@ -8,7 +8,8 @@ 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
10
  import { generateMessageIDV2, getKeyAuthor, unixTimestampSeconds } from './generics.js';
11
- import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getRawMediaUploadData } from './messages-media.js';
11
+ import { buildZipArchive, downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getRawMediaUploadData, resolveStickerBuffer } from './messages-media.js';
12
+ import { makeSticker, convertToTrayIcon, isLikelyVideoBuffer } from './sticker.js';
12
13
  import { shouldIncludeReportingToken } from './reporting-utils.js';
13
14
  const MIMETYPE_MAP = {
14
15
  image: 'image/jpeg',
@@ -18,6 +19,34 @@ const MIMETYPE_MAP = {
18
19
  sticker: 'image/webp',
19
20
  'product-catalog-image': 'image/jpeg'
20
21
  };
22
+ const RAW_PROTO_MESSAGE_KEYS = [
23
+ 'conversation', 'senderKeyDistributionMessage', 'imageMessage', 'contactMessage', 'locationMessage',
24
+ 'extendedTextMessage', 'documentMessage', 'audioMessage', 'videoMessage', 'call', 'chat', 'protocolMessage',
25
+ 'contactsArrayMessage', 'highlyStructuredMessage', 'fastRatchetKeySenderKeyDistributionMessage',
26
+ 'sendPaymentMessage', 'liveLocationMessage', 'requestPaymentMessage', 'declinePaymentRequestMessage',
27
+ 'cancelPaymentRequestMessage', 'templateMessage', 'stickerMessage', 'groupInviteMessage',
28
+ 'templateButtonReplyMessage', 'productMessage', 'deviceSentMessage', 'messageContextInfo', 'listMessage',
29
+ 'viewOnceMessage', 'orderMessage', 'listResponseMessage', 'ephemeralMessage', 'invoiceMessage',
30
+ 'buttonsMessage', 'buttonsResponseMessage', 'paymentInviteMessage', 'interactiveMessage', 'reactionMessage',
31
+ 'stickerSyncRmrMessage', 'interactiveResponseMessage', 'pollCreationMessage', 'pollUpdateMessage',
32
+ 'keepInChatMessage', 'documentWithCaptionMessage', 'requestPhoneNumberMessage', 'viewOnceMessageV2',
33
+ 'encReactionMessage', 'editedMessage', 'viewOnceMessageV2Extension', 'pollCreationMessageV2',
34
+ 'scheduledCallCreationMessage', 'groupMentionedMessage', 'pinInChatMessage', 'pollCreationMessageV3',
35
+ 'scheduledCallEditMessage', 'ptvMessage', 'botInvokeMessage', 'callLogMesssage', 'messageHistoryBundle',
36
+ 'encCommentMessage', 'bcallMessage', 'lottieStickerMessage', 'eventMessage', 'encEventResponseMessage',
37
+ 'commentMessage', 'newsletterAdminInviteMessage', 'placeholderMessage', 'secretEncryptedMessage',
38
+ 'albumMessage', 'eventCoverImage', 'stickerPackMessage', 'statusMentionMessage', 'pollResultSnapshotMessage',
39
+ 'pollCreationOptionImageMessage', 'associatedChildMessage', 'groupStatusMentionMessage',
40
+ 'pollCreationMessageV4', 'statusAddYours', 'groupStatusMessage', 'richResponseMessage',
41
+ 'statusNotificationMessage', 'limitSharingMessage', 'botTaskMessage', 'questionMessage',
42
+ 'messageHistoryNotice', 'groupStatusMessageV2', 'botForwardedMessage', 'statusQuestionAnswerMessage',
43
+ 'questionReplyMessage', 'questionResponseMessage', 'statusQuotedMessage', 'statusStickerInteractionMessage',
44
+ 'pollCreationMessageV5', 'newsletterFollowerInviteMessageV2', 'pollResultSnapshotMessageV3',
45
+ 'newsletterAdminProfileMessage', 'newsletterAdminProfileMessageV2', 'spoilerMessage',
46
+ 'pollCreationMessageV6', 'conditionalRevealMessage', 'pollAddOptionMessage', 'eventInviteMessage',
47
+ 'groupRootKeyShare', 'paymentReminderMessage', 'splitPaymentMessage', 'newsletterAdminProfileStatusMessage',
48
+ 'rootSecretDistributeMessage'
49
+ ];
21
50
  const MessageTypeProto = {
22
51
  image: WAProto.Message.ImageMessage,
23
52
  video: WAProto.Message.VideoMessage,
@@ -633,6 +662,13 @@ export const generateWAMessageContent = async (message, options) => {
633
662
  }
634
663
  })
635
664
  }
665
+ else if ('botInvoke' in message && !!message.botInvoke) {
666
+ m = {
667
+ botInvokeMessage: {
668
+ message: message.botInvoke
669
+ }
670
+ }
671
+ }
636
672
  else if (hasNonNullishProperty(message, 'text')) {
637
673
  const extContent = { text: message.text };
638
674
  let urlInfo = message.linkPreview;
@@ -845,8 +881,113 @@ export const generateWAMessageContent = async (message, options) => {
845
881
  }
846
882
  };
847
883
  }
884
+ else if (hasNonNullishProperty(message, 'stickerPacks')) {
885
+ const pack = message.stickerPacks;
886
+ const rawStickers = Array.isArray(pack.stickers) ? pack.stickers : [];
887
+ if (rawStickers.length === 0) {
888
+ throw new Boom('stickerPacks.stickers must have at least one sticker', { statusCode: 400 });
889
+ }
890
+ const packName = pack.name || 'Sticker Pack';
891
+ const publisher = pack.publisher || pack.author || '';
892
+ // each entry can be raw sticker media, or { image, animated, emojis, accessibilityLabel, quality }
893
+ const preparedStickers = await Promise.all(rawStickers.map(async (entry, i) => {
894
+ const isWrapped = entry && typeof entry === 'object' && !Buffer.isBuffer(entry) && 'image' in entry;
895
+ const rawBuffer = await resolveStickerBuffer(isWrapped ? entry.image : entry, options.options);
896
+ const animated = isWrapped ? !!entry.animated : false;
897
+ const emojis = (isWrapped && entry.emojis) || [];
898
+ const webp = await makeSticker(rawBuffer, {
899
+ pack: packName,
900
+ author: publisher,
901
+ emojis,
902
+ animated: animated || isLikelyVideoBuffer(rawBuffer),
903
+ quality: (isWrapped && entry.quality) || 60
904
+ });
905
+ return {
906
+ fileName: `sticker_${i}.webp`,
907
+ data: webp,
908
+ isAnimated: animated,
909
+ emojis,
910
+ accessibilityLabel: isWrapped ? entry.accessibilityLabel : undefined,
911
+ isLottie: false,
912
+ mimetype: 'image/webp'
913
+ };
914
+ }));
915
+ const trayIconFileName = 'tray.png';
916
+ const trayIconPng = await convertToTrayIcon(await resolveStickerBuffer(
917
+ (rawStickers[0] && typeof rawStickers[0] === 'object' && !Buffer.isBuffer(rawStickers[0]) && 'image' in rawStickers[0])
918
+ ? rawStickers[0].image
919
+ : rawStickers[0],
920
+ options.options
921
+ ));
922
+ const stickerPackId = pack.stickerPackId || randomUUID();
923
+ const zipEntries = [
924
+ ...preparedStickers.map(s => ({ name: s.fileName, data: s.data })),
925
+ { name: trayIconFileName, data: trayIconPng }
926
+ ];
927
+ const archive = buildZipArchive(zipEntries);
928
+ // reuse the normal, well-tested media upload path to encrypt+upload the archive,
929
+ // then repurpose the resulting media fields into the stickerPackMessage itself
930
+ const uploaded = await prepareWAMessageMedia({
931
+ document: archive,
932
+ mimetype: 'application/zip',
933
+ fileName: `${stickerPackId}.zip`
934
+ }, options);
935
+ const doc = uploaded.documentMessage;
936
+ m = {
937
+ stickerPackMessage: {
938
+ stickerPackId,
939
+ name: packName,
940
+ publisher,
941
+ packDescription: pack.description,
942
+ stickers: preparedStickers.map(s => ({
943
+ fileName: s.fileName,
944
+ isAnimated: s.isAnimated,
945
+ emojis: s.emojis,
946
+ accessibilityLabel: s.accessibilityLabel,
947
+ isLottie: s.isLottie,
948
+ mimetype: s.mimetype
949
+ })),
950
+ fileLength: doc.fileLength,
951
+ fileSha256: doc.fileSha256,
952
+ fileEncSha256: doc.fileEncSha256,
953
+ mediaKey: doc.mediaKey,
954
+ directPath: doc.directPath,
955
+ mediaKeyTimestamp: doc.mediaKeyTimestamp,
956
+ trayIconFileName,
957
+ stickerPackSize: archive.length,
958
+ stickerPackOrigin: proto.Message.StickerPackMessage.StickerPackOrigin.USER_CREATED
959
+ }
960
+ };
961
+ }
962
+ else if (hasNonNullishProperty(message, 'sticker') && (message.pack || message.author || message.isPrivate || message.premium || message.animated)) {
963
+ const rawBuffer = await resolveStickerBuffer(message.sticker, options.options);
964
+ const finalWebp = await makeSticker(rawBuffer, {
965
+ pack: message.pack || '',
966
+ author: message.author || '',
967
+ emojis: message.emojis || [],
968
+ isPrivate: !!message.isPrivate,
969
+ animated: !!message.animated,
970
+ quality: message.quality || 60
971
+ });
972
+ m = await prepareWAMessageMedia({ sticker: finalWebp }, options);
973
+ if (m.stickerMessage) {
974
+ if (message.premium) {
975
+ m.stickerMessage.premium = typeof message.premium === 'number' ? message.premium : 1;
976
+ }
977
+ if (message.isPrivate) {
978
+ m.stickerMessage.isAvatar = true;
979
+ }
980
+ }
981
+ }
848
982
  else {
849
- m = await prepareWAMessageMedia(message, options);
983
+ const rawProtoKey = RAW_PROTO_MESSAGE_KEYS.find(k => hasNonNullishProperty(message, k));
984
+ if (rawProtoKey) {
985
+ const built = proto.Message.fromObject({ [rawProtoKey]: message[rawProtoKey] });
986
+ m = { [rawProtoKey]: built[rawProtoKey] };
987
+ }
988
+ else {
989
+ m = await prepareWAMessageMedia(message, options);
990
+ }
850
991
  }
851
992
  if (hasOptionalProperty(message, 'viewOnce') && !!message.viewOnce) {
852
993
  m = { viewOnceMessage: { message: m } };
@@ -0,0 +1,133 @@
1
+ import { Boom } from '@hapi/boom';
2
+ import { spawn } from 'child_process';
3
+ import { randomUUID } from 'crypto';
4
+
5
+ const runFfmpeg = (input, args) => new Promise((resolve, reject) => {
6
+ const proc = spawn('ffmpeg', ['-hide_banner', '-loglevel', 'error', '-i', 'pipe:0', ...args, 'pipe:1'], { stdio: ['pipe', 'pipe', 'pipe'] });
7
+ const outChunks = [];
8
+ const errChunks = [];
9
+ proc.stdout.on('data', chunk => outChunks.push(chunk));
10
+ proc.stderr.on('data', chunk => errChunks.push(chunk));
11
+ proc.on('error', err => reject(new Boom(`ffmpeg failed to start: ${err.message}`, { statusCode: 500 })));
12
+ proc.on('close', code => {
13
+ if (code !== 0) {
14
+ reject(new Boom(`ffmpeg exited with code ${code}: ${Buffer.concat(errChunks).toString('utf8').slice(0, 500)}`, { statusCode: 500 }));
15
+ return;
16
+ }
17
+ resolve(Buffer.concat(outChunks));
18
+ });
19
+ proc.stdin.on('error', () => { });
20
+ proc.stdin.end(input);
21
+ });
22
+ export const isLikelyVideoBuffer = (buffer) => {
23
+ if (!Buffer.isBuffer(buffer) || buffer.length < 4) {
24
+ return false;
25
+ }
26
+ const header = buffer.subarray(0, 16).toString('ascii');
27
+ if (header.startsWith('RIFF') && /AVI|WEBM/.test(header)) {
28
+ return true;
29
+ }
30
+ if (buffer.subarray(0, 4).toString('hex') === '1a45dfa3') {
31
+ return true;
32
+ }
33
+ const ftyp = buffer.toString('ascii', 4, 12);
34
+ if (buffer.toString('ascii', 0, 4) === 'ftyp' || /^(ftyp)/.test(buffer.toString('ascii', 0, 8))) {
35
+ return /mp4|isom|M4V|M4A|qt\s/.test(ftyp);
36
+ }
37
+ return false;
38
+ };
39
+ export const convertToWebp = async (buffer, { animated = false, quality = 60 } = {}) => {
40
+ const scalePad = "scale='min(512,iw)':'min(512,ih)':force_original_aspect_ratio=decrease,format=rgba,pad=512:512:-1:-1:color=#00000000";
41
+ const args = animated
42
+ ? ['-vf', `${scalePad},fps=15`, '-loop', '0', '-preset', 'default', '-an', '-vsync', '0', '-vcodec', 'libwebp', '-q:v', String(quality), '-f', 'webp']
43
+ : ['-vf', `${scalePad}`, '-vframes', '1', '-vcodec', 'libwebp', '-q:v', String(quality), '-f', 'webp'];
44
+ return runFfmpeg(buffer, args);
45
+ };
46
+ export const convertToTrayIcon = async (buffer) => {
47
+ const scalePad = "scale='min(96,iw)':'min(96,ih)':force_original_aspect_ratio=decrease,format=rgba,pad=96:96:-1:-1:color=#00000000";
48
+ const args = ['-vf', scalePad, '-vframes', '1', '-vcodec', 'png', '-f', 'image2'];
49
+ return runFfmpeg(buffer, args);
50
+ };
51
+ const readChunks = (buf) => {
52
+ const chunks = [];
53
+ let offset = 12;
54
+ while (offset + 8 <= buf.length) {
55
+ const fourCC = buf.toString('ascii', offset, offset + 4);
56
+ const size = buf.readUInt32LE(offset + 4);
57
+ const dataStart = offset + 8;
58
+ const data = buf.subarray(dataStart, dataStart + size);
59
+ chunks.push({ fourCC, data });
60
+ offset = dataStart + size + (size % 2);
61
+ }
62
+ return chunks;
63
+ };
64
+ const buildChunk = (fourCC, data) => {
65
+ const header = Buffer.alloc(8);
66
+ header.write(fourCC, 0, 4, 'ascii');
67
+ header.writeUInt32LE(data.length, 4);
68
+ const pad = data.length % 2 === 1 ? Buffer.from([0]) : Buffer.alloc(0);
69
+ return Buffer.concat([header, data, pad]);
70
+ };
71
+ const buildExifChunkData = (json) => {
72
+ const header = Buffer.from([0x49, 0x49, 0x2a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x41, 0x57, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00]);
73
+ const payload = Buffer.from(JSON.stringify(json), 'utf8');
74
+ header.writeUInt32LE(payload.length, 14);
75
+ return Buffer.concat([header, payload]);
76
+ };
77
+ export const writeExifToWebp = (webpBuffer, { packId, packName = '', publisher = '', emojis = [], isAvatar = false } = {}) => {
78
+ if (webpBuffer.toString('ascii', 0, 4) !== 'RIFF' || webpBuffer.toString('ascii', 8, 12) !== 'WEBP') {
79
+ throw new Boom('writeExifToWebp: not a valid webp buffer', { statusCode: 400 });
80
+ }
81
+ const chunks = readChunks(webpBuffer);
82
+ const exifData = buildExifChunkData({
83
+ 'sticker-pack-id': packId || randomUUID(),
84
+ 'sticker-pack-name': packName,
85
+ 'sticker-pack-publisher': publisher,
86
+ emojis: emojis.length ? emojis : ['🤖'],
87
+ 'is-avatar-sticker': isAvatar ? 1 : 0
88
+ });
89
+ let vp8x = chunks.find(c => c.fourCC === 'VP8X');
90
+ const otherChunks = chunks.filter(c => c.fourCC !== 'VP8X' && c.fourCC !== 'EXIF');
91
+ if (!vp8x) {
92
+ const img = otherChunks.find(c => c.fourCC === 'VP8 ' || c.fourCC === 'VP8L');
93
+ if (!img) {
94
+ throw new Boom('writeExifToWebp: no VP8/VP8L image data found', { statusCode: 400 });
95
+ }
96
+ let width;
97
+ let height;
98
+ if (img.fourCC === 'VP8 ') {
99
+ width = (img.data.readUInt16LE(6) & 0x3fff);
100
+ height = (img.data.readUInt16LE(8) & 0x3fff);
101
+ }
102
+ else {
103
+ const b = img.data;
104
+ width = 1 + (((b[2] & 0x3f) << 8) | b[1]);
105
+ height = 1 + (((b[4] & 0xf) << 10) | (b[3] << 2) | ((b[2] & 0xc0) >> 6));
106
+ }
107
+ const flags = Buffer.alloc(4);
108
+ flags[0] = 0x08;
109
+ const dims = Buffer.alloc(6);
110
+ dims.writeUIntLE(width - 1, 0, 3);
111
+ dims.writeUIntLE(height - 1, 3, 3);
112
+ vp8x = { fourCC: 'VP8X', data: Buffer.concat([flags, dims]) };
113
+ }
114
+ else {
115
+ const flagsByte = vp8x.data[0] | 0x08;
116
+ vp8x = { fourCC: 'VP8X', data: Buffer.concat([Buffer.from([flagsByte]), vp8x.data.subarray(1)]) };
117
+ }
118
+ const rebuilt = [vp8x, ...otherChunks, { fourCC: 'EXIF', data: exifData }];
119
+ const chunkBuffers = rebuilt.map(c => buildChunk(c.fourCC, c.data));
120
+ const payload = Buffer.concat(chunkBuffers);
121
+ const riffSize = 4 + payload.length;
122
+ const out = Buffer.alloc(8 + 4 + payload.length);
123
+ out.write('RIFF', 0, 4, 'ascii');
124
+ out.writeUInt32LE(riffSize, 4);
125
+ out.write('WEBP', 8, 4, 'ascii');
126
+ payload.copy(out, 12);
127
+ return out;
128
+ };
129
+ export const makeSticker = async (buffer, { pack = '', author = '', emojis = [], isPrivate = false, animated = false, quality = 60 } = {}) => {
130
+ const shouldAnimate = animated || isLikelyVideoBuffer(buffer);
131
+ const webp = await convertToWebp(buffer, { animated: shouldAnimate, quality });
132
+ return writeExifToWebp(webp, { packName: pack, publisher: author, emojis, isAvatar: isPrivate });
133
+ };
@@ -0,0 +1,162 @@
1
+ import { mkdir, readdir, readFile } from 'fs/promises';
2
+ import { dirname, join } from 'path';
3
+ import { proto } from '../../WAProto/index.js';
4
+ import { initAuthCreds } from './auth-utils.js';
5
+ export const useSqliteAuthState = async (pathOrFolder, options = {}) => {
6
+ const {
7
+ fileName = 'auth.db',
8
+ migrateFromFolder,
9
+ logger
10
+ } = options;
11
+ const dbPath = /\.(db|sqlite|sqlite3)$/i.test(pathOrFolder) ? pathOrFolder : join(pathOrFolder, fileName);
12
+ const bufReplacer = (_, value) => {
13
+ if (Buffer.isBuffer(value) || value instanceof Uint8Array || value?.type === 'Buffer') {
14
+ return {
15
+ type: 'Buffer',
16
+ data: Buffer.from(value?.data || value).toString('base64')
17
+ };
18
+ }
19
+ return value;
20
+ };
21
+ const bufReviver = (_, value) => {
22
+ if (value && typeof value === 'object' && value.type === 'Buffer' && typeof value.data === 'string') {
23
+ return Buffer.from(value.data, 'base64');
24
+ }
25
+ return value;
26
+ };
27
+ const encode = value => {
28
+ if (Buffer.isBuffer(value) || value instanceof Uint8Array) {
29
+ return Buffer.concat([Buffer.from([1]), Buffer.from(value)]);
30
+ }
31
+ return Buffer.concat([Buffer.from([0]), Buffer.from(JSON.stringify(value, bufReplacer), 'utf8')]);
32
+ };
33
+ const decode = blob => {
34
+ if (!blob || blob.length === 0) return null;
35
+ if (blob[0] === 1) return Buffer.from(blob.subarray(1));
36
+ return JSON.parse(Buffer.from(blob.subarray(1)).toString('utf8'), bufReviver);
37
+ };
38
+ const fixName = s => s?.replace(/\//g, '__')?.replace(/:/g, '-');
39
+ const keyOf = (category, id) => fixName(`${category}-${id}`);
40
+ let DatabaseSync;
41
+ try {
42
+ ({
43
+ DatabaseSync
44
+ } = await import('node:sqlite'));
45
+ } catch (err) {
46
+ throw new Error("useSqliteAuthState needs the built-in 'node:sqlite' module (Node 22.5+). Upgrade Node, or use useMultiFileAuthState instead.");
47
+ }
48
+ await mkdir(dirname(dbPath), {
49
+ recursive: true
50
+ }).catch(() => {});
51
+ const db = new DatabaseSync(dbPath);
52
+ db.exec('PRAGMA journal_mode = TRUNCATE');
53
+ db.exec('PRAGMA synchronous = NORMAL');
54
+ db.exec('PRAGMA busy_timeout = 5000');
55
+ db.exec('CREATE TABLE IF NOT EXISTS auth_state (k TEXT PRIMARY KEY, v BLOB NOT NULL) WITHOUT ROWID');
56
+ const qGet = db.prepare('SELECT v FROM auth_state WHERE k = ?');
57
+ const qUpsert = db.prepare('INSERT INTO auth_state(k,v) VALUES(?,?) ON CONFLICT(k) DO UPDATE SET v=excluded.v');
58
+ const qDelete = db.prepare('DELETE FROM auth_state WHERE k = ?');
59
+ const runTx = fn => {
60
+ db.exec('BEGIN');
61
+ try {
62
+ const r = fn();
63
+ db.exec('COMMIT');
64
+ return r;
65
+ } catch (err) {
66
+ db.exec('ROLLBACK');
67
+ throw err;
68
+ }
69
+ };
70
+ const readRaw = k => {
71
+ const row = qGet.get(k);
72
+ if (!row) return null;
73
+ try {
74
+ return decode(row.v);
75
+ } catch (err) {
76
+ logger?.warn?.({
77
+ k,
78
+ err: err?.message
79
+ }, 'sqlite-auth: failed to decode row, treating as missing');
80
+ return null;
81
+ }
82
+ };
83
+ const runMigration = async folder => {
84
+ let files;
85
+ try {
86
+ files = await readdir(folder);
87
+ } catch {
88
+ return 0;
89
+ }
90
+ const rows = [];
91
+ for (const file of files) {
92
+ if (!file.endsWith('.json')) continue;
93
+ let value;
94
+ try {
95
+ value = JSON.parse(await readFile(join(folder, file), 'utf8'), bufReviver);
96
+ } catch {
97
+ continue;
98
+ }
99
+ if (value === null || value === undefined) continue;
100
+ rows.push([file.slice(0, -'.json'.length), encode(value)]);
101
+ }
102
+ runTx(() => {
103
+ for (const [k, v] of rows) qUpsert.run(k, v);
104
+ });
105
+ return rows.length;
106
+ };
107
+ const hasCreds = () => !!qGet.get('creds');
108
+ if (migrateFromFolder && !hasCreds()) {
109
+ const n = await runMigration(migrateFromFolder);
110
+ if (n > 0) logger?.info?.({
111
+ count: n,
112
+ from: migrateFromFolder
113
+ }, 'sqlite-auth: migrated legacy auth state');
114
+ }
115
+ let creds = readRaw('creds');
116
+ if (!creds) {
117
+ creds = initAuthCreds();
118
+ qUpsert.run('creds', encode(creds));
119
+ }
120
+ const keys = {
121
+ get: async (type, ids) => {
122
+ const data = {};
123
+ for (const id of ids) {
124
+ let value = readRaw(keyOf(type, id));
125
+ if (type === 'app-state-sync-key' && value) {
126
+ value = proto.Message.AppStateSyncKeyData.fromObject(value);
127
+ }
128
+ if (value !== null && value !== undefined) {
129
+ data[id] = value;
130
+ }
131
+ }
132
+ return data;
133
+ },
134
+ set: async data => {
135
+ const ops = [];
136
+ for (const category in data) {
137
+ for (const id in data[category]) {
138
+ ops.push([keyOf(category, id), data[category][id]]);
139
+ }
140
+ }
141
+ runTx(() => {
142
+ for (const [k, value] of ops) {
143
+ if (value === null || value === undefined) qDelete.run(k);else qUpsert.run(k, encode(value));
144
+ }
145
+ });
146
+ },
147
+ clear: async () => {
148
+ db.prepare("DELETE FROM auth_state WHERE k <> 'creds'").run();
149
+ }
150
+ };
151
+ return {
152
+ state: {
153
+ creds,
154
+ keys
155
+ },
156
+ saveCreds: async () => {
157
+ qUpsert.run('creds', encode(creds));
158
+ },
159
+ db,
160
+ close: () => db.close()
161
+ };
162
+ };
@@ -0,0 +1,53 @@
1
+ import { getBinaryNodeChild, getBinaryNodeChildren, getBinaryNodeChildString } from '../../WABinary/index.js'
2
+
3
+ export class USyncBotProfileProtocol {
4
+ constructor() {
5
+ this.name = 'bot'
6
+ }
7
+
8
+ getQueryElement() {
9
+ return {
10
+ tag: 'bot',
11
+ attrs: {},
12
+ content: [{ tag: 'profile', attrs: { v: '1' } }]
13
+ }
14
+ }
15
+
16
+ getUserElement(user) {
17
+ return {
18
+ tag: 'bot',
19
+ attrs: {},
20
+ content: [{ tag: 'profile', attrs: { persona_id: user.personaId } }]
21
+ }
22
+ }
23
+
24
+ parser(node) {
25
+ const botNode = getBinaryNodeChild(node, 'bot')
26
+ const profile = getBinaryNodeChild(botNode, 'profile')
27
+ const commandsNode = getBinaryNodeChild(profile, 'commands')
28
+ const promptsNode = getBinaryNodeChild(profile, 'prompts')
29
+ const commands = []
30
+ const prompts = []
31
+ for (const command of getBinaryNodeChildren(commandsNode, 'command')) {
32
+ commands.push({
33
+ name: getBinaryNodeChildString(command, 'name'),
34
+ description: getBinaryNodeChildString(command, 'description')
35
+ })
36
+ }
37
+ for (const prompt of getBinaryNodeChildren(promptsNode, 'prompt')) {
38
+ prompts.push(`${getBinaryNodeChildString(prompt, 'emoji')} ${getBinaryNodeChildString(prompt, 'text')}`)
39
+ }
40
+ return {
41
+ isDefault: !!getBinaryNodeChild(profile, 'default'),
42
+ jid: node.attrs.jid,
43
+ name: getBinaryNodeChildString(profile, 'name'),
44
+ attributes: getBinaryNodeChildString(profile, 'attributes'),
45
+ description: getBinaryNodeChildString(profile, 'description'),
46
+ category: getBinaryNodeChildString(profile, 'category'),
47
+ personaId: profile.attrs['persona_id'],
48
+ commandsDescription: getBinaryNodeChildString(commandsNode, 'description'),
49
+ commands,
50
+ prompts
51
+ }
52
+ }
53
+ }
@@ -0,0 +1,43 @@
1
+ import { assertNodeErrorFree, getBinaryNodeChild } from '../../WABinary/index.js'
2
+
3
+ export class USyncBusinessProtocol {
4
+ constructor(profileVersion = '2') {
5
+ this.name = 'business'
6
+ this.profileVersion = profileVersion
7
+ }
8
+
9
+ getQueryElement() {
10
+ return {
11
+ tag: 'business',
12
+ attrs: {},
13
+ content: [
14
+ { tag: 'verified_name', attrs: {} },
15
+ { tag: 'profile', attrs: { v: this.profileVersion } }
16
+ ]
17
+ }
18
+ }
19
+
20
+ getUserElement(user) {
21
+ const children = []
22
+ if (user.verifiedNameSerial) {
23
+ children.push({ tag: 'verified_name', attrs: { serial: user.verifiedNameSerial } })
24
+ }
25
+ if (user.businessProfileTag) {
26
+ children.push({ tag: 'profile', attrs: { tag: user.businessProfileTag } })
27
+ }
28
+ return children.length > 0 ? { tag: 'business', attrs: {}, content: children } : null
29
+ }
30
+
31
+ parser(node) {
32
+ if (node.tag !== 'business') return null
33
+ assertNodeErrorFree(node)
34
+ const verifiedNameNode = getBinaryNodeChild(node, 'verified_name')
35
+ const profileNode = getBinaryNodeChild(node, 'profile')
36
+ return {
37
+ verifiedName: verifiedNameNode?.content ?? null,
38
+ verifiedLevel: verifiedNameNode?.attrs?.verified_level ?? null,
39
+ profileTag: profileNode?.attrs?.tag ?? null,
40
+ pnJid: node.attrs?.pn_jid ?? null
41
+ }
42
+ }
43
+ }
@@ -1,52 +1,54 @@
1
- import { assertNodeErrorFree } from '../../WABinary/index.js';
2
- import { USyncUser } from '../USyncUser.js';
1
+ import { assertNodeErrorFree } from '../../WABinary/index.js'
2
+
3
3
  export class USyncContactProtocol {
4
- constructor() {
5
- this.name = 'contact';
6
- }
7
- getQueryElement() {
8
- return {
9
- tag: 'contact',
10
- attrs: {}
11
- };
12
- }
13
- getUserElement(user) {
14
- if (user.phone) {
15
- return {
16
- tag: 'contact',
17
- attrs: {},
18
- content: user.phone
19
- };
20
- }
21
- if (user.username) {
22
- return {
23
- tag: 'contact',
24
- attrs: {
25
- username: user.username,
26
- ...(user.usernameKey ? { pin: user.usernameKey } : {}),
27
- ...(user.lid ? { lid: user.lid } : {})
28
- }
29
- };
30
- }
31
- if (user.type) {
32
- return {
33
- tag: 'contact',
34
- attrs: {
35
- type: user.type
36
- }
37
- };
38
- }
39
- return {
40
- tag: 'contact',
41
- attrs: {}
42
- };
43
- }
44
- parser(node) {
45
- if (node.tag === 'contact') {
46
- assertNodeErrorFree(node);
47
- return node?.attrs?.type === 'in';
48
- }
49
- return false;
50
- }
4
+ constructor() {
5
+ this.name = 'contact'
6
+ }
7
+
8
+ getQueryElement() {
9
+ return {
10
+ tag: 'contact',
11
+ attrs: {}
12
+ }
13
+ }
14
+
15
+ getUserElement(user) {
16
+ if (user.phone) {
17
+ return {
18
+ tag: 'contact',
19
+ attrs: {},
20
+ content: user.phone
21
+ }
22
+ }
23
+ if (user.username) {
24
+ return {
25
+ tag: 'contact',
26
+ attrs: {
27
+ username: user.username,
28
+ ...(user.usernameKey ? { pin: user.usernameKey } : {}),
29
+ ...(user.lid ? { lid: user.lid } : {})
30
+ }
31
+ }
32
+ }
33
+ if (user.type) {
34
+ return {
35
+ tag: 'contact',
36
+ attrs: {
37
+ type: user.type
38
+ }
39
+ }
40
+ }
41
+ return {
42
+ tag: 'contact',
43
+ attrs: {}
44
+ }
45
+ }
46
+
47
+ parser(node) {
48
+ if (node.tag === 'contact') {
49
+ assertNodeErrorFree(node)
50
+ return node?.attrs?.type === 'in'
51
+ }
52
+ return false
53
+ }
51
54
  }
52
- //# sourceMappingURL=USyncContactProtocol.js.map