@systemzero/baileys 1.0.6 → 1.0.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.
- package/README.md +371 -305
- package/lib/Defaults/index.js +2 -2
- package/lib/Socket/messages-recv.js +9 -4
- package/lib/Socket/socket.js +4 -4
- package/lib/Utils/generics.js +2 -1
- package/lib/Utils/index.js +1 -0
- package/lib/Utils/messages.js +1 -1
- package/lib/Utils/scheduling.js +138 -0
- package/lib/Utils/validate-connection.js +11 -5
- package/package.json +2 -3
package/lib/Defaults/index.js
CHANGED
|
@@ -108,8 +108,8 @@ export const MEDIA_HKDF_KEY_MAPPING = {
|
|
|
108
108
|
export const MEDIA_KEYS = Object.keys(MEDIA_PATH_MAP);
|
|
109
109
|
export const MIN_PREKEY_COUNT = 5;
|
|
110
110
|
export const INITIAL_PREKEY_COUNT = 812;
|
|
111
|
-
export const UPLOAD_TIMEOUT =
|
|
112
|
-
export const MIN_UPLOAD_INTERVAL =
|
|
111
|
+
export const UPLOAD_TIMEOUT = 45000; // 45 segundos (era 30, uploads podem demorar mais em conexões lentas)
|
|
112
|
+
export const MIN_UPLOAD_INTERVAL = 30000; // 30 segundos entre uploads normais (era 5s, muito agressivo)
|
|
113
113
|
export const DEFAULT_CACHE_TTLS = {
|
|
114
114
|
SIGNAL_STORE: 5 * 60, // 5 minutes
|
|
115
115
|
MSG_RETRY: 60 * 60, // 1 hour
|
|
@@ -1036,14 +1036,20 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1036
1036
|
logger.debug({ node }, 'Connection closed, skipping retry');
|
|
1037
1037
|
return;
|
|
1038
1038
|
}
|
|
1039
|
-
// Handle pre-key errors with upload and delay
|
|
1040
1039
|
if (isPreKeyError) {
|
|
1041
1040
|
logger.info({ error: errorMessage }, 'PreKey error detected, uploading and retrying');
|
|
1042
1041
|
try {
|
|
1043
1042
|
logger.debug('Uploading pre-keys for error recovery');
|
|
1044
|
-
|
|
1043
|
+
// Sobe mais chaves de uma vez (30 em vez de 5) — o WhatsApp
|
|
1044
|
+
// às vezes precisa de um conjunto maior pra resolver conflitos
|
|
1045
|
+
// de ID quando a sessão local ficou dessincronizada do servidor.
|
|
1046
|
+
await uploadPreKeys(30, 0, true);
|
|
1047
|
+
// Aumentado de 1000ms pra 2500ms — o servidor do WhatsApp
|
|
1048
|
+
// precisa de mais tempo pra processar e distribuir as novas
|
|
1049
|
+
// chaves antes de a gente tentar o retry, especialmente em
|
|
1050
|
+
// casos de dessincronização severa.
|
|
1045
1051
|
logger.debug('Waiting for server to process new pre-keys');
|
|
1046
|
-
await delay(
|
|
1052
|
+
await delay(2500);
|
|
1047
1053
|
}
|
|
1048
1054
|
catch (uploadErr) {
|
|
1049
1055
|
logger.error({ uploadErr }, 'Pre-key upload failed, proceeding with retry anyway');
|
|
@@ -1057,7 +1063,6 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1057
1063
|
}
|
|
1058
1064
|
catch (err) {
|
|
1059
1065
|
logger.error({ err, isPreKeyError }, 'Failed to handle retry, attempting basic retry');
|
|
1060
|
-
// Still attempt retry even if pre-key upload failed
|
|
1061
1066
|
try {
|
|
1062
1067
|
const encNode = getBinaryNodeChild(node, 'enc');
|
|
1063
1068
|
await sendRetryRequest(node, !encNode);
|
package/lib/Socket/socket.js
CHANGED
|
@@ -311,7 +311,7 @@ export const makeSocket = (config) => {
|
|
|
311
311
|
logger.info({ node }, 'not logged in, attempting registration...');
|
|
312
312
|
}
|
|
313
313
|
else {
|
|
314
|
-
node = generateLoginNode(creds.me.id, config);
|
|
314
|
+
node = generateLoginNode(creds.me.id, { ...config, auth: { creds } });
|
|
315
315
|
logger.info({ node }, 'logging in...');
|
|
316
316
|
}
|
|
317
317
|
const payloadEnc = noise.encrypt(proto.ClientPayload.encode(node).finish());
|
|
@@ -342,9 +342,9 @@ export const makeSocket = (config) => {
|
|
|
342
342
|
let uploadPreKeysPromise = null;
|
|
343
343
|
let lastUploadTime = 0;
|
|
344
344
|
/** generates and uploads a set of pre-keys to the server */
|
|
345
|
-
const uploadPreKeys = async (count = MIN_PREKEY_COUNT, retryCount = 0) => {
|
|
346
|
-
// Check minimum interval (except for retries)
|
|
347
|
-
if (retryCount === 0) {
|
|
345
|
+
const uploadPreKeys = async (count = MIN_PREKEY_COUNT, retryCount = 0, force = false) => {
|
|
346
|
+
// Check minimum interval (except for retries ou uploads forçados por erro de PreKey)
|
|
347
|
+
if (retryCount === 0 && !force) {
|
|
348
348
|
const timeSinceLastUpload = Date.now() - lastUploadTime;
|
|
349
349
|
if (timeSinceLastUpload < MIN_UPLOAD_INTERVAL) {
|
|
350
350
|
logger.debug(`Skipping upload, only ${timeSinceLastUpload}ms since last upload`);
|
package/lib/Utils/generics.js
CHANGED
|
@@ -277,7 +277,8 @@ export const getStatusFromReceiptType = (type) => {
|
|
|
277
277
|
return status;
|
|
278
278
|
};
|
|
279
279
|
const CODE_MAP = {
|
|
280
|
-
conflict: DisconnectReason.connectionReplaced
|
|
280
|
+
conflict: DisconnectReason.connectionReplaced,
|
|
281
|
+
forbidden: DisconnectReason.forbidden
|
|
281
282
|
};
|
|
282
283
|
/**
|
|
283
284
|
* Stream errors generally provide a reason, map that to a baileys DisconnectReason
|
package/lib/Utils/index.js
CHANGED
package/lib/Utils/messages.js
CHANGED
|
@@ -98,7 +98,7 @@ if (mediaType === 'audio' && uploadData.ptt === true) {
|
|
|
98
98
|
? uploadData.media
|
|
99
99
|
: (typeof uploadData.media === 'string' ? uploadData.media : mediaUrl);
|
|
100
100
|
pttTranscodedPath = await transcodeAudioToOpus(sourceForTranscode);
|
|
101
|
-
uploadData.media = pttTranscodedPath;
|
|
101
|
+
uploadData.media = { url: pttTranscodedPath };
|
|
102
102
|
}
|
|
103
103
|
uploadData.mimetype = 'audio/ogg; codecs=opus';
|
|
104
104
|
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message Scheduling System
|
|
3
|
+
* Ported from innovatorssoft/baileys — schedule messages to be sent automatically
|
|
4
|
+
* at specific times or after a delay.
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - schedule(jid, content, Date) — send at an absolute time
|
|
8
|
+
* - scheduleDelay(jid, content, delayMs) — send after N milliseconds
|
|
9
|
+
* - cancel(id) — cancel by ID
|
|
10
|
+
* - cancelForJid(jid) — cancel all pending for a JID
|
|
11
|
+
* - getPending() — list all pending messages
|
|
12
|
+
* - get(id) — get a single scheduled entry
|
|
13
|
+
* - clearAll() — wipe the whole queue
|
|
14
|
+
* - stop() — stop the internal timer
|
|
15
|
+
* - start() — restart the timer
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export class MessageScheduler {
|
|
19
|
+
constructor(sendMessage, options = {}) {
|
|
20
|
+
this.queue = new Map();
|
|
21
|
+
this.timer = null;
|
|
22
|
+
this.sendMessage = sendMessage;
|
|
23
|
+
this.options = {
|
|
24
|
+
maxQueue: options.maxQueue ?? 1000,
|
|
25
|
+
checkInterval: options.checkInterval ?? 1000,
|
|
26
|
+
onSent: options.onSent ?? (() => {}),
|
|
27
|
+
onFailed: options.onFailed ?? (() => {}),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
generateId() {
|
|
32
|
+
return `sched_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
schedule(jid, content, scheduledTime) {
|
|
36
|
+
if (this.queue.size >= this.options.maxQueue) {
|
|
37
|
+
throw new Error(`Maximum queue size (${this.options.maxQueue}) reached`);
|
|
38
|
+
}
|
|
39
|
+
if (scheduledTime.getTime() <= Date.now()) {
|
|
40
|
+
throw new Error('Scheduled time must be in the future');
|
|
41
|
+
}
|
|
42
|
+
const scheduled = {
|
|
43
|
+
id: this.generateId(),
|
|
44
|
+
jid,
|
|
45
|
+
content,
|
|
46
|
+
scheduledTime,
|
|
47
|
+
createdAt: new Date(),
|
|
48
|
+
status: 'pending',
|
|
49
|
+
};
|
|
50
|
+
this.queue.set(scheduled.id, scheduled);
|
|
51
|
+
this.ensureTimerRunning();
|
|
52
|
+
return scheduled;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
scheduleDelay(jid, content, delayMs) {
|
|
56
|
+
return this.schedule(jid, content, new Date(Date.now() + delayMs));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
cancel(id) {
|
|
60
|
+
const scheduled = this.queue.get(id);
|
|
61
|
+
if (scheduled && scheduled.status === 'pending') {
|
|
62
|
+
scheduled.status = 'cancelled';
|
|
63
|
+
this.queue.delete(id);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
cancelForJid(jid) {
|
|
70
|
+
let cancelled = 0;
|
|
71
|
+
for (const [id, scheduled] of this.queue) {
|
|
72
|
+
if (scheduled.jid === jid && scheduled.status === 'pending') {
|
|
73
|
+
scheduled.status = 'cancelled';
|
|
74
|
+
this.queue.delete(id);
|
|
75
|
+
cancelled++;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return cancelled;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
getPending() {
|
|
82
|
+
return Array.from(this.queue.values()).filter(s => s.status === 'pending');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
get(id) {
|
|
86
|
+
return this.queue.get(id);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
clearAll() {
|
|
90
|
+
const count = this.queue.size;
|
|
91
|
+
this.queue.clear();
|
|
92
|
+
this.stopTimer();
|
|
93
|
+
return count;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async processQueue() {
|
|
97
|
+
const now = Date.now();
|
|
98
|
+
for (const [id, scheduled] of this.queue) {
|
|
99
|
+
if (scheduled.status !== 'pending') continue;
|
|
100
|
+
if (scheduled.scheduledTime.getTime() > now) continue;
|
|
101
|
+
try {
|
|
102
|
+
const message = await this.sendMessage(scheduled.jid, scheduled.content);
|
|
103
|
+
scheduled.status = 'sent';
|
|
104
|
+
scheduled.messageId = message?.key?.id ?? undefined;
|
|
105
|
+
this.options.onSent(scheduled, message);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
scheduled.status = 'failed';
|
|
108
|
+
scheduled.error = error.message;
|
|
109
|
+
this.options.onFailed(scheduled, error);
|
|
110
|
+
}
|
|
111
|
+
this.queue.delete(id);
|
|
112
|
+
}
|
|
113
|
+
if (this.queue.size === 0) this.stopTimer();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
ensureTimerRunning() {
|
|
117
|
+
if (!this.timer) {
|
|
118
|
+
this.timer = setInterval(() => this.processQueue(), this.options.checkInterval);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
stopTimer() {
|
|
123
|
+
if (this.timer) {
|
|
124
|
+
clearInterval(this.timer);
|
|
125
|
+
this.timer = null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
stop() { this.stopTimer(); }
|
|
130
|
+
|
|
131
|
+
start() {
|
|
132
|
+
if (this.queue.size > 0) this.ensureTimerRunning();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export const createMessageScheduler = (sendMessage, options) => {
|
|
137
|
+
return new MessageScheduler(sendMessage, options);
|
|
138
|
+
};
|
|
@@ -48,14 +48,18 @@ const getClientPayload = (config) => {
|
|
|
48
48
|
};
|
|
49
49
|
export const generateLoginNode = (userJid, config) => {
|
|
50
50
|
const { user, device } = jidDecode(userJid);
|
|
51
|
+
// lidDbMigrated: informa ao servidor se este dispositivo já completou a
|
|
52
|
+
// migração de sessão para o sistema LID. Manter como false (hardcoded)
|
|
53
|
+
// quando a conta já foi migrada faz o WhatsApp rejeitar a sessão com 403,
|
|
54
|
+
// porque o servidor espera que o cliente reconheça o estado de migração.
|
|
55
|
+
const hasLidMigrated = !!(config.auth?.creds?.myAppStateKeyId || config.auth?.creds?.lid);
|
|
51
56
|
const payload = {
|
|
52
57
|
...getClientPayload(config),
|
|
53
58
|
passive: true,
|
|
54
59
|
pull: true,
|
|
55
60
|
username: +user,
|
|
56
61
|
device: device,
|
|
57
|
-
|
|
58
|
-
lidDbMigrated: false
|
|
62
|
+
lidDbMigrated: hasLidMigrated
|
|
59
63
|
};
|
|
60
64
|
return proto.ClientPayload.fromObject(payload);
|
|
61
65
|
};
|
|
@@ -92,9 +96,11 @@ export const generateRegistrationNode = ({ registrationId, signedPreKey, signedI
|
|
|
92
96
|
supportGuestChat: undefined
|
|
93
97
|
},
|
|
94
98
|
version: {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
99
|
+
// usa a versão real do WA passada no config, em vez de um valor
|
|
100
|
+
// fixo hardcoded (10.15.7) que fica desatualizado e pode causar 403
|
|
101
|
+
primary: config.version[0],
|
|
102
|
+
secondary: config.version[1],
|
|
103
|
+
tertiary: config.version[2]
|
|
98
104
|
}
|
|
99
105
|
};
|
|
100
106
|
const companionProto = proto.DeviceProps.encode(companion).finish();
|
package/package.json
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@systemzero/baileys",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.7",
|
|
5
5
|
"description": "System-zero baileys bot",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"whatsapp",
|
|
8
8
|
"automation",
|
|
9
|
-
"whatsapp-web"
|
|
10
|
-
"whatsapp-bot"
|
|
9
|
+
"whatsapp-web"
|
|
11
10
|
],
|
|
12
11
|
"publishConfig": {
|
|
13
12
|
"access": "public"
|