@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 +35 -8
- package/lib/Socket/communities.js +3 -3
- package/lib/Socket/groups.js +4 -4
- package/lib/Socket/messages-send.js +3 -3
- package/lib/Utils/business.js +2 -2
- package/lib/Utils/generics.d.ts +1 -2
- package/lib/Utils/generics.js +8 -9
- package/lib/Utils/messages-media.js +5 -5
- package/lib/Utils/messages.js +2 -2
- package/package.json +1 -1
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 = '
|
|
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
|
|
256
|
-
|
|
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
|
|
286
|
+
audio: fs.readFileSync('media.ogg'),
|
|
261
287
|
mimetype: 'audio/ogg; codecs=opus',
|
|
262
|
-
ptt:
|
|
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,
|
|
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 =
|
|
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:
|
|
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
|
},
|
package/lib/Socket/groups.js
CHANGED
|
@@ -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 {
|
|
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 =
|
|
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:
|
|
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:
|
|
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,
|
|
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 ||
|
|
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:
|
|
1086
|
+
messageId: generateMessageID(sock.user?.id),
|
|
1087
1087
|
...options
|
|
1088
1088
|
});
|
|
1089
1089
|
const isEventMsg = 'event' in content && !!content.event;
|
package/lib/Utils/business.js
CHANGED
|
@@ -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 {
|
|
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' +
|
|
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);
|
package/lib/Utils/generics.d.ts
CHANGED
|
@@ -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
|
|
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
|
/**
|
package/lib/Utils/generics.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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 {
|
|
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 +
|
|
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(),
|
|
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 +
|
|
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 +
|
|
328
|
+
originalFilePath = join(getTmpFilesDirectory(), mediaType + generateMessageID() + '-original');
|
|
329
329
|
originalFileStream = createWriteStream(originalFilePath);
|
|
330
330
|
}
|
|
331
331
|
let fileLength = 0;
|
package/lib/Utils/messages.js
CHANGED
|
@@ -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 {
|
|
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 ||
|
|
695
|
+
id: options?.messageId || generateMessageID()
|
|
696
696
|
},
|
|
697
697
|
message: message,
|
|
698
698
|
messageTimestamp: timestamp,
|