@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.
@@ -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 = 30000; // 30 seconds
112
- export const MIN_UPLOAD_INTERVAL = 5000; // 5 seconds minimum between uploads
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
- await uploadPreKeys(5);
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(1000);
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);
@@ -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`);
@@ -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
@@ -23,4 +23,5 @@ export * from './group-status-detection.js';
23
23
  export * from './bad-mac-handler.js';
24
24
  export * from './resolve-lid-phone.js';
25
25
  export * from './get-best-version.js';
26
+ export * from './scheduling.js';
26
27
  //# sourceMappingURL=index.js.map
@@ -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
- // TODO: investigate (hard set as false atm)
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
- primary: 10,
96
- secondary: 15,
97
- tertiary: 7
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.6",
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"