@areumtecnologia/baileys 1.0.0
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/baileys.code-workspace +8 -0
- package/desktop.ini +0 -0
- package/handlers/desktop.ini +0 -0
- package/handlers/groups.js +94 -0
- package/handlers/messages.js +285 -0
- package/handlers/presence-status.js +9 -0
- package/handlers/users.js +94 -0
- package/index.js +456 -0
- package/package.json +16 -0
- package/tests/app.js +68 -0
- package/tests/desktop.ini +0 -0
- package/types/buttons.js +72 -0
- package/types/desktop.ini +0 -0
- package/types/interactive-messages.js +318 -0
- package/utils/desktop.ini +0 -0
- package/utils/generic-utils.js +23 -0
- package/utils/index.js +4 -0
- package/utils/message-normalizer.js +323 -0
- package/utils/message-store-db-handler.js +39 -0
- package/utils/message-store.js +67 -0
package/index.js
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
// Biblioteca chamada synapse-b
|
|
2
|
+
const wb = require('@whiskeysockets/baileys');
|
|
3
|
+
const {
|
|
4
|
+
Browsers,
|
|
5
|
+
makeWASocket,
|
|
6
|
+
decryptPollVote,
|
|
7
|
+
DisconnectReason,
|
|
8
|
+
jidNormalizedUser,
|
|
9
|
+
downloadContentFromMessage,
|
|
10
|
+
useMultiFileAuthState,
|
|
11
|
+
fetchLatestBaileysVersion,
|
|
12
|
+
} = require('@itsukichan/baileys');
|
|
13
|
+
const { Boom } = require('@hapi/boom');
|
|
14
|
+
const EventEmitter = require('events');
|
|
15
|
+
const fs = require('fs/promises');
|
|
16
|
+
const { constants } = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const pino = require('pino');
|
|
19
|
+
// Importa os handlers de responsabilidades específicas
|
|
20
|
+
const MessageHandler = require('./handlers/messages');
|
|
21
|
+
const GroupHandler = require('./handlers/groups');
|
|
22
|
+
const { UserHandler, PresenceStatus } = require('./handlers/users');
|
|
23
|
+
const { MessageNormalizer, MessageStore } = require('./utils');
|
|
24
|
+
const { InteractiveMessage, CallButton, CopyCodeButton, ListButton, ListRow, ListSection, QuickReplyButton, UrlButton, LocationButton } = require('./types/interactive-messages');
|
|
25
|
+
const NodeCache = require("node-cache");
|
|
26
|
+
const groupCache = new NodeCache({ stdTTL: 5 * 60, useClones: false });
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Representa um wrapper de alto nível para a biblioteca Baileys, simplificando a criação e o gerenciamento de um cliente de WhatsApp.
|
|
30
|
+
* A classe abstrai a complexidade do ciclo de vida da conexão e emite eventos semânticos para todas as interações importantes.
|
|
31
|
+
*
|
|
32
|
+
* @extends {EventEmitter}
|
|
33
|
+
*
|
|
34
|
+
* @fires Client#status
|
|
35
|
+
* @fires Client#error
|
|
36
|
+
* @fires Client#message_received
|
|
37
|
+
* @fires Client#message_sent
|
|
38
|
+
* @fires Client#message_update
|
|
39
|
+
* @fires Client#message_delete
|
|
40
|
+
* @fires Client#message_reaction
|
|
41
|
+
* @fires Client#incoming_call
|
|
42
|
+
* @fires Client#group_update
|
|
43
|
+
* @fires Client#group_participants_update
|
|
44
|
+
* @fires Client#presence_update
|
|
45
|
+
* @fires Client#chat_update
|
|
46
|
+
* @fires Client#chat_delete
|
|
47
|
+
* @fires Client#contact_update
|
|
48
|
+
* @fires Client#blocklist_update
|
|
49
|
+
*/
|
|
50
|
+
class Client extends EventEmitter {
|
|
51
|
+
/**
|
|
52
|
+
* Cria uma instância do Cliente.
|
|
53
|
+
* @param {object} [options={}] - Opções de configuração para o cliente.
|
|
54
|
+
* @param {string} [options.sessionName='session'] - O nome da sessão a ser usada, que nomeia a pasta de autenticação.
|
|
55
|
+
*/
|
|
56
|
+
constructor(options = {}) {
|
|
57
|
+
super();
|
|
58
|
+
this.sock = null;
|
|
59
|
+
this.dataPath = options.dataPath || ".synapse-b/auth_data";
|
|
60
|
+
this.sessionName = options.sessionName;
|
|
61
|
+
this.authPath = [this.dataPath, this.sessionName].join('/');
|
|
62
|
+
this.store = options.store ? options.store : new MessageStore(this.authPath);
|
|
63
|
+
this.isOnline = false;
|
|
64
|
+
this.connected = false;
|
|
65
|
+
this.manualDisconnect = false;
|
|
66
|
+
this.receivedPendingNotifications = false;
|
|
67
|
+
this.loggerLevel = options.loggerLevel || "error";
|
|
68
|
+
this.restartOnClose = options.restartOnClose || false;
|
|
69
|
+
this.status = ClientEvent.DISCONNECTED;
|
|
70
|
+
this.markOnlineOnConnect = options.markOnlineOnConnect || false;
|
|
71
|
+
this.enviroment = options.enviroment ? options.enviroment : null;
|
|
72
|
+
this.qrCode = null;
|
|
73
|
+
// =================================================================================================
|
|
74
|
+
// INTEGRAÇÃO DOS HANDLERS
|
|
75
|
+
// =================================================================================================
|
|
76
|
+
// Instancia os handlers, passando a si mesma (this) como referência.
|
|
77
|
+
// Isso permite que os handlers acessem o 'sock' e outros métodos do cliente.
|
|
78
|
+
this.messages = new MessageHandler(this);
|
|
79
|
+
this.groups = new GroupHandler(this);
|
|
80
|
+
this.users = new UserHandler(this);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Inicia o cliente, configura a autenticação e estabelece a conexão com o WhatsApp.
|
|
85
|
+
* Gerencia todo o ciclo de vida da conexão, incluindo reconexão automática e solicitação de novo QR code.
|
|
86
|
+
* @returns {Promise<void>}
|
|
87
|
+
*/
|
|
88
|
+
async connect() {
|
|
89
|
+
this.status = ClientEvent.INIT;
|
|
90
|
+
const { state, saveCreds } = await useMultiFileAuthState(this.authPath);
|
|
91
|
+
const { version } = await fetchLatestBaileysVersion();
|
|
92
|
+
const credsPath = path.resolve(process.cwd(), this.authPath, "creds.json");
|
|
93
|
+
const creds = await this.fileExists(credsPath);
|
|
94
|
+
if (creds) {
|
|
95
|
+
// Conecta usando @itsukichan/baileys
|
|
96
|
+
this.sock = makeWASocket({
|
|
97
|
+
auth: state,
|
|
98
|
+
version,
|
|
99
|
+
browser: this.enviroment ? this.enviroment : Browsers.macOS("Desktop"),
|
|
100
|
+
printQRInTerminal: false, // Desabilitamos para controlar via evento
|
|
101
|
+
logger: pino({ level: this.loggerLevel }),
|
|
102
|
+
markOnlineOnConnect: this.markOnlineOnConnect || false,
|
|
103
|
+
keepAliveIntervalMs: 15000,
|
|
104
|
+
cachedGroupMetadata: async (jid) => groupCache.get(jid),
|
|
105
|
+
getMessage: async (key) => {
|
|
106
|
+
const chatId = key.remoteJid;
|
|
107
|
+
const msg = this.store?.getMessage(chatId, key.id);
|
|
108
|
+
// precisa retornar o raw.message
|
|
109
|
+
return msg ? msg.raw?.message : undefined;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
} else {
|
|
113
|
+
// Conecta usando Baileys
|
|
114
|
+
this.sock = wb.makeWASocket({
|
|
115
|
+
auth: state,
|
|
116
|
+
version,
|
|
117
|
+
browser: this.enviroment ? this.enviroment : Browsers.macOS("Desktop"),
|
|
118
|
+
printQRInTerminal: false, // Desabilitamos para controlar via evento
|
|
119
|
+
logger: pino({ level: this.loggerLevel }),
|
|
120
|
+
markOnlineOnConnect: this.markOnlineOnConnect || false,
|
|
121
|
+
cachedGroupMetadata: async (jid) => groupCache.get(jid),
|
|
122
|
+
getMessage: async (key) => {
|
|
123
|
+
const chatId = key.remoteJid;
|
|
124
|
+
const msg = this.store?.getMessage(chatId, key.id);
|
|
125
|
+
// precisa retornar o raw.message
|
|
126
|
+
return msg ? msg.raw?.message : undefined;
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// =================================================================================================
|
|
132
|
+
// EVENTO CENTRALIZADO DE CICLO DE VIDA
|
|
133
|
+
// =================================================================================================
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Disparado quando as credenciais são atualizadas.
|
|
137
|
+
* O handler `saveCreds` da Baileys cuida de persistir essas credenciais.
|
|
138
|
+
*/
|
|
139
|
+
this.sock.ev.on('creds.update', saveCreds);
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Gerencia TODAS as atualizações do estado da conexão, emitindo um evento 'status' padronizado.
|
|
143
|
+
*/
|
|
144
|
+
this.sock.ev.on('connection.update', async (update) => {
|
|
145
|
+
|
|
146
|
+
const { connection, isOnline, receivedPendingNotifications, lastDisconnect, qr, isNewLogin } = update;
|
|
147
|
+
|
|
148
|
+
if (receivedPendingNotifications)
|
|
149
|
+
this.receivedPendingNotifications = true;
|
|
150
|
+
|
|
151
|
+
if (isOnline)
|
|
152
|
+
this.isOnline = true;
|
|
153
|
+
|
|
154
|
+
if (qr) {
|
|
155
|
+
this.qrCode = qr;
|
|
156
|
+
this.status = ClientEvent.PAIRING_CODE;
|
|
157
|
+
this.emit(ClientEvent.PAIRING_CODE, update);
|
|
158
|
+
this.emit(ClientEvent.STATUS_CHANGE, this.status);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// intercepta a configuração de pairing e reinicio de conexao apos login bem sucedido...
|
|
163
|
+
if (isNewLogin && qr == undefined) {
|
|
164
|
+
this.qrCode = null;
|
|
165
|
+
this.status = ClientEvent.PAIRING_SUCCESS;
|
|
166
|
+
this.emit(ClientEvent.PAIRING_SUCCESS, update);
|
|
167
|
+
this.emit(ClientEvent.STATUS_CHANGE, this.status);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Dentro de 'connection.update'
|
|
172
|
+
switch (connection) {
|
|
173
|
+
case 'connecting':
|
|
174
|
+
this.status = ClientEvent.CONNECTING;
|
|
175
|
+
this.emit(ClientEvent.CONNECTING, update);
|
|
176
|
+
this.emit(ClientEvent.STATUS_CHANGE, this.status);
|
|
177
|
+
break;
|
|
178
|
+
|
|
179
|
+
case 'open':
|
|
180
|
+
this.connected = true;
|
|
181
|
+
this.manualDisconnect = false;
|
|
182
|
+
this.status = ClientEvent.CONNECTED;
|
|
183
|
+
this.emit(ClientEvent.CONNECTED, update);
|
|
184
|
+
this.emit(ClientEvent.STATUS_CHANGE, this.status);
|
|
185
|
+
this.presenceSetInterval = setInterval(() => {
|
|
186
|
+
if (this.sock?.sendPresenceUpdate) {
|
|
187
|
+
this.sock.sendPresenceUpdate('available').catch(() => { });
|
|
188
|
+
}
|
|
189
|
+
}, 30_000);
|
|
190
|
+
break;
|
|
191
|
+
|
|
192
|
+
case 'close':
|
|
193
|
+
// Cancelar o intervalo
|
|
194
|
+
if (this.presenceSetInterval) {
|
|
195
|
+
clearInterval(this.presenceSetInterval);
|
|
196
|
+
this.presenceSetInterval = null;
|
|
197
|
+
}
|
|
198
|
+
// Aqui entraria a lógica que já existe para tratar a desconexão
|
|
199
|
+
this.qrCode = null;
|
|
200
|
+
this.connected = false;
|
|
201
|
+
let disconnectReason, statusType;
|
|
202
|
+
const boomError = new Boom(lastDisconnect?.error);
|
|
203
|
+
const statusCode = boomError?.output?.statusCode;
|
|
204
|
+
const reasonType = boomError.data?.content?.[0]?.attrs?.type || boomError.data?.attrs?.type || 'unknown';
|
|
205
|
+
|
|
206
|
+
// Conexao fechada apos leitura bem-sucedida do qrcode para que uma nova conexao seja feita. Nao ha necessidade de disparar evento de desconexao
|
|
207
|
+
if (statusCode === DisconnectReason.restartRequired) {
|
|
208
|
+
return this.connect();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (this.manualDisconnect) {
|
|
212
|
+
// Desconexão manual solicitada pelo cliente.
|
|
213
|
+
statusType = DisconnectReasons.MANUAL_DISCONNECT;
|
|
214
|
+
disconnectReason = { statusCode, statusType, reason: reasonType, details: lastDisconnect };
|
|
215
|
+
} else if (statusCode === DisconnectReason.loggedOut) {
|
|
216
|
+
// 'Sessão deslogada por motivo desconhecido.'
|
|
217
|
+
statusType = DisconnectReasons.LOGGED_OUT;
|
|
218
|
+
disconnectReason = { statusCode, statusType, reason: reasonType, details: boomError.data };
|
|
219
|
+
try {
|
|
220
|
+
await fs.rm(this.authPath, { recursive: true, force: true });
|
|
221
|
+
// if (this.restartOnClose) this.connect();
|
|
222
|
+
} catch (err) {
|
|
223
|
+
this.emit(ClientEvent.ERROR, err);
|
|
224
|
+
}
|
|
225
|
+
} else if (statusCode === 408) {
|
|
226
|
+
// 'Sessão deslogada por motivo desconhecido.'
|
|
227
|
+
statusType = DisconnectReasons.PAIRING_FAILED;
|
|
228
|
+
disconnectReason = { statusCode, statusType, reason: 'qr_read_attempts_ended', details: boomError.data };
|
|
229
|
+
} else {
|
|
230
|
+
//'Conexão perdida por erro ou instabilidade.'
|
|
231
|
+
statusType = DisconnectReasons.CONNECTION_ERROR;
|
|
232
|
+
disconnectReason = { statusCode, statusType, reason: reasonType, details: lastDisconnect?.error };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
this.status = ClientEvent.DISCONNECTED;
|
|
236
|
+
this.emit(ClientEvent.DISCONNECTED, disconnectReason);
|
|
237
|
+
this.emit(ClientEvent.STATUS_CHANGE, this.status);
|
|
238
|
+
// Reinicia a conexao se o servidor originar desconexao, especialmente 503
|
|
239
|
+
if (statusCode === DisconnectReason.unavailableService) {
|
|
240
|
+
return this.connect();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Lógica de reconexão em caso de erros desconhecidos
|
|
244
|
+
if (statusType === DisconnectReasons.CONNECTION_ERROR) {
|
|
245
|
+
this.emit(ClientEvent.ERROR, update);
|
|
246
|
+
if (this.restartOnClose) {
|
|
247
|
+
this.connect();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// =================================================================================================
|
|
256
|
+
// OUTROS EVENTOS DE INTERAÇÃO
|
|
257
|
+
// =================================================================================================
|
|
258
|
+
|
|
259
|
+
this.sock.ev.on('call', (calls) => this.emit(ClientEvent.CALL, calls[0]));
|
|
260
|
+
this.sock.ev.on('groups.update', async ([event]) => {
|
|
261
|
+
this.emit(ClientEvent.GROUP_UPDATE, event);
|
|
262
|
+
const metadata = await this.sock.groupMetadata(event.id);
|
|
263
|
+
groupCache.set(event.id, metadata);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
this.sock.ev.on('group-participants.update', async (event) => {
|
|
267
|
+
this.emit(ClientEvent.GROUP_PARTICIPANTS_UPDATE, event);
|
|
268
|
+
const metadata = await this.sock.groupMetadata(event.id)
|
|
269
|
+
groupCache.set(event.id, metadata)
|
|
270
|
+
});
|
|
271
|
+
// this.sock.ev.on('groups.update', (updates) => this.emit(ClientEvent.GROUP_UPDATE, updates));
|
|
272
|
+
// this.sock.ev.on('group-participants.update', (update) => this.emit(ClientEvent.GROUP_PARTICIPANTS_UPDATE, update));
|
|
273
|
+
this.sock.ev.on('presence.update', (update) => this.emit(ClientEvent.PRESENCE_UPDATE, update));
|
|
274
|
+
this.sock.ev.on('contacts.update', (updates) => this.emit(ClientEvent.CONTACT_UPDATE, updates));
|
|
275
|
+
this.sock.ev.on('blocklist.update', (update) => this.emit(ClientEvent.BLOCKLIST_UPDATE, update));
|
|
276
|
+
this.sock.ev.on('chats.update', (updates) => this.emit(ClientEvent.CHAT_UPDATE, updates));
|
|
277
|
+
this.sock.ev.on('chats.delete', (jids) => this.emit(ClientEvent.CHAT_DELETE, jids));
|
|
278
|
+
|
|
279
|
+
this.sock.ev.on('messaging-history.set', (history) => {
|
|
280
|
+
try {
|
|
281
|
+
|
|
282
|
+
for (const chat of history.chats) {
|
|
283
|
+
const chatId = chat.id;
|
|
284
|
+
const messages = history.messages.filter(m => m.key.remoteJid === chatId);
|
|
285
|
+
for (const msg of messages) {
|
|
286
|
+
const nmsg = MessageNormalizer.normalize(msg, this);
|
|
287
|
+
if (nmsg && this.store && this.store.setMessage) this.store.setMessage(chatId, nmsg);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
this.emit(ClientEvent.MESSAGES_HISTORY_SYNC_DONE, history);
|
|
291
|
+
|
|
292
|
+
} catch (error) {
|
|
293
|
+
this.emit(ClientEvent.ERROR, error);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
this.sock.ev.on('messages.update', (updates) => this.emit(ClientEvent.MESSAGE_UPDATE, updates));
|
|
297
|
+
this.sock.ev.on('messages.delete', (item) => this.emit(ClientEvent.MESSAGE_DELETE, item));
|
|
298
|
+
this.sock.ev.on('messages.reaction', (reactions) => this.emit(ClientEvent.MESSAGE_REACTION, reactions));
|
|
299
|
+
this.sock.ev.on('messages.upsert', async (event) => {
|
|
300
|
+
try {
|
|
301
|
+
|
|
302
|
+
const { messages, type } = event;
|
|
303
|
+
const msg = messages[0];
|
|
304
|
+
if (!msg.message || msg.message.protocolMessage) {
|
|
305
|
+
this.emit(ClientEvent.NOTIFICATION, msg);
|
|
306
|
+
} else {
|
|
307
|
+
if (msg.broadcast || msg.key.remoteJid == 'status@broadcast') {
|
|
308
|
+
this.emit(ClientEvent.BROADCAST_MESSAGE, msg);
|
|
309
|
+
} else {
|
|
310
|
+
const nmsg = await MessageNormalizer.normalize(msg, this);
|
|
311
|
+
this.store.setMessage(nmsg.chatId, nmsg);
|
|
312
|
+
// Mensagens de conversacao
|
|
313
|
+
switch (type) {
|
|
314
|
+
case 'append':
|
|
315
|
+
this.emit(ClientEvent.MESSAGE_SENT, nmsg);
|
|
316
|
+
break;
|
|
317
|
+
|
|
318
|
+
case 'notify':
|
|
319
|
+
if (msg.key.fromMe) {
|
|
320
|
+
this.emit(ClientEvent.MESSAGE_SENT, nmsg);
|
|
321
|
+
} else {
|
|
322
|
+
this.emit(ClientEvent.MESSAGE_RECEIVED, nmsg);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
break;
|
|
326
|
+
default:
|
|
327
|
+
this.emit(ClientEvent.NOTIFICATION, nmsg);
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
} catch (error) {
|
|
334
|
+
this.emit(ClientEvent.ERROR, error);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Desconecta o cliente do WhatsApp de forma manual, sem apagar a sessão.
|
|
342
|
+
* @returns {Promise<void>}
|
|
343
|
+
*/
|
|
344
|
+
disconnect() {
|
|
345
|
+
try {
|
|
346
|
+
this.manualDisconnect = true;
|
|
347
|
+
return this.sock?.end();
|
|
348
|
+
} catch (error) {
|
|
349
|
+
this.emit(ClientEvent.ERROR, error);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/** Logout e remoção de dados de sessão */
|
|
354
|
+
async logout() {
|
|
355
|
+
try {
|
|
356
|
+
if (this.sock.logout) {
|
|
357
|
+
await this.sock.logout();
|
|
358
|
+
}
|
|
359
|
+
// Aqui você pode adicionar a limpeza local da sessão
|
|
360
|
+
} catch (err) {
|
|
361
|
+
this.emit(ClientEvent.ERROR, err);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Valida a conexão antes de executar uma ação.
|
|
367
|
+
* @private
|
|
368
|
+
* @throws {Error} Se o cliente não estiver conectado.
|
|
369
|
+
*/
|
|
370
|
+
_validateConnection() {
|
|
371
|
+
if (!this.sock || !this.connected) {
|
|
372
|
+
throw new Error('Cliente não está conectado.');
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
async decryptPollVote(vote, voteParams) {
|
|
377
|
+
return await decryptPollVote(vote, voteParams);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
jidNormalizedUser(id) {
|
|
381
|
+
return jidNormalizedUser(id);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
async downloadContentFromMessage(content, type) {
|
|
385
|
+
return await downloadContentFromMessage(content, type);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
async fileExists(path) {
|
|
389
|
+
try {
|
|
390
|
+
await fs.access(path, constants.F_OK);
|
|
391
|
+
return true;
|
|
392
|
+
} catch {
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Enumeração estática dos eventos emitidos pelo Client.
|
|
401
|
+
*/
|
|
402
|
+
class ClientEvent {
|
|
403
|
+
static INIT = 'init';
|
|
404
|
+
static CONNECTING = 'connecting';
|
|
405
|
+
static STATUS_CHANGE = 'status_change';
|
|
406
|
+
static ERROR = 'error';
|
|
407
|
+
static MESSAGE_RECEIVED = 'message_received';
|
|
408
|
+
static MESSAGE_SENT = 'message_sent';
|
|
409
|
+
static MESSAGE_UPDATE = 'message_update';
|
|
410
|
+
static MESSAGE_DELETE = 'message_delete';
|
|
411
|
+
static MESSAGE_REACTION = 'message_reaction';
|
|
412
|
+
static CALL = 'call';
|
|
413
|
+
static GROUP_UPDATE = 'group_update';
|
|
414
|
+
static GROUP_PARTICIPANTS_UPDATE = 'group_participants_update';
|
|
415
|
+
static PRESENCE_UPDATE = 'presence_update';
|
|
416
|
+
static CHAT_UPDATE = 'chat_update';
|
|
417
|
+
static CHAT_DELETE = 'chat_delete';
|
|
418
|
+
static CONTACT_UPDATE = 'contact_update';
|
|
419
|
+
static BLOCKLIST_UPDATE = 'blocklist_update';
|
|
420
|
+
static PAIRING_CODE = 'pairing_code';
|
|
421
|
+
static PAIRING_SUCCESS = 'pairing_success';
|
|
422
|
+
static DISCONNECTED = 'disconnected';
|
|
423
|
+
static CONNECTED = 'connected';
|
|
424
|
+
static MESSAGES_HISTORY_SYNC_DONE = 'messages_history_sync_done';
|
|
425
|
+
static BROADCAST_MESSAGE = 'broadcast_message';
|
|
426
|
+
static NOTIFICATION = 'notification';
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Enumeração estática dos motivos de desconexão.
|
|
431
|
+
*/
|
|
432
|
+
class DisconnectReasons {
|
|
433
|
+
static MANUAL_DISCONNECT = 'manual_disconnect';
|
|
434
|
+
static LOGGED_OUT = 'logged_out';
|
|
435
|
+
static PAIRING_FAILED = 'pairing_failed';
|
|
436
|
+
static CONNECTION_ERROR = 'connection_error';
|
|
437
|
+
static RESTART_REQUIRED = 'restart_required';
|
|
438
|
+
static UNAVAILABLE_SERVICE = 'unavailable_service';
|
|
439
|
+
static UNKNOWN = 'unknown';
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
module.exports = {
|
|
443
|
+
Client,
|
|
444
|
+
PresenceStatus,
|
|
445
|
+
ClientEvent,
|
|
446
|
+
DisconnectReasons,
|
|
447
|
+
InteractiveMessage,
|
|
448
|
+
QuickReplyButton,
|
|
449
|
+
UrlButton,
|
|
450
|
+
CopyCodeButton,
|
|
451
|
+
CallButton,
|
|
452
|
+
ListButton,
|
|
453
|
+
ListSection,
|
|
454
|
+
ListRow,
|
|
455
|
+
LocationButton
|
|
456
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@areumtecnologia/baileys",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "More baileys fork",
|
|
5
|
+
"license": "ISC",
|
|
6
|
+
"author": "Áreum Tecnologia",
|
|
7
|
+
"type": "commonjs",
|
|
8
|
+
"main": "index.js",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@itsukichan/baileys": "^7.3.2",
|
|
14
|
+
"baileys": "^7.0.0-rc.9"
|
|
15
|
+
}
|
|
16
|
+
}
|
package/tests/app.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const { Client, ClientEvent } = require('../index'); // Caminho para sua classe Client
|
|
2
|
+
// 1. Instanciação do Cliente atualizada para usar a nova classe
|
|
3
|
+
const client = new Client({
|
|
4
|
+
sessionName: sessionId,
|
|
5
|
+
restartOnClose: true,
|
|
6
|
+
markOnlineOnConnect: false,
|
|
7
|
+
enviroment: [account.name || 'MacOS', 'Chrome', '118.0.0.0'],
|
|
8
|
+
store: {
|
|
9
|
+
// getMessage: async (chatId, mid) => {
|
|
10
|
+
// const msg = (await messages.select([{ chat_id: chatId }, AND, { mid }]))[0];
|
|
11
|
+
// return msg;
|
|
12
|
+
// },
|
|
13
|
+
// setMessage: async (chatId, msg) => {
|
|
14
|
+
// // Nao implementa pois ja registra as mensagens nos eventos message_received e message_sent
|
|
15
|
+
// }
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
client.on(ClientEvent.STATUS_CHANGE, async (status) => {
|
|
20
|
+
console.log(new Date().toLocaleDateString('pt-br'), `[${sessionId}] Status alterado para: ${status}`);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
client.on(ClientEvent.PAIRING_CODE, async ({ qr }) => {
|
|
24
|
+
console.log(new Date().toLocaleDateString('pt-br'), `[${sessionId}] QR Code recebido. Enviando via webhook...`);
|
|
25
|
+
const base64 = await QRCode.toDataURL(qr);
|
|
26
|
+
client.qrCodeAttempts = (client.qrCodeAttempts || 0) + 1;
|
|
27
|
+
client.qrCode = { base64, url: qr, attempts: client.qrCodeAttempts, status: 'waiting' };
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
client.on(ClientEvent.PAIRING_SUCCESS, async (event) => {
|
|
31
|
+
console.log(new Date().toLocaleDateString('pt-br'), `[${sessionId}] QR Code lido com sucesso. Enviando via webhook...`);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
client.on(ClientEvent.CONNECTED, async () => {
|
|
35
|
+
console.log(new Date().toLocaleDateString('pt-br'), `[${sessionId}] Conectado com sucesso.`);
|
|
36
|
+
client.qrCode = null; // Limpa o QR code após a conexão
|
|
37
|
+
|
|
38
|
+
// Envia mensagem de boas-vindas usando o novo handler
|
|
39
|
+
const user = client.sock.user;
|
|
40
|
+
const welcomeMessage = `🤖 Olá, ${user.name || 'usuário'}!\nSua sessão (${sessionId}) foi conectada com sucesso.`;
|
|
41
|
+
await client.messages.sendTextMessage(user.id, welcomeMessage);
|
|
42
|
+
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
client.on(ClientEvent.DISCONNECTED, async (reason) => {
|
|
46
|
+
console.log(new Date().toLocaleDateString('pt-br'), `[${sessionId}] Desconectado. Motivo: ${reason.statusType}`, reason.details || '');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
client.on(ClientEvent.MESSAGE_RECEIVED, async (msg) => {
|
|
50
|
+
console.log(new Date().toLocaleDateString('pt-br'), `[${sessionId}] Mensagem recebida de ${msg.from}`);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
client.on(ClientEvent.MESSAGE_SENT, async (msg) => {
|
|
54
|
+
console.log(new Date().toLocaleDateString('pt-br'), `[${sessionId}] Mensagem enviada para ${msg.to}`);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
client.on(ClientEvent.CALL, async (call) => {
|
|
58
|
+
console.log(new Date().toLocaleDateString('pt-br'), `[${sessionId}] Chamada recebida de ${call.from}`);
|
|
59
|
+
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
client.on(ClientEvent.ERROR, (error) => {
|
|
63
|
+
console.error(new Date().toLocaleDateString('pt-br'), `[${sessionId}] Ocorreu um erro na instância do cliente:`, error, error?.lastDisconnect?.output);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
// Inicia a conexão
|
|
68
|
+
await client.connect();
|
|
Binary file
|
package/types/buttons.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Classe única para construir uma mensagem com múltiplos botões de diferentes tipos, sem método build
|
|
2
|
+
class MessageButton {
|
|
3
|
+
constructor({ title = '', text = '', footer = '' } = {}) {
|
|
4
|
+
this.title = title;
|
|
5
|
+
this.text = text;
|
|
6
|
+
this.footer = footer;
|
|
7
|
+
this.buttons = [];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Adiciona um botão de resposta rápida
|
|
11
|
+
addQuickReply(id, text) {
|
|
12
|
+
this.buttons.push({ id, text });
|
|
13
|
+
return this;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Adiciona um botão de URL
|
|
17
|
+
addUrlButton(display_text, url) {
|
|
18
|
+
this.buttons.push({
|
|
19
|
+
name: 'cta_url',
|
|
20
|
+
buttonParamsJson: JSON.stringify({ display_text, url })
|
|
21
|
+
});
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Adiciona um botão de copiar código
|
|
26
|
+
addCopyCodeButton(display_text, code) {
|
|
27
|
+
this.buttons.push({
|
|
28
|
+
name: 'copy_code',
|
|
29
|
+
buttonParamsJson: JSON.stringify({ display_text, code })
|
|
30
|
+
});
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Adiciona um botão de chamada telefônica
|
|
35
|
+
addCallButton(display_text, phone_number) {
|
|
36
|
+
this.buttons.push({
|
|
37
|
+
name: 'call',
|
|
38
|
+
buttonParamsJson: JSON.stringify({ display_text, phone_number })
|
|
39
|
+
});
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Permite usar o objeto diretamente
|
|
44
|
+
toJSON() {
|
|
45
|
+
return {
|
|
46
|
+
title: this.title,
|
|
47
|
+
text: this.text,
|
|
48
|
+
footer: this.footer,
|
|
49
|
+
buttons: this.buttons
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = { MessageButton };
|
|
55
|
+
|
|
56
|
+
/*
|
|
57
|
+
Exemplo de uso:
|
|
58
|
+
|
|
59
|
+
const { MessageButton } = require('./buttons');
|
|
60
|
+
|
|
61
|
+
const msg = new MessageButton({
|
|
62
|
+
title: 'Header Title',
|
|
63
|
+
text: 'Pick one option below',
|
|
64
|
+
footer: 'Footer text'
|
|
65
|
+
})
|
|
66
|
+
.addQuickReply('quick_1', 'Quick Reply')
|
|
67
|
+
.addUrlButton('Open Site', 'https://example.com')
|
|
68
|
+
.addCopyCodeButton('Copy Code', '123456')
|
|
69
|
+
.addCallButton('Call Us', '+5511999999999');
|
|
70
|
+
|
|
71
|
+
await sendButtons(this.sock, jid, msg); // msg será convertido automaticamente
|
|
72
|
+
*/
|
|
Binary file
|