@usebridger/transport-baileys 0.0.0-stage → 0.2.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/dist/index.mjs ADDED
@@ -0,0 +1,1184 @@
1
+ import { Browsers, BufferJSON, DisconnectReason, WAMessageStubType, decryptPollVote, downloadMediaMessage, fetchLatestBaileysVersion, initAuthCreds, isJidGroup, isJidStatusBroadcast, isLidUser, isPnUser, jidDecode, jidNormalizedUser, makeCacheableSignalKeyStore, makeWASocket, proto, toNumber } from "baileys";
2
+ import { Readable } from "node:stream";
3
+ import { TypedEmitter, createMessage } from "@usebridger/core/adapter";
4
+ import { UnsupportedError } from "@usebridger/core";
5
+ import { createHash } from "node:crypto";
6
+ //#region src/auth-state.ts
7
+ /**
8
+ * Monta o auth state de uma tentativa de conexão. As chaves passam por um cache em memória
9
+ * (o do Baileys, 5 min por chave): o Signal lê a sessão do remetente a cada mensagem, e sem ele
10
+ * cada leitura iria ao storage e repetiria a conversão do `BufferJSON`. O cache vive só nesta
11
+ * tentativa: depois de um `clear()` do bot, a próxima começa vazia e não ressuscita chave
12
+ * apagada. Ele não vê gravações de outro processo, o que não acontece porque a mesma sessão
13
+ * não roda em dois (ADR 0036).
14
+ */
15
+ async function loadAuthState(store, logger) {
16
+ const stored = await store.getCreds();
17
+ const creds = stored === void 0 ? initAuthCreds() : fromJson(stored);
18
+ const cached = makeCacheableSignalKeyStore({
19
+ get: async (type, ids) => {
20
+ const raw = await store.getKeys(type, ids);
21
+ const keys = {};
22
+ for (const [id, value] of Object.entries(raw)) keys[id] = type === "app-state-sync-key" ? toSyncKeyInstance(fromJson(value)) : fromJson(value);
23
+ return keys;
24
+ },
25
+ set: (data) => store.setKeys(toKeyData(data))
26
+ }, logger);
27
+ return {
28
+ state: {
29
+ creds,
30
+ keys: {
31
+ get: async (type, ids) => {
32
+ const found = await cached.get(type, ids);
33
+ for (const id of Object.keys(found)) if (found[id] === null || found[id] === void 0) delete found[id];
34
+ return found;
35
+ },
36
+ set: (data) => cached.set(withSyncKeyInstances(data))
37
+ }
38
+ },
39
+ saveCreds: () => store.setCreds(toJson(creds))
40
+ };
41
+ }
42
+ function toSyncKeyInstance(value) {
43
+ return value instanceof proto.Message.AppStateSyncKeyData ? value : proto.Message.AppStateSyncKeyData.fromObject(value);
44
+ }
45
+ function withSyncKeyInstances(data) {
46
+ const syncKeys = data["app-state-sync-key"];
47
+ if (syncKeys === void 0) return data;
48
+ const converted = {};
49
+ for (const [id, value] of Object.entries(syncKeys)) converted[id] = value ? toSyncKeyInstance(value) : value;
50
+ return {
51
+ ...data,
52
+ "app-state-sync-key": converted
53
+ };
54
+ }
55
+ function toKeyData(data) {
56
+ const out = {};
57
+ for (const [type, entries] of Object.entries(data)) {
58
+ const converted = {};
59
+ for (const [id, value] of Object.entries(entries ?? {})) converted[id] = value === null || value === void 0 ? null : toJson(value);
60
+ out[type] = converted;
61
+ }
62
+ return out;
63
+ }
64
+ function toJson(value) {
65
+ return JSON.parse(JSON.stringify(value, BufferJSON.replacer));
66
+ }
67
+ function fromJson(value) {
68
+ return JSON.parse(JSON.stringify(value), BufferJSON.reviver);
69
+ }
70
+ //#endregion
71
+ //#region src/disconnect-reason.ts
72
+ function toDisconnectReason(error, { pairing }) {
73
+ switch (statusCode(error)) {
74
+ case DisconnectReason.loggedOut: return "logged-out";
75
+ case DisconnectReason.forbidden:
76
+ case DisconnectReason.multideviceMismatch: return "auth-failed";
77
+ case DisconnectReason.connectionReplaced: return "replaced";
78
+ case DisconnectReason.timedOut: return pairing ? "qr-timeout" : "connection-lost";
79
+ case DisconnectReason.connectionClosed:
80
+ case DisconnectReason.restartRequired: return "connection-lost";
81
+ case DisconnectReason.badSession:
82
+ case DisconnectReason.unavailableService:
83
+ case 405: return "server-error";
84
+ default: return "unknown";
85
+ }
86
+ }
87
+ /** O Baileys encerra com um `Boom`, que traz o código em `output.statusCode`. */
88
+ function statusCode(error) {
89
+ if (typeof error !== "object" || error === null || !("output" in error)) return void 0;
90
+ const { output } = error;
91
+ if (typeof output !== "object" || output === null || !("statusCode" in output)) return void 0;
92
+ return typeof output.statusCode === "number" ? output.statusCode : void 0;
93
+ }
94
+ //#endregion
95
+ //#region src/normalize.ts
96
+ /** Envelopes que só embrulham o conteúdo de verdade. */
97
+ const ENVELOPES = [
98
+ "ephemeralMessage",
99
+ "viewOnceMessage",
100
+ "viewOnceMessageV2",
101
+ "viewOnceMessageV2Extension",
102
+ "documentWithCaptionMessage",
103
+ "pollCreationMessageV4"
104
+ ];
105
+ const VIEW_ONCE = /* @__PURE__ */ new Set([
106
+ "viewOnceMessage",
107
+ "viewOnceMessageV2",
108
+ "viewOnceMessageV2Extension"
109
+ ]);
110
+ /**
111
+ * Campos que não são o conteúdo da mensagem. Edição, apagamento e reação chegam por eventos
112
+ * próprios do Baileys (`events.ts`); o resto é metadado do protocolo.
113
+ */
114
+ const CONTROL = /* @__PURE__ */ new Set([
115
+ "protocolMessage",
116
+ "reactionMessage",
117
+ "encReactionMessage",
118
+ "pollUpdateMessage",
119
+ "keepInChatMessage",
120
+ "pinInChatMessage",
121
+ "senderKeyDistributionMessage",
122
+ "fastRatchetKeySenderKeyDistributionMessage",
123
+ "messageContextInfo"
124
+ ]);
125
+ /**
126
+ * Tira os envelopes, em qualquer ordem e aninhados (ephemeral com viewOnce dentro, por exemplo).
127
+ * O limite só protege de um proto malformado.
128
+ */
129
+ function unwrap(content) {
130
+ let current = content;
131
+ let viewOnce = false;
132
+ for (let depth = 0; depth < 10; depth++) {
133
+ const envelope = ENVELOPES.find((name) => current[name]?.message);
134
+ if (envelope === void 0) break;
135
+ if (VIEW_ONCE.has(envelope)) viewOnce = true;
136
+ current = current[envelope]?.message ?? current;
137
+ }
138
+ return {
139
+ content: current,
140
+ viewOnce
141
+ };
142
+ }
143
+ /** Campo que define o tipo: o primeiro preenchido que não é de controle. */
144
+ function contentKey(content) {
145
+ return Object.keys(content).find((key) => content[key] != null && !CONTROL.has(key));
146
+ }
147
+ /**
148
+ * Converte uma mensagem do Baileys. `null` quando não há o que entregar: sem conteúdo (aviso de
149
+ * grupo, mensagem que não deu para decifrar) ou só controle (edição, reação, apagamento).
150
+ */
151
+ function toMessage(raw, env) {
152
+ return build(raw, env, toNumber(raw.messageTimestamp) * 1e3, false);
153
+ }
154
+ /**
155
+ * Nova versão de uma mensagem editada, com `isEdited: true`. `raw` é a chave da original com o
156
+ * conteúdo novo; o Baileys não manda o `pushName` na edição, então o nome vem de quem chama.
157
+ */
158
+ function toEditedMessage(raw, env) {
159
+ return build(raw, env, toNumber(raw.messageTimestamp) * 1e3, true);
160
+ }
161
+ async function build(raw, env, timestamp, isEdited) {
162
+ const chatId = raw.key.remoteJid;
163
+ const id = raw.key.id;
164
+ if (!raw.message || !chatId || !id) return null;
165
+ const { content, viewOnce } = unwrap(raw.message);
166
+ const key = contentKey(content);
167
+ if (key === void 0) return null;
168
+ const isGroup = isJidGroup(chatId) === true;
169
+ const fromMe = raw.key.fromMe === true;
170
+ const node = content[key];
171
+ const context = contextOf(node);
172
+ const base = {
173
+ id,
174
+ chat: {
175
+ id: chatId,
176
+ isGroup
177
+ },
178
+ sender: await authorOf(raw.key, chatId, env, raw.pushName ?? null),
179
+ timestamp,
180
+ fromMe,
181
+ quoted: context ? await quoted(context, chatId, env, timestamp) : null,
182
+ mentions: await Promise.all((context?.mentionedJid ?? []).map((jid) => resolveContact(env, jid, void 0, null))),
183
+ isForwarded: context?.isForwarded === true,
184
+ isViewOnce: viewOnce || raw.key.isViewOnce === true || flag(node, "viewOnce"),
185
+ isEdited
186
+ };
187
+ const unwrapped = {
188
+ ...raw,
189
+ message: content
190
+ };
191
+ const media = (m) => ({
192
+ mimetype: m.mimetype ?? "application/octet-stream",
193
+ size: m.fileLength == null ? null : toNumber(m.fileLength) || null,
194
+ download: () => env.download(unwrapped),
195
+ stream: () => env.stream(unwrapped)
196
+ });
197
+ const message = createMessage(init(key, content, base, media));
198
+ natives.set(message, raw);
199
+ return message;
200
+ }
201
+ /**
202
+ * Mensagem do Baileys de onde cada `Message` saiu. Citar no envio precisa do proto original
203
+ * (`quoted` do `sendMessage`); o `WeakMap` some com a `Message`, sem cache para limpar.
204
+ */
205
+ const natives = /* @__PURE__ */ new WeakMap();
206
+ /** O `WAMessage` que originou a mensagem, se ela foi normalizada por este transport. */
207
+ function nativeOf(message) {
208
+ return natives.get(message);
209
+ }
210
+ function init(key, c, base, media) {
211
+ switch (key) {
212
+ case "conversation": return {
213
+ ...base,
214
+ type: "text",
215
+ text: c.conversation ?? ""
216
+ };
217
+ case "extendedTextMessage": return {
218
+ ...base,
219
+ type: "text",
220
+ text: c.extendedTextMessage?.text ?? ""
221
+ };
222
+ case "imageMessage": {
223
+ const m = c.imageMessage ?? {};
224
+ return {
225
+ ...base,
226
+ type: "image",
227
+ text: m.caption ?? null,
228
+ media: media(m)
229
+ };
230
+ }
231
+ case "videoMessage":
232
+ case "ptvMessage": {
233
+ const m = c[key] ?? {};
234
+ return {
235
+ ...base,
236
+ type: "video",
237
+ text: m.caption ?? null,
238
+ media: media(m)
239
+ };
240
+ }
241
+ case "audioMessage": {
242
+ const m = c.audioMessage ?? {};
243
+ return {
244
+ ...base,
245
+ type: m.ptt ? "voice" : "audio",
246
+ text: null,
247
+ media: media(m)
248
+ };
249
+ }
250
+ case "stickerMessage": return {
251
+ ...base,
252
+ type: "sticker",
253
+ text: null,
254
+ media: media(c.stickerMessage ?? {})
255
+ };
256
+ case "documentMessage": {
257
+ const m = c.documentMessage ?? {};
258
+ return {
259
+ ...base,
260
+ type: "document",
261
+ text: m.caption ?? null,
262
+ fileName: m.fileName ?? null,
263
+ media: media(m)
264
+ };
265
+ }
266
+ case "locationMessage": {
267
+ const m = c.locationMessage ?? {};
268
+ return {
269
+ ...base,
270
+ type: "location",
271
+ text: m.comment ?? null,
272
+ location: {
273
+ latitude: m.degreesLatitude ?? 0,
274
+ longitude: m.degreesLongitude ?? 0,
275
+ name: m.name ?? m.address ?? null,
276
+ ...m.address ? { address: m.address } : {}
277
+ }
278
+ };
279
+ }
280
+ case "liveLocationMessage": {
281
+ const m = c.liveLocationMessage ?? {};
282
+ return {
283
+ ...base,
284
+ type: "location",
285
+ text: m.caption ?? null,
286
+ location: {
287
+ latitude: m.degreesLatitude ?? 0,
288
+ longitude: m.degreesLongitude ?? 0,
289
+ name: null
290
+ }
291
+ };
292
+ }
293
+ case "contactMessage":
294
+ case "contactsArrayMessage": {
295
+ const list = key === "contactMessage" ? [c.contactMessage ?? {}] : c.contactsArrayMessage?.contacts ?? [];
296
+ return {
297
+ ...base,
298
+ type: "contact",
299
+ text: null,
300
+ contacts: list.map((m) => ({
301
+ name: m.displayName ?? "",
302
+ vcard: m.vcard ?? ""
303
+ }))
304
+ };
305
+ }
306
+ case "pollCreationMessage":
307
+ case "pollCreationMessageV2":
308
+ case "pollCreationMessageV3":
309
+ case "pollCreationMessageV5": {
310
+ const m = c[key] ?? {};
311
+ return {
312
+ ...base,
313
+ type: "poll",
314
+ text: null,
315
+ poll: {
316
+ name: m.name ?? "",
317
+ options: (m.options ?? []).map((o) => o.optionName ?? "")
318
+ }
319
+ };
320
+ }
321
+ default: return {
322
+ ...base,
323
+ type: "unknown",
324
+ text: null
325
+ };
326
+ }
327
+ }
328
+ function contextOf(node) {
329
+ if (typeof node !== "object" || node === null || !("contextInfo" in node)) return null;
330
+ return node.contextInfo ?? null;
331
+ }
332
+ function flag(node, name) {
333
+ return typeof node === "object" && node !== null && name in node && Reflect.get(node, name) === true;
334
+ }
335
+ /**
336
+ * A citada vem só com id, autor e conteúdo: sem horário (vale o da mensagem que cita) e sem nome
337
+ * do autor.
338
+ */
339
+ function quoted(context, chatId, env, timestamp) {
340
+ if (!context.quotedMessage || !context.stanzaId) return Promise.resolve(null);
341
+ const author = context.participant ? jidNormalizedUser(context.participant) : void 0;
342
+ return build({
343
+ key: {
344
+ remoteJid: context.remoteJid ?? chatId,
345
+ id: context.stanzaId,
346
+ fromMe: author !== void 0 && env.selfIds.includes(author),
347
+ participant: author
348
+ },
349
+ message: context.quotedMessage
350
+ }, env, timestamp, false);
351
+ }
352
+ /**
353
+ * Autor de uma mensagem pela chave. Grupo, status e citada o trazem em `participant`. Sem ele
354
+ * (conversa privada), o autor é o próprio chat, ou a sessão quando foi ela que mandou.
355
+ */
356
+ function authorOf(key, chatId, env, name) {
357
+ const alt = key.participant ? key.participantAlt : key.fromMe ? void 0 : key.remoteJidAlt;
358
+ return resolveContact(env, authorJid(key, chatId, env.selfIds), alt, name);
359
+ }
360
+ /** O JID do autor pela chave, sem resolver o telefone (ver `authorOf`). */
361
+ function authorJid(key, chatId, selfIds) {
362
+ if (key.participant) return key.participant;
363
+ return key.fromMe ? selfIds[0] ?? chatId : chatId;
364
+ }
365
+ /**
366
+ * Contato com o telefone resolvido: do próprio JID de telefone, do JID alternativo que o
367
+ * Baileys manda junto com um LID ou do mapeamento LID ↔ telefone da sessão.
368
+ */
369
+ async function resolveContact(env, jid, alt, name) {
370
+ const id = jidNormalizedUser(jid) || jid;
371
+ return {
372
+ id,
373
+ name,
374
+ phone: await phoneOf(env, id, alt)
375
+ };
376
+ }
377
+ async function phoneOf(env, id, alt) {
378
+ if (isPnUser(id)) return jidDecode(id)?.user ?? null;
379
+ if (alt && isPnUser(alt)) return jidDecode(alt)?.user ?? null;
380
+ if (!isLidUser(id)) return null;
381
+ const pn = await env.pnForLid(id);
382
+ return pn ? jidDecode(pn)?.user ?? null : null;
383
+ }
384
+ //#endregion
385
+ //#region src/events.ts
386
+ /**
387
+ * Nome e telefone já vistos de cada contato. Decide quando sai o `contact.updated` e dá nome a
388
+ * quem aparece sem `pushName` (reação, edição, apagamento, grupo). Vive enquanto o transport
389
+ * vive: depois de reiniciar o processo, cada contato conta de novo como visto pela primeira vez.
390
+ */
391
+ var ContactBook = class {
392
+ #known = /* @__PURE__ */ new Map();
393
+ /**
394
+ * Registra um remetente. Devolve o `contact.updated` quando há o que contar: na primeira vez
395
+ * que o contato aparece (ADR 0049) ou quando o nome ou o telefone mudou. Campo desconhecido
396
+ * (`null`) não apaga o que já se sabia.
397
+ */
398
+ observe(contact) {
399
+ const known = this.#known.get(contact.id);
400
+ const name = contact.name ?? known?.name ?? null;
401
+ const phone = contact.phone ?? known?.phone ?? null;
402
+ this.#known.set(contact.id, {
403
+ name,
404
+ phone
405
+ });
406
+ const update = { id: contact.id };
407
+ if (name !== null && name !== known?.name) update.name = name;
408
+ if (phone !== null && phone !== known?.phone) update.phone = phone;
409
+ return update.name === void 0 && update.phone === void 0 ? null : update;
410
+ }
411
+ nameOf(id) {
412
+ return this.#known.get(id)?.name ?? null;
413
+ }
414
+ /** O contato com o nome já visto, quando o evento não trouxe nenhum. */
415
+ named(contact) {
416
+ return contact.name === null ? {
417
+ ...contact,
418
+ name: this.nameOf(contact.id)
419
+ } : contact;
420
+ }
421
+ };
422
+ /** O `Chat` de um JID; os eventos de grupo também o usam (ADR 0059). */
423
+ function chatOf(id) {
424
+ return {
425
+ id,
426
+ isGroup: isJidGroup(id) === true
427
+ };
428
+ }
429
+ /**
430
+ * `reaction` a partir do `messages.reaction`: `key` é a mensagem reagida e `reaction.key` a
431
+ * mensagem de reação, que diz quem reagiu. Texto vazio é reação removida.
432
+ */
433
+ async function toReaction({ key, reaction }, env, contacts) {
434
+ const from = reaction.key;
435
+ const chatId = from?.remoteJid ?? key.remoteJid;
436
+ if (!from || !chatId || !key.id || isJidStatusBroadcast(chatId)) return null;
437
+ return {
438
+ chat: chatOf(chatId),
439
+ messageId: key.id,
440
+ sender: contacts.named(await authorOf(from, chatId, env, null)),
441
+ emoji: reaction.text || null,
442
+ fromMe: from.fromMe === true
443
+ };
444
+ }
445
+ /**
446
+ * `message.edited` a partir do `messages.update` de edição. A chave é a da original com o autor
447
+ * da edição, e o conteúdo novo vem em `editedMessage`.
448
+ */
449
+ function toEdited({ key, update }, env, contacts) {
450
+ const content = update.message?.editedMessage?.message;
451
+ const chatId = key.remoteJid;
452
+ if (!content || !chatId || isJidStatusBroadcast(chatId)) return Promise.resolve(null);
453
+ return toEditedMessage({
454
+ key,
455
+ message: content,
456
+ messageTimestamp: update.messageTimestamp ?? Math.floor(Date.now() / 1e3),
457
+ pushName: contacts.nameOf(jidNormalizedUser(authorJid(key, chatId, env.selfIds)))
458
+ }, env);
459
+ }
460
+ /**
461
+ * `message.deleted` a partir do `messages.update` de apagamento (`REVOKE`). `key` é a apagada;
462
+ * `update.key` é a mensagem que apagou, que diz quem apagou (o autor ou um admin do grupo).
463
+ */
464
+ async function toDeleted({ key, update }, env, contacts) {
465
+ const chatId = key.remoteJid;
466
+ if (!chatId || !key.id || isJidStatusBroadcast(chatId)) return null;
467
+ const by = update.key;
468
+ return {
469
+ chat: chatOf(chatId),
470
+ messageId: key.id,
471
+ deletedBy: by ? contacts.named(await authorOf(by, chatId, env, null)) : null,
472
+ fromMe: (by ?? key).fromMe === true
473
+ };
474
+ }
475
+ /**
476
+ * Converte a alteração de participantes. Entrada e saída da própria sessão viram
477
+ * `group.joined`/`group.left` e saem da lista; promovida ou rebaixada, ela segue em
478
+ * `group.participants`. Troca de número (`modify`) não tem evento no core.
479
+ */
480
+ async function toParticipantEvents({ id, author, authorPn, participants, action }, env, contacts) {
481
+ if (action === "modify") return {
482
+ self: null,
483
+ others: null
484
+ };
485
+ const isSelf = (p) => [
486
+ p.id,
487
+ p.phoneNumber,
488
+ p.lid
489
+ ].some((jid) => jid && env.selfIds.includes(jidNormalizedUser(jid)));
490
+ const self = !participants.some(isSelf) ? null : action === "add" ? "group.joined" : action === "remove" ? "group.left" : null;
491
+ const rest = self === null ? participants : participants.filter((p) => !isSelf(p));
492
+ if (rest.length === 0) return {
493
+ self,
494
+ others: null
495
+ };
496
+ const contact = async (jid, alt, name) => contacts.named(await resolveContact(env, jid, alt, name));
497
+ return {
498
+ self,
499
+ others: {
500
+ chat: chatOf(id),
501
+ action,
502
+ participants: await Promise.all(rest.map((p) => contact(p.id, p.phoneNumber, p.notify ?? p.name ?? null))),
503
+ actor: author ? await contact(author, authorPn, null) : null
504
+ }
505
+ };
506
+ }
507
+ /**
508
+ * `group.updated` com só os campos que mudaram. O Baileys também emite `groups.update` com os
509
+ * metadados completos de cada grupo ao sincronizar (`groupFetchAllParticipating`); esse traz
510
+ * `participants` e não é uma alteração, então fica de fora.
511
+ */
512
+ function toGroupUpdated(update) {
513
+ if (!update.id || update.participants) return null;
514
+ const changed = { chat: chatOf(update.id) };
515
+ if (update.subject !== void 0) changed.title = update.subject;
516
+ if ("desc" in update) changed.description = update.desc || null;
517
+ if (update.announce !== void 0) changed.announce = update.announce;
518
+ if (update.restrict !== void 0) changed.restrict = update.restrict;
519
+ return Object.keys(changed).length > 1 ? changed : null;
520
+ }
521
+ //#endregion
522
+ //#region src/format.ts
523
+ /**
524
+ * Renderiza a árvore. O WhatsApp não tem escape: um `*` literal do texto pode casar com outro e
525
+ * virar negrito, como já acontece no app. Por isso o texto literal vai como está.
526
+ */
527
+ function renderWhatsApp(text) {
528
+ const mentions = /* @__PURE__ */ new Set();
529
+ const render = (nodes) => {
530
+ let out = "";
531
+ for (const node of nodes) {
532
+ if (typeof node === "string") {
533
+ out += node;
534
+ continue;
535
+ }
536
+ switch (node.type) {
537
+ case "bold":
538
+ case "italic":
539
+ out += wrap(node.type === "bold" ? "*" : "_", render(node.children));
540
+ break;
541
+ case "code":
542
+ out += wrap("`", node.text);
543
+ break;
544
+ case "link": {
545
+ const label = render(node.children);
546
+ out += label === node.url ? node.url : `${label} (${node.url})`;
547
+ break;
548
+ }
549
+ case "mention":
550
+ mentions.add(node.contact.id);
551
+ out += `@${jidDecode(node.contact.id)?.user ?? node.contact.id}`;
552
+ }
553
+ }
554
+ return out;
555
+ };
556
+ return {
557
+ text: render(text.nodes),
558
+ mentions: [...mentions]
559
+ };
560
+ }
561
+ /**
562
+ * O marcador só vale colado no texto (`* a *` sai literal): o espaço das pontas fica do lado de
563
+ * fora.
564
+ */
565
+ function wrap(marker, inner) {
566
+ const [, lead = "", core = "", trail = ""] = /^(\s*)([\s\S]*?)(\s*)$/.exec(inner) ?? [];
567
+ if (core === "") return inner;
568
+ return `${lead}${marker}${core}${marker}${trail}`;
569
+ }
570
+ //#endregion
571
+ //#region src/logger.ts
572
+ function toBaileysLogger(log) {
573
+ const write = (method) => (obj, msg) => {
574
+ if (typeof obj === "string") log[method](obj);
575
+ else log[method](msg ?? "", toFields(obj));
576
+ };
577
+ return {
578
+ get level() {
579
+ return log.level;
580
+ },
581
+ child: (bindings) => toBaileysLogger(log.child(bindings)),
582
+ trace: write("trace"),
583
+ debug: write("debug"),
584
+ info: write("info"),
585
+ warn: write("warn"),
586
+ error: write("error")
587
+ };
588
+ }
589
+ function toFields(obj) {
590
+ if (obj instanceof Error) return { err: obj };
591
+ if (typeof obj === "object" && obj !== null) return obj;
592
+ return obj === void 0 ? void 0 : { value: obj };
593
+ }
594
+ //#endregion
595
+ //#region src/outgoing.ts
596
+ /**
597
+ * Conteúdo do `sendMessage`, com as menções que o Baileys põe no `contextInfo` de qualquer tipo.
598
+ * A árvore neutra (`formatted`, `formattedCaption`) vira a marcação do WhatsApp, e as menções
599
+ * dela se juntam às de `options.mentions` (ADR 0061).
600
+ */
601
+ function toContent(content, options) {
602
+ const ids = new Set(options?.mentions);
603
+ let text;
604
+ let caption;
605
+ if (content.type === "text") {
606
+ text = content.text;
607
+ if (content.formatted) {
608
+ const rendered = renderWhatsApp(content.formatted);
609
+ text = rendered.text;
610
+ for (const id of rendered.mentions) ids.add(id);
611
+ }
612
+ } else if ("caption" in content || "formattedCaption" in content) {
613
+ caption = content.caption;
614
+ if (content.formattedCaption) {
615
+ const rendered = renderWhatsApp(content.formattedCaption);
616
+ caption = rendered.text;
617
+ for (const id of rendered.mentions) ids.add(id);
618
+ }
619
+ }
620
+ const mentions = ids.size > 0 ? { mentions: [...ids] } : {};
621
+ switch (content.type) {
622
+ case "text": return {
623
+ text: text ?? content.text,
624
+ ...mentions
625
+ };
626
+ case "image": return {
627
+ image: content.media,
628
+ caption,
629
+ mimetype: content.mimetype,
630
+ ...mentions
631
+ };
632
+ case "video": return {
633
+ video: content.media,
634
+ caption,
635
+ mimetype: content.mimetype,
636
+ ...mentions
637
+ };
638
+ case "audio":
639
+ case "voice": return {
640
+ audio: content.media,
641
+ ptt: content.type === "voice",
642
+ mimetype: content.mimetype,
643
+ ...mentions
644
+ };
645
+ case "sticker": return {
646
+ sticker: content.media,
647
+ ...mentions
648
+ };
649
+ case "document": return {
650
+ document: content.media,
651
+ fileName: content.fileName,
652
+ mimetype: content.mimetype,
653
+ caption,
654
+ ...mentions
655
+ };
656
+ case "album": throw new UnsupportedError("send.album", "baileys");
657
+ case "poll": return {
658
+ poll: {
659
+ name: content.name,
660
+ values: [...content.options],
661
+ selectableCount: content.multiple === true ? 0 : 1
662
+ },
663
+ ...mentions
664
+ };
665
+ }
666
+ }
667
+ function toWAKey(key) {
668
+ return {
669
+ remoteJid: key.chatId,
670
+ id: key.id,
671
+ fromMe: key.fromMe,
672
+ participant: key.senderId ?? void 0
673
+ };
674
+ }
675
+ /**
676
+ * A mensagem a citar como o Baileys a quer. Recebida por este transport, vai o proto original
677
+ * (a citada mostra mídia e legenda); montada fora dele (testes, storage), vai só o texto.
678
+ */
679
+ function toQuoted(message) {
680
+ return nativeOf(message) ?? {
681
+ key: toWAKey(message.key),
682
+ message: { conversation: message.text ?? "" }
683
+ };
684
+ }
685
+ /** Metadados do grupo com o telefone de cada participante resolvido, inclusive com LID. */
686
+ async function toGroupMetadata(native, env) {
687
+ const participants = await Promise.all(native.participants.map(async (p) => {
688
+ return {
689
+ ...await resolveContact(env, p.id, p.phoneNumber, p.name ?? p.notify ?? null),
690
+ isAdmin: p.admin === "admin" || p.admin === "superadmin",
691
+ isSuperAdmin: p.admin === "superadmin"
692
+ };
693
+ }));
694
+ return {
695
+ id: native.id,
696
+ title: native.subject,
697
+ description: native.desc ?? null,
698
+ ownerId: native.owner ? jidNormalizedUser(native.owner) || native.owner : null,
699
+ participants
700
+ };
701
+ }
702
+ //#endregion
703
+ //#region src/polls.ts
704
+ /** Enquetes guardadas por padrão; a mais antiga sai primeiro. */
705
+ const DEFAULT_MAX_POLLS = 1e3;
706
+ /**
707
+ * Enquetes que a sessão enviou ou recebeu enquanto conectada, pelo ID da mensagem. O ID basta: o
708
+ * mesmo chat privado aparece ora como LID, ora como telefone, e o ID do WhatsApp é aleatório. Vive
709
+ * até o `disconnect()`: voto de enquete anterior não se decifra e é descartado.
710
+ */
711
+ var PollBook = class {
712
+ #polls = /* @__PURE__ */ new Map();
713
+ #max;
714
+ constructor(max = DEFAULT_MAX_POLLS) {
715
+ this.#max = max;
716
+ }
717
+ /** Guarda a enquete, se a mensagem for uma e trouxer o segredo; o resto é ignorado. */
718
+ remember(raw) {
719
+ const id = raw.key.id;
720
+ if (!id || !raw.message) return;
721
+ const { content } = unwrap(raw.message);
722
+ const poll = content.pollCreationMessage ?? content.pollCreationMessageV2 ?? content.pollCreationMessageV3 ?? content.pollCreationMessageV5;
723
+ const secret = raw.message.messageContextInfo?.messageSecret ?? content.messageContextInfo?.messageSecret;
724
+ if (!poll || !secret) return;
725
+ this.#polls.delete(id);
726
+ this.#polls.set(id, {
727
+ key: raw.key,
728
+ secret,
729
+ hashes: (poll.options ?? []).map((o) => sha256Hex(o.optionName ?? ""))
730
+ });
731
+ if (this.#polls.size > this.#max) {
732
+ const oldest = this.#polls.keys().next().value;
733
+ if (oldest !== void 0) this.#polls.delete(oldest);
734
+ }
735
+ }
736
+ get(id) {
737
+ return this.#polls.get(id);
738
+ }
739
+ clear() {
740
+ this.#polls.clear();
741
+ }
742
+ };
743
+ /** O `pollUpdateMessage` da mensagem, se ela for um voto. */
744
+ function pollUpdateOf(raw) {
745
+ return raw.message ? unwrap(raw.message).content.pollUpdateMessage ?? null : null;
746
+ }
747
+ /**
748
+ * `poll.vote` a partir do `pollUpdateMessage`. Devolve `null` quando a enquete não está no
749
+ * `PollBook` (enviada antes da conexão atual, por exemplo) e lança quando ela está, mas o voto não
750
+ * decifra.
751
+ */
752
+ async function toPollVote(raw, polls, env, contacts) {
753
+ const update = pollUpdateOf(raw);
754
+ const pollKey = update?.pollCreationMessageKey;
755
+ const chatId = raw.key.remoteJid;
756
+ if (!update?.vote || !pollKey?.id || !chatId) return null;
757
+ const poll = polls.get(pollKey.id);
758
+ if (poll === void 0) return null;
759
+ const vote = decrypt(update.vote, poll, pollKey, raw.key, env.selfIds);
760
+ if (vote === null) throw new Error("baileys: o voto da enquete não decifrou");
761
+ const options = /* @__PURE__ */ new Set();
762
+ for (const hash of vote.selectedOptions ?? []) {
763
+ const index = poll.hashes.indexOf(Buffer.from(hash).toString("hex"));
764
+ if (index >= 0) options.add(index);
765
+ }
766
+ return {
767
+ chat: chatOf(chatId),
768
+ messageId: pollKey.id,
769
+ sender: contacts.named(await authorOf(raw.key, chatId, env, raw.pushName ?? null)),
770
+ options: [...options].sort((a, b) => a - b),
771
+ fromMe: raw.key.fromMe === true
772
+ };
773
+ }
774
+ /**
775
+ * Decifra o voto. A chave sai do JID de quem criou e de quem votou, como o aparelho do votante os
776
+ * viu, e com o LID ele pode ter usado o telefone ou o LID: tenta cada par conhecido.
777
+ */
778
+ function decrypt(vote, poll, pollKey, voteKey, selfIds) {
779
+ const creators = unique([...jidsOf(poll.key, selfIds), ...jidsOf(pollKey, selfIds)]);
780
+ const voters = unique(jidsOf(voteKey, selfIds));
781
+ const pollMsgId = pollKey.id ?? "";
782
+ for (const pollCreatorJid of creators) for (const voterJid of voters) {
783
+ const decrypted = tryDecrypt(vote, {
784
+ pollCreatorJid,
785
+ pollMsgId,
786
+ pollEncKey: poll.secret,
787
+ voterJid
788
+ });
789
+ if (decrypted !== null) return decrypted;
790
+ }
791
+ return null;
792
+ }
793
+ function tryDecrypt(vote, context) {
794
+ try {
795
+ return decryptPollVote(vote, context);
796
+ } catch {
797
+ return null;
798
+ }
799
+ }
800
+ /** JIDs possíveis do autor de uma chave: o da sessão, o do participante ou o do chat privado. */
801
+ function jidsOf(key, selfIds) {
802
+ if (key.fromMe) return [...selfIds];
803
+ return (key.participant ? [key.participant, key.participantAlt] : [key.remoteJid, key.remoteJidAlt]).flatMap((jid) => jid ? [jidNormalizedUser(jid) || jid] : []);
804
+ }
805
+ function unique(values) {
806
+ return [...new Set(values)];
807
+ }
808
+ function sha256Hex(text) {
809
+ return createHash("sha256").update(text).digest("hex");
810
+ }
811
+ //#endregion
812
+ //#region src/transport.ts
813
+ var BaileysTransport = class {
814
+ name = "baileys";
815
+ /**
816
+ * Todas as capabilities do core, menos `actions`: o WhatsApp pelo Baileys não tem botões
817
+ * confiáveis, e o kernel envia o menu em texto numerado (ADR 0062).
818
+ */
819
+ capabilities = /* @__PURE__ */ new Set([
820
+ "groups",
821
+ "groups.add",
822
+ "groups.remove",
823
+ "groups.promote",
824
+ "mentions",
825
+ "reactions",
826
+ "typing",
827
+ "send.text",
828
+ "send.image",
829
+ "send.video",
830
+ "send.audio",
831
+ "send.voice",
832
+ "send.sticker",
833
+ "send.document",
834
+ "media.download",
835
+ "message.edit",
836
+ "message.delete",
837
+ "polls",
838
+ "quoted",
839
+ "pairing"
840
+ ]);
841
+ #pairing;
842
+ #driver;
843
+ #deps;
844
+ #events;
845
+ #socket = null;
846
+ #self = null;
847
+ /** Sobe a cada `connect()`/`disconnect()`: invalida a tentativa que ainda estava começando. */
848
+ #attempt = 0;
849
+ /**
850
+ * Metadados por grupo (ADR 0046): o kernel os pede a cada comando `group-admin`. Invalidado
851
+ * pelos eventos de grupo do Baileys e a cada conexão, porque a queda pode ter perdido algum.
852
+ */
853
+ #groups = /* @__PURE__ */ new Map();
854
+ /** Contatos já vistos, entre conexões: o `contact.updated` sai só no que é novo. */
855
+ #contacts = new ContactBook();
856
+ /** Enquetes vistas, para decifrar os votos (ADR 0071); o `disconnect()` as esquece. */
857
+ #polls = new PollBook();
858
+ constructor(options, deps) {
859
+ this.#pairing = options.pairing;
860
+ this.#driver = options.driver;
861
+ this.#deps = deps;
862
+ this.#events = new TypedEmitter((error, event) => this.#log.error("handler de evento do transport falhou", {
863
+ err: error,
864
+ event
865
+ }));
866
+ }
867
+ get self() {
868
+ return this.#self;
869
+ }
870
+ /** O socket do Baileys (`WASocket`) da tentativa atual, ou `null` sem conexão. */
871
+ get native() {
872
+ return this.#socket;
873
+ }
874
+ /**
875
+ * O `WAMessage` de onde a mensagem saiu (ADR 0066), o mesmo que o envio usa para citar. A citada
876
+ * devolve o proto montado do `contextInfo`, sem `pushName` nem horário. O Baileys não emite
877
+ * `interaction`: para ela, e para mensagem que não veio daqui, `undefined`.
878
+ */
879
+ raw(source) {
880
+ return "key" in source ? nativeOf(source) : void 0;
881
+ }
882
+ get #log() {
883
+ return this.#deps.log;
884
+ }
885
+ on(event, handler) {
886
+ return this.#events.on(event, handler);
887
+ }
888
+ async connect() {
889
+ const attempt = ++this.#attempt;
890
+ this.#endSocket();
891
+ const version = await this.#driver.version();
892
+ const logger = toBaileysLogger(this.#log.child({ lib: "baileys" }));
893
+ const { state, saveCreds } = await loadAuthState(this.#deps.auth, logger);
894
+ if (attempt !== this.#attempt) return;
895
+ this.#groups.clear();
896
+ const socket = this.#driver.makeSocket({
897
+ auth: state,
898
+ logger,
899
+ version,
900
+ cachedGroupMetadata: (jid) => this.#cachedGroup(jid)
901
+ });
902
+ this.#socket = socket;
903
+ const current = () => this.#socket === socket;
904
+ let saving = Promise.resolve();
905
+ let pairing = false;
906
+ let codeRequested = false;
907
+ socket.ev.on("creds.update", () => {
908
+ if (!current()) return;
909
+ saving = saving.then(saveCreds).catch((error) => {
910
+ this.#log.error("falha ao gravar as credenciais da sessão", { err: error });
911
+ });
912
+ });
913
+ let inbound = Promise.resolve();
914
+ const enqueue = (work, failure, messageId) => {
915
+ inbound = inbound.then(work).catch((error) => {
916
+ this.#log.error(failure, {
917
+ err: error,
918
+ messageId
919
+ });
920
+ });
921
+ };
922
+ const env = this.#normalizeEnv(socket, state, logger);
923
+ const contacts = this.#contacts;
924
+ const polls = this.#polls;
925
+ socket.ev.on("messages.upsert", ({ messages, type }) => {
926
+ if (!current()) return;
927
+ if (type !== "notify") return;
928
+ for (const raw of messages) {
929
+ if (raw.key.remoteJid && isJidStatusBroadcast(raw.key.remoteJid)) continue;
930
+ enqueue(async () => {
931
+ polls.remember(raw);
932
+ if (pollUpdateOf(raw) !== null) {
933
+ const vote = await toPollVote(raw, polls, env, contacts);
934
+ if (vote === null) this.#log.debug("voto de enquete desconhecida; descartado", { messageId: raw.key.id });
935
+ else if (current()) this.#events.emit("poll.vote", vote);
936
+ return;
937
+ }
938
+ const message = await toMessage(raw, env);
939
+ if (message === null || !current()) return;
940
+ const contact = message.fromMe ? null : contacts.observe(message.sender);
941
+ if (contact !== null) this.#events.emit("contact.updated", contact);
942
+ this.#events.emit("message", message);
943
+ }, "falha ao normalizar mensagem do Baileys; descartada", raw.key.id);
944
+ }
945
+ });
946
+ socket.ev.on("messages.reaction", (reactions) => {
947
+ if (!current()) return;
948
+ for (const item of reactions) enqueue(async () => {
949
+ const reaction = await toReaction(item, env, contacts);
950
+ if (reaction !== null && current()) this.#events.emit("reaction", reaction);
951
+ }, "falha ao converter reação do Baileys; descartada", item.key.id);
952
+ });
953
+ socket.ev.on("messages.update", (updates) => {
954
+ if (!current()) return;
955
+ for (const item of updates) if (item.update.message?.editedMessage) enqueue(async () => {
956
+ const message = await toEdited(item, env, contacts);
957
+ if (message !== null && current()) this.#events.emit("message.edited", message);
958
+ }, "falha ao converter edição do Baileys; descartada", item.key.id);
959
+ else if (item.update.messageStubType === WAMessageStubType.REVOKE) enqueue(async () => {
960
+ const deleted = await toDeleted(item, env, contacts);
961
+ if (deleted !== null && current()) this.#events.emit("message.deleted", deleted);
962
+ }, "falha ao converter apagamento do Baileys; descartado", item.key.id);
963
+ });
964
+ socket.ev.on("groups.upsert", (groups) => {
965
+ if (!current()) return;
966
+ for (const { id } of groups) enqueue(async () => {
967
+ if (current()) this.#events.emit("group.joined", { chat: chatOf(id) });
968
+ }, "falha ao repassar grupo novo");
969
+ });
970
+ socket.ev.on("groups.update", (updates) => {
971
+ if (!current()) return;
972
+ for (const update of updates) {
973
+ if (update.id) this.#groups.delete(update.id);
974
+ const changed = toGroupUpdated(update);
975
+ if (changed === null) continue;
976
+ enqueue(async () => {
977
+ if (current()) this.#events.emit("group.updated", changed);
978
+ }, "falha ao repassar alteração de grupo");
979
+ }
980
+ });
981
+ socket.ev.on("group-participants.update", (update) => {
982
+ if (!current()) return;
983
+ this.#groups.delete(update.id);
984
+ enqueue(async () => {
985
+ const { self, others } = await toParticipantEvents(update, env, contacts);
986
+ if (!current()) return;
987
+ if (self !== null) this.#events.emit(self, { chat: chatOf(update.id) });
988
+ if (others !== null) this.#events.emit("group.participants", others);
989
+ }, "falha ao converter alteração de participantes do Baileys; descartada");
990
+ });
991
+ socket.ev.on("connection.update", ({ connection, lastDisconnect, qr }) => {
992
+ if (!current()) return;
993
+ if (qr !== void 0) {
994
+ pairing = true;
995
+ if (this.#pairing === "qr") this.#events.emit("connection.qr", { qr });
996
+ else if (!codeRequested && !state.creds.registered) {
997
+ codeRequested = true;
998
+ this.#requestPairingCode(socket, this.#pairing.phone);
999
+ }
1000
+ }
1001
+ switch (connection) {
1002
+ case "connecting":
1003
+ this.#events.emit("connection.status", { status: "connecting" });
1004
+ return;
1005
+ case "open":
1006
+ pairing = false;
1007
+ this.#self = socket.user ? toContact(socket.user) : null;
1008
+ this.#events.emit("connection.status", { status: "open" });
1009
+ return;
1010
+ case "close": {
1011
+ this.#socket = null;
1012
+ const error = lastDisconnect?.error;
1013
+ const reason = toDisconnectReason(error, { pairing });
1014
+ this.#events.emit("connection.status", {
1015
+ status: "closed",
1016
+ reason,
1017
+ error
1018
+ });
1019
+ return;
1020
+ }
1021
+ }
1022
+ });
1023
+ }
1024
+ async disconnect() {
1025
+ this.#attempt++;
1026
+ this.#endSocket();
1027
+ this.#polls.clear();
1028
+ }
1029
+ /** Encerra o socket atual sem emitir nada: quem chamou já sabe que a conexão acabou. */
1030
+ #endSocket() {
1031
+ const socket = this.#socket;
1032
+ if (socket === null) return;
1033
+ this.#socket = null;
1034
+ socket.end(void 0).catch((error) => {
1035
+ this.#log.warn("falha ao encerrar o socket do Baileys", { err: error });
1036
+ });
1037
+ }
1038
+ #normalizeEnv(socket, state, logger) {
1039
+ const media = {
1040
+ reuploadRequest: (m) => socket.updateMediaMessage(m),
1041
+ logger
1042
+ };
1043
+ return {
1044
+ get selfIds() {
1045
+ const me = state.creds.me;
1046
+ return [me?.id, me?.lid].flatMap((id) => id ? [jidNormalizedUser(id)] : []);
1047
+ },
1048
+ pnForLid: (lid) => this.#pnForLid(socket, lid),
1049
+ download: (raw) => downloadMediaMessage(raw, "buffer", {}, media),
1050
+ stream: async (raw) => Readable.toWeb(await downloadMediaMessage(raw, "stream", {}, media))
1051
+ };
1052
+ }
1053
+ #pnForLid(socket, lid) {
1054
+ return socket.signalRepository.lidMapping.getPNForLID(lid).catch((error) => {
1055
+ this.#log.warn("falha ao resolver o telefone de um LID", {
1056
+ err: error,
1057
+ lid
1058
+ });
1059
+ return null;
1060
+ });
1061
+ }
1062
+ #requestPairingCode(socket, phone) {
1063
+ socket.requestPairingCode(phone).then((code) => {
1064
+ if (this.#socket === socket) this.#events.emit("connection.pairing-code", { code });
1065
+ }, (error) => {
1066
+ this.#log.error("falha ao pedir o código de pareamento", { err: error });
1067
+ });
1068
+ }
1069
+ async send(chatId, content, options) {
1070
+ const sent = await this.#connected().sendMessage(chatId, toContent(content, options), options?.quoted ? { quoted: toQuoted(options.quoted) } : void 0);
1071
+ const id = sent?.key.id;
1072
+ if (!id) throw new Error("baileys: o envio não devolveu a chave da mensagem");
1073
+ if (content.type === "poll" && sent) this.#polls.remember(sent);
1074
+ return {
1075
+ chatId,
1076
+ id,
1077
+ fromMe: true,
1078
+ senderId: isJidGroup(chatId) ? this.#self?.id ?? null : null
1079
+ };
1080
+ }
1081
+ async react(key, emoji) {
1082
+ await this.#connected().sendMessage(key.chatId, { react: {
1083
+ text: emoji ?? "",
1084
+ key: toWAKey(key)
1085
+ } });
1086
+ }
1087
+ async edit(key, text, formatted) {
1088
+ if (!formatted) {
1089
+ await this.#connected().sendMessage(key.chatId, {
1090
+ text,
1091
+ edit: toWAKey(key)
1092
+ });
1093
+ return;
1094
+ }
1095
+ const rendered = renderWhatsApp(formatted);
1096
+ await this.#connected().sendMessage(key.chatId, {
1097
+ text: rendered.text,
1098
+ edit: toWAKey(key),
1099
+ ...rendered.mentions.length > 0 && { mentions: [...rendered.mentions] }
1100
+ });
1101
+ }
1102
+ async delete(key) {
1103
+ await this.#connected().sendMessage(key.chatId, { delete: toWAKey(key) });
1104
+ }
1105
+ async sendTyping(chatId, kind) {
1106
+ await this.#connected().sendPresenceUpdate(kind === "voice" ? "recording" : "composing", chatId);
1107
+ }
1108
+ async getGroupMetadata(groupId) {
1109
+ const socket = this.#connected();
1110
+ let native = this.#groups.get(groupId);
1111
+ if (native === void 0) {
1112
+ const fetching = socket.groupMetadata(groupId);
1113
+ native = fetching;
1114
+ this.#groups.set(groupId, fetching);
1115
+ fetching.catch(() => {
1116
+ if (this.#groups.get(groupId) === fetching) this.#groups.delete(groupId);
1117
+ });
1118
+ }
1119
+ return toGroupMetadata(await native, { pnForLid: (lid) => this.#pnForLid(socket, lid) });
1120
+ }
1121
+ async updateGroupParticipants(groupId, participantIds, action) {
1122
+ const results = await this.#connected().groupParticipantsUpdate(groupId, [...participantIds], action);
1123
+ this.#groups.delete(groupId);
1124
+ const failed = results.filter((r) => r.status !== "200");
1125
+ if (failed.length > 0) {
1126
+ const list = failed.map((r) => `${r.jid ?? "?"} (${r.status})`).join(", ");
1127
+ throw new Error(`baileys: ${action} recusado para ${list}`);
1128
+ }
1129
+ }
1130
+ /** Socket da conexão atual; sem ele, a ação falha na hora em vez de esperar. */
1131
+ #connected() {
1132
+ if (this.#socket === null) throw new Error("baileys: sem conexão");
1133
+ return this.#socket;
1134
+ }
1135
+ /** Para o envio em grupo: usa a consulta que já existe, sem disparar outra. */
1136
+ async #cachedGroup(jid) {
1137
+ return this.#groups.get(jid)?.catch(() => void 0);
1138
+ }
1139
+ };
1140
+ /** A própria sessão: o `id` do Baileys traz o aparelho (`5511…:12@s.whatsapp.net`). */
1141
+ function toContact(user) {
1142
+ const id = jidNormalizedUser(user.id);
1143
+ const jid = jidDecode(id);
1144
+ const phone = jid?.server === "s.whatsapp.net" ? jid.user : jidDecode(user.phoneNumber)?.user ?? null;
1145
+ return {
1146
+ id,
1147
+ name: user.name ?? user.notify ?? null,
1148
+ phone
1149
+ };
1150
+ }
1151
+ //#endregion
1152
+ //#region src/index.ts
1153
+ function baileys(options = {}) {
1154
+ const pairing = options.pairing ?? "qr";
1155
+ if (pairing !== "qr" && !/^\d{8,15}$/.test(pairing.phone)) throw new TypeError(`baileys: pairing.phone deve ter só dígitos, com DDI (ex.: 5511999999999); recebido "${pairing.phone}"`);
1156
+ return (deps) => new BaileysTransport({
1157
+ pairing,
1158
+ driver: driver(options.version, deps)
1159
+ }, deps);
1160
+ }
1161
+ function driver(fixed, deps) {
1162
+ let latest;
1163
+ return {
1164
+ makeSocket: ({ auth, logger, version, cachedGroupMetadata }) => makeWASocket({
1165
+ auth,
1166
+ logger,
1167
+ version,
1168
+ cachedGroupMetadata,
1169
+ browser: Browsers.ubuntu("Chrome")
1170
+ }),
1171
+ version: () => {
1172
+ if (fixed) return Promise.resolve(fixed);
1173
+ latest ??= fetchLatestBaileysVersion().then(({ version, error }) => {
1174
+ if (error !== void 0) deps.log.warn("não deu para buscar a versão do WhatsApp Web; usando a do Baileys", { err: error });
1175
+ return version;
1176
+ });
1177
+ return latest;
1178
+ }
1179
+ };
1180
+ }
1181
+ //#endregion
1182
+ export { baileys };
1183
+
1184
+ //# sourceMappingURL=index.mjs.map