@areumtecnologia/wasockets 2.0.0 → 2.3.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.
Files changed (75) hide show
  1. package/README.md +1987 -1649
  2. package/lib/Client/Builders/Buttons.js +127 -0
  3. package/lib/Client/Builders/EventBuilder.js +122 -0
  4. package/lib/Client/Builders/InteractiveMessage.js +150 -0
  5. package/lib/Client/Builders/Lists.js +78 -0
  6. package/lib/Client/Builders/PollBuilder.js +84 -0
  7. package/lib/Client/Builders/index.js +21 -0
  8. package/lib/Client/Constants.js +54 -0
  9. package/lib/Client/Context/CallContext.js +96 -0
  10. package/lib/Client/Context/MediaObject.js +155 -0
  11. package/lib/Client/Context/MessageContext.js +146 -0
  12. package/lib/Client/Modules/BusinessManager.js +155 -0
  13. package/lib/Client/Modules/ChannelManager.js +144 -0
  14. package/lib/Client/Modules/ChatManager.js +113 -0
  15. package/lib/Client/Modules/CommunityManager.js +136 -0
  16. package/lib/Client/Modules/GroupManager.js +194 -0
  17. package/lib/Client/Modules/LabelManager.js +123 -0
  18. package/lib/Client/Modules/MessageManager.js +198 -0
  19. package/lib/Client/Modules/PresenceManager.js +68 -0
  20. package/lib/Client/Modules/PrivacyManager.js +98 -0
  21. package/lib/Client/Modules/ProfileManager.js +92 -0
  22. package/lib/Client/Modules/UserManager.js +92 -0
  23. package/lib/Client/Modules/VoipManager.js +117 -0
  24. package/lib/Client/Modules/index.js +29 -0
  25. package/lib/Client/Normalizers/EventNormalizer.js +82 -0
  26. package/lib/Client/Normalizers/JidNormalizer.js +93 -0
  27. package/lib/Client/Normalizers/MessageNormalizer.js +134 -0
  28. package/lib/Client/WaClient.js +438 -0
  29. package/lib/Client/index.d.ts +464 -0
  30. package/lib/Client/index.js +76 -0
  31. package/lib/Defaults/baileys-version.json +1 -1
  32. package/lib/Defaults/connection.js +14 -1
  33. package/lib/Socket/index.js +51 -48
  34. package/lib/Socket/messages-recv.js +74 -13
  35. package/lib/Socket/messages-send.js +50 -25
  36. package/lib/Socket/socket.js +13 -0
  37. package/lib/Socket/voip.js +123 -0
  38. package/lib/Store/make-cache-manager-store.js +14 -5
  39. package/lib/Types/index.d.ts +4 -0
  40. package/lib/Utils/auth-utils.js +16 -1
  41. package/lib/Utils/browser-utils.js +6 -2
  42. package/lib/Utils/generics.js +47 -8
  43. package/lib/Utils/index.d.ts +1 -0
  44. package/lib/Utils/messages-media.js +44 -21
  45. package/lib/Utils/pre-key-manager.js +16 -1
  46. package/lib/Utils/validate-connection.js +10 -11
  47. package/lib/VoIP/VoIPCrypto.js +150 -0
  48. package/lib/VoIP/VoIPManager.js +469 -0
  49. package/lib/VoIP/VoIPMediaEngine.js +224 -0
  50. package/lib/VoIP/VoIPProtocol.js +354 -0
  51. package/lib/VoIP/VoIPRelayTransport.js +173 -0
  52. package/lib/VoIP/VoIPSession.js +314 -0
  53. package/lib/VoIP/assets/wasm/loader.js +5 -0
  54. package/lib/VoIP/assets/wasm/whatsapp.wasm +0 -0
  55. package/lib/VoIP/assets/wasm/worker-modules.js +305 -0
  56. package/lib/VoIP/engine/audio-feeder.d.mts +15 -0
  57. package/lib/VoIP/engine/audio-feeder.mjs +243 -0
  58. package/lib/VoIP/engine/index.d.mts +91 -0
  59. package/lib/VoIP/engine/index.mjs +1683 -0
  60. package/lib/VoIP/engine/relay-transport.d.mts +43 -0
  61. package/lib/VoIP/engine/relay-transport.mjs +607 -0
  62. package/lib/VoIP/engine/signaling.d.mts +44 -0
  63. package/lib/VoIP/engine/signaling.mjs +893 -0
  64. package/lib/VoIP/engine/types.d.mts +183 -0
  65. package/lib/VoIP/engine/types.mjs +17 -0
  66. package/lib/VoIP/engine/wasm-engine.d.mts +103 -0
  67. package/lib/VoIP/engine/wasm-engine.mjs +1397 -0
  68. package/lib/VoIP/engine/worker-bootstrap.mjs +1049 -0
  69. package/lib/VoIP/index.js +51 -0
  70. package/lib/VoIP/utils/audio.d.ts +28 -0
  71. package/lib/VoIP/utils/audio.js +148 -0
  72. package/lib/WABinary/generic-utils.js +22 -6
  73. package/lib/index.d.ts +382 -274
  74. package/lib/index.js +5 -0
  75. package/package.json +51 -49
@@ -0,0 +1,96 @@
1
+ "use strict"
2
+
3
+ Object.defineProperty(exports, "__esModule", { value: true })
4
+
5
+ const { JidNormalizer } = require("../Normalizers/JidNormalizer")
6
+
7
+ class CallContext {
8
+ /**
9
+ * @param {import('../WaClient').WaClient} client
10
+ * @param {Object} callData - Inbound call offer or active call metadata
11
+ * @param {Object} [activeCallInstance] - Low-level WebAssembly ActiveCall instance
12
+ */
13
+ constructor(client, callData, activeCallInstance = null) {
14
+ this.client = client
15
+ this.raw = callData || {}
16
+
17
+ this.id = this.raw.id || this.raw.callId || ''
18
+ this.from = this.raw.from || this.raw.chatId || ''
19
+ this.fromNumber = JidNormalizer.toPhoneNumber(this.from)
20
+ this.isVideo = Boolean(this.raw.isVideo)
21
+ this.status = this.raw.status || 'calling'
22
+ this.startTime = this.raw.date ? new Date(this.raw.date).getTime() : Date.now()
23
+ this.instance = activeCallInstance || null
24
+ }
25
+
26
+ _getSock() {
27
+ if (!this.client.sock) {
28
+ throw new Error('Socket is not connected.')
29
+ }
30
+ return this.client.sock
31
+ }
32
+
33
+ /**
34
+ * Atende a chamada recebida via WebAssembly VoIP
35
+ * @param {Object} [options={}]
36
+ */
37
+ async accept(options = {}) {
38
+ const sock = this._getSock()
39
+ if (!sock.acceptCall) {
40
+ throw new Error('VoIP stack is not initialized on the underlying socket.')
41
+ }
42
+
43
+ const callOptions = {
44
+ isVideo: this.isVideo,
45
+ ...options
46
+ }
47
+
48
+ const activeCall = await sock.acceptCall(this.id, this.from, callOptions)
49
+ this.instance = activeCall
50
+ this.status = 'connected'
51
+ return activeCall
52
+ }
53
+
54
+ /**
55
+ * Rejeita a chamada recebida
56
+ * @param {number|string} [reason=0]
57
+ */
58
+ async reject(reason = 0) {
59
+ const sock = this._getSock()
60
+ if (sock.rejectCall) {
61
+ await sock.rejectCall(this.id, this.from, reason)
62
+ }
63
+ this.status = 'rejected'
64
+ }
65
+
66
+ /**
67
+ * Encerra a chamada em andamento
68
+ */
69
+ async hangup() {
70
+ if (this.instance && typeof this.instance.terminate === 'function') {
71
+ await this.instance.terminate()
72
+ } else if (this.instance && typeof this.instance.end === 'function') {
73
+ await this.instance.end()
74
+ } else {
75
+ const sock = this._getSock()
76
+ if (sock.rejectCall) {
77
+ await sock.rejectCall(this.id, this.from, 0)
78
+ }
79
+ }
80
+ this.status = 'ended'
81
+ }
82
+
83
+ /**
84
+ * Transmite áudio na chamada conectada
85
+ * @param {string|Buffer} audioPathOrBuffer
86
+ */
87
+ async playAudio(audioPathOrBuffer) {
88
+ if (this.instance && typeof this.instance.playAudio === 'function') {
89
+ return this.instance.playAudio(audioPathOrBuffer)
90
+ }
91
+ throw new Error('No active audio stream or call is not connected yet.')
92
+ }
93
+ }
94
+
95
+ exports.CallContext = CallContext
96
+ exports.default = CallContext
@@ -0,0 +1,155 @@
1
+ "use strict"
2
+
3
+ Object.defineProperty(exports, "__esModule", { value: true })
4
+
5
+ const { promises: fsPromises } = require("fs")
6
+ const { dirname } = require("path")
7
+ const { downloadContentFromMessage } = require("../../Utils/messages-media")
8
+
9
+ class MediaObject {
10
+ /**
11
+ * @param {Object} mediaNode - Raw protobuf media node (imageMessage, videoMessage, etc.)
12
+ * @param {string} canonicalType - Canonical message type ('image', 'video', 'voice', 'audio', 'document', 'sticker')
13
+ */
14
+ constructor(mediaNode, canonicalType) {
15
+ this.mediaNode = mediaNode || {}
16
+ this.rawType = canonicalType || 'unknown'
17
+ this.mediaType = this._resolveMediaType(canonicalType)
18
+
19
+ this.mimetype = this.mediaNode.mimetype || this._defaultMimetype(this.mediaType)
20
+ this.fileLength = Number(this.mediaNode.fileLength || 0)
21
+ this.fileName = this.mediaNode.fileName || ''
22
+ this.seconds = Number(this.mediaNode.seconds || 0)
23
+ this.isVoice = Boolean(this.mediaNode.ptt)
24
+ this.isAnimated = Boolean(this.mediaNode.gifPlayback || this.mediaNode.isAnimated)
25
+ this.extension = this._inferExtension()
26
+
27
+ this._bufferCache = null
28
+ }
29
+
30
+ _resolveMediaType(type) {
31
+ if (type === 'voice' || type === 'audio') return 'audio'
32
+ if (type === 'gif' || type === 'video') return 'video'
33
+ if (type === 'image') return 'image'
34
+ if (type === 'sticker') return 'sticker'
35
+ if (type === 'document') return 'document'
36
+ return 'document'
37
+ }
38
+
39
+ _defaultMimetype(mediaType) {
40
+ switch (mediaType) {
41
+ case 'image': return 'image/jpeg'
42
+ case 'video': return 'video/mp4'
43
+ case 'audio': return 'audio/ogg; codecs=opus'
44
+ case 'sticker': return 'image/webp'
45
+ default: return 'application/octet-stream'
46
+ }
47
+ }
48
+
49
+ _inferExtension() {
50
+ if (this.fileName && this.fileName.includes('.')) {
51
+ const ext = this.fileName.split('.').pop()
52
+ if (ext) return ext.toLowerCase()
53
+ }
54
+ if (!this.mimetype) return 'bin'
55
+
56
+ const base = this.mimetype.split(';')[0].trim().toLowerCase()
57
+ const sub = base.split('/')[1] || 'bin'
58
+
59
+ if (sub === 'jpeg') return 'jpg'
60
+ if (sub.includes('ogg') || sub.includes('opus')) return 'ogg'
61
+ if (sub === 'pdf') return 'pdf'
62
+ if (sub === 'webp') return 'webp'
63
+ if (sub === 'mp4') return 'mp4'
64
+ if (sub === 'mpeg') return 'mp3'
65
+
66
+ return sub.replace(/[^a-z0-9]/gi, '') || 'bin'
67
+ }
68
+
69
+ /**
70
+ * Downloads media as a Node.js Readable stream
71
+ * @param {Object} [options={}]
72
+ * @returns {Promise<import('stream').Readable>}
73
+ */
74
+ async toStream(options = {}) {
75
+ if (!this.mediaNode.mediaKey && !this.mediaNode.directPath && !this.mediaNode.url) {
76
+ throw new Error('Media node does not contain valid decryption keys or download URL.')
77
+ }
78
+
79
+ return downloadContentFromMessage(this.mediaNode, this.mediaType, options)
80
+ }
81
+
82
+ /**
83
+ * Downloads media and returns decrypted Buffer (caches in memory)
84
+ * @param {Object} [options={}]
85
+ * @returns {Promise<Buffer>}
86
+ */
87
+ async toBuffer(options = {}) {
88
+ if (this._bufferCache) {
89
+ return this._bufferCache
90
+ }
91
+
92
+ const stream = await this.toStream(options)
93
+ const chunks = []
94
+
95
+ for await (const chunk of stream) {
96
+ chunks.push(chunk)
97
+ }
98
+
99
+ this._bufferCache = Buffer.concat(chunks)
100
+ return this._bufferCache
101
+ }
102
+
103
+ /**
104
+ * Saves the downloaded media directly to disk
105
+ * @param {string} destinationPath - Full or relative file path to save
106
+ * @param {Object} [options={}]
107
+ * @returns {Promise<string>} Saved destination path
108
+ */
109
+ async save(destinationPath, options = {}) {
110
+ const buffer = await this.toBuffer(options)
111
+ const dir = dirname(destinationPath)
112
+ await fsPromises.mkdir(dir, { recursive: true })
113
+ await fsPromises.writeFile(destinationPath, buffer)
114
+ return destinationPath
115
+ }
116
+
117
+ /**
118
+ * Returns Base64 encoded string of the media
119
+ * @param {Object} [options={}]
120
+ * @returns {Promise<string>}
121
+ */
122
+ async toBase64(options = {}) {
123
+ const buffer = await this.toBuffer(options)
124
+ return buffer.toString('base64')
125
+ }
126
+
127
+ /**
128
+ * Returns a Data URI string (e.g. data:image/jpeg;base64,...)
129
+ * @param {Object} [options={}]
130
+ * @returns {Promise<string>}
131
+ */
132
+ async toDataUri(options = {}) {
133
+ const base64 = await this.toBase64(options)
134
+ return `data:${this.mimetype};base64,${base64}`
135
+ }
136
+
137
+ /**
138
+ * Returns a Sharp instance for image processing (requires sharp installed)
139
+ * @param {Object} [options={}]
140
+ */
141
+ async toSharp(options = {}) {
142
+ let sharp
143
+ try {
144
+ sharp = require('sharp')
145
+ } catch {
146
+ throw new Error('The "sharp" package is required to use toSharp(). Run "npm install sharp" to add it.')
147
+ }
148
+
149
+ const buffer = await this.toBuffer(options)
150
+ return sharp(buffer)
151
+ }
152
+ }
153
+
154
+ exports.MediaObject = MediaObject
155
+ exports.default = MediaObject
@@ -0,0 +1,146 @@
1
+ "use strict"
2
+
3
+ Object.defineProperty(exports, "__esModule", { value: true })
4
+
5
+ const { JidNormalizer } = require("../Normalizers/JidNormalizer")
6
+ const { MessageNormalizer } = require("../Normalizers/MessageNormalizer")
7
+ const { MediaObject } = require("./MediaObject")
8
+
9
+ class MessageContext {
10
+ /**
11
+ * @param {import('../WaClient').WaClient} client
12
+ * @param {Object} rawMessage - proto.IWebMessageInfo
13
+ */
14
+ constructor(client, rawMessage) {
15
+ this.client = client
16
+ this.raw = rawMessage
17
+
18
+ const key = rawMessage.key || {}
19
+ this.id = key.id || ''
20
+ this.chatId = key.remoteJid || ''
21
+ this.isFromMe = Boolean(key.fromMe)
22
+ this.isGroup = JidNormalizer.isGroup(this.chatId)
23
+ this.isChannel = JidNormalizer.isNewsletter(this.chatId)
24
+ this.isBroadcast = JidNormalizer.isBroadcast(this.chatId)
25
+
26
+ // Sender determination (group participant vs 1:1 chat remoteJid)
27
+ this.sender = this.isGroup
28
+ ? (key.participant || rawMessage.participant || this.chatId)
29
+ : this.chatId
30
+ this.senderNumber = JidNormalizer.toPhoneNumber(this.sender)
31
+ this.senderName = rawMessage.pushName || this.senderNumber || 'Desconhecido'
32
+
33
+ // Content Extraction
34
+ const messageContent = rawMessage.message || null
35
+ this.type = MessageNormalizer.extractType(messageContent)
36
+ this.body = MessageNormalizer.extractBody(messageContent)
37
+ this.mediaNode = MessageNormalizer.extractMediaNode(messageContent)
38
+ this.hasMedia = Boolean(this.mediaNode)
39
+ this.media = this.hasMedia ? new MediaObject(this.mediaNode, this.type) : null
40
+ this.timestamp = Number(rawMessage.messageTimestamp || Math.floor(Date.now() / 1000))
41
+
42
+ // Context info & mentions
43
+ this.contextInfo = MessageNormalizer.extractContextInfo(messageContent)
44
+ this.mentionedJids = this.contextInfo?.mentionedJid || []
45
+
46
+ // Quoted message parsing
47
+ this.quoted = null
48
+ if (this.contextInfo?.quotedMessage) {
49
+ const quotedMsg = this.contextInfo.quotedMessage
50
+ this.quoted = {
51
+ id: this.contextInfo.stanzaId,
52
+ sender: this.contextInfo.participant,
53
+ senderNumber: JidNormalizer.toPhoneNumber(this.contextInfo.participant),
54
+ type: MessageNormalizer.extractType(quotedMsg),
55
+ body: MessageNormalizer.extractBody(quotedMsg),
56
+ raw: quotedMsg
57
+ }
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Responde citando esta mensagem automaticamente
63
+ * @param {string|Object} content - Texto ou objeto de mensagem do WaSockets
64
+ * @param {Object} [options={}] - Opções adicionais de envio
65
+ */
66
+ async reply(content, options = {}) {
67
+ const payload = typeof content === 'string' ? { text: content } : content
68
+ return this.client.sendMessage(this.chatId, payload, {
69
+ quoted: this.raw,
70
+ ...options
71
+ })
72
+ }
73
+
74
+ /**
75
+ * Reage à mensagem com um emoji
76
+ * @param {string} emoji - Emoji único (ex: '👍', '❤️', '🔥')
77
+ */
78
+ async react(emoji) {
79
+ return this.client.sendMessage(this.chatId, {
80
+ react: {
81
+ text: emoji || '',
82
+ key: this.raw.key
83
+ }
84
+ })
85
+ }
86
+
87
+ /**
88
+ * Remove qualquer reação anteriormente atribuída a esta mensagem
89
+ */
90
+ async removeReaction() {
91
+ return this.react('')
92
+ }
93
+
94
+ /**
95
+ * Apaga esta mensagem para todos na conversa (revoke)
96
+ */
97
+ async delete() {
98
+ return this.client.sendMessage(this.chatId, {
99
+ delete: this.raw.key
100
+ })
101
+ }
102
+
103
+ /**
104
+ * Marca esta mensagem como lida (ticks azuis)
105
+ */
106
+ async markRead() {
107
+ if (!this.client.sock?.readMessages) return
108
+ return this.client.sock.readMessages([this.raw.key])
109
+ }
110
+
111
+ /**
112
+ * Encaminha esta mensagem para outro destinatário
113
+ * @param {string} targetJid - Destinatário
114
+ * @param {Object} [options={}]
115
+ */
116
+ async forward(targetJid, options = {}) {
117
+ return this.client.sendMessage(
118
+ targetJid,
119
+ { forward: this.raw, ...options }
120
+ )
121
+ }
122
+
123
+ /**
124
+ * Fixa a mensagem no chat com duração configurável
125
+ * @param {number} [durationInSeconds=86400] - 86400 (24h), 604800 (7d), 2592000 (30d)
126
+ */
127
+ async pin(durationInSeconds = 86400) {
128
+ if (!this.client.sock?.pinInChatMessage) {
129
+ throw new Error('Método pinInChatMessage indisponível no socket.')
130
+ }
131
+ return this.client.sock.pinInChatMessage(this.chatId, this.raw.key, 1, durationInSeconds)
132
+ }
133
+
134
+ /**
135
+ * Desafixa a mensagem do topo da conversa
136
+ */
137
+ async unpin() {
138
+ if (!this.client.sock?.pinInChatMessage) {
139
+ throw new Error('Método pinInChatMessage indisponível no socket.')
140
+ }
141
+ return this.client.sock.pinInChatMessage(this.chatId, this.raw.key, 2)
142
+ }
143
+ }
144
+
145
+ exports.MessageContext = MessageContext
146
+ exports.default = MessageContext
@@ -0,0 +1,155 @@
1
+ "use strict"
2
+
3
+ Object.defineProperty(exports, "__esModule", { value: true })
4
+
5
+ const { JidNormalizer } = require("../Normalizers/JidNormalizer")
6
+
7
+ class BusinessManager {
8
+ /**
9
+ * @param {import('../WaClient').WaClient} client
10
+ */
11
+ constructor(client) {
12
+ this.client = client
13
+ }
14
+
15
+ _getSock() {
16
+ if (!this.client.sock) {
17
+ throw new Error('Socket is not connected.')
18
+ }
19
+ return this.client.sock
20
+ }
21
+
22
+ /**
23
+ * Cria um produto no catálogo comercial
24
+ * @param {Object} product
25
+ * @param {string} product.name
26
+ * @param {string} [product.description]
27
+ * @param {number} product.price
28
+ * @param {string} [product.currency='BRL']
29
+ * @param {Array<string|Buffer>} [product.images=[]]
30
+ * @param {string} [product.retailerId]
31
+ * @param {string} [product.url]
32
+ * @param {boolean} [product.isHidden=false]
33
+ */
34
+ async createProduct(product) {
35
+ const sock = this._getSock()
36
+ if (!sock.productCreate) {
37
+ throw new Error('productCreate indisponível no socket.')
38
+ }
39
+
40
+ const formattedImages = (product.images || []).map(img => (
41
+ typeof img === 'string' ? { url: img } : img
42
+ ))
43
+
44
+ const payload = {
45
+ name: product.name,
46
+ description: product.description || '',
47
+ price: Math.round((Number(product.price) || 0) * 1000), // Preço em milésimos
48
+ currency: product.currency || 'BRL',
49
+ images: formattedImages,
50
+ retailerId: product.retailerId || undefined,
51
+ url: product.url || undefined,
52
+ isHidden: Boolean(product.isHidden)
53
+ }
54
+
55
+ return sock.productCreate(payload)
56
+ }
57
+
58
+ /**
59
+ * Atualiza dados de um produto existente no catálogo
60
+ * @param {string} productId
61
+ * @param {Object} update
62
+ */
63
+ async updateProduct(productId, update = {}) {
64
+ const sock = this._getSock()
65
+ if (!sock.productUpdate) {
66
+ throw new Error('productUpdate indisponível no socket.')
67
+ }
68
+
69
+ const payload = { ...update }
70
+ if (typeof update.price !== 'undefined') {
71
+ payload.price = Math.round((Number(update.price) || 0) * 1000)
72
+ }
73
+ if (Array.isArray(update.images)) {
74
+ payload.images = update.images.map(img => (
75
+ typeof img === 'string' ? { url: img } : img
76
+ ))
77
+ }
78
+
79
+ return sock.productUpdate(productId, payload)
80
+ }
81
+
82
+ /**
83
+ * Exclui um ou mais produtos do catálogo
84
+ * @param {string|string[]} productIds
85
+ * @returns {Promise<{ deleted: number }>}
86
+ */
87
+ async deleteProduct(productIds) {
88
+ const sock = this._getSock()
89
+ if (!sock.productDelete) {
90
+ throw new Error('productDelete indisponível no socket.')
91
+ }
92
+ const ids = Array.isArray(productIds) ? productIds : [productIds]
93
+ return sock.productDelete(ids)
94
+ }
95
+
96
+ /**
97
+ * Obtém detalhes de um produto específico
98
+ * @param {string} productId
99
+ * @param {string} [merchantJid] - JID da empresa (ou self se omitido)
100
+ */
101
+ async getProduct(productId, merchantJid) {
102
+ const sock = this._getSock()
103
+ if (!sock.getProduct) {
104
+ throw new Error('getProduct indisponível no socket.')
105
+ }
106
+ const targetJid = merchantJid ? JidNormalizer.toJid(merchantJid) : undefined
107
+ return sock.getProduct(targetJid, productId)
108
+ }
109
+
110
+ /**
111
+ * Lista produtos do catálogo
112
+ * @param {Object} [options={}]
113
+ * @param {string} [options.jid] - JID do comerciante
114
+ * @param {number} [options.limit=20]
115
+ * @param {string} [options.cursor]
116
+ */
117
+ async getCatalog(options = {}) {
118
+ const sock = this._getSock()
119
+ if (!sock.getCatalog) {
120
+ throw new Error('getCatalog indisponível no socket.')
121
+ }
122
+ const jid = options.jid ? JidNormalizer.toJid(options.jid) : undefined
123
+ return sock.getCatalog({ ...options, jid })
124
+ }
125
+
126
+ /**
127
+ * Lista coleções de produtos
128
+ * @param {string} [merchantJid]
129
+ * @param {number} [limit=20]
130
+ */
131
+ async getCollections(merchantJid, limit = 20) {
132
+ const sock = this._getSock()
133
+ if (!sock.getCollections) {
134
+ throw new Error('getCollections indisponível no socket.')
135
+ }
136
+ const targetJid = merchantJid ? JidNormalizer.toJid(merchantJid) : undefined
137
+ return sock.getCollections(targetJid, limit)
138
+ }
139
+
140
+ /**
141
+ * Obtém os detalhes de um pedido de checkout
142
+ * @param {string} orderId
143
+ * @param {string} orderToken - Token base64 do pedido
144
+ */
145
+ async getOrderDetails(orderId, orderToken) {
146
+ const sock = this._getSock()
147
+ if (!sock.getOrderDetails) {
148
+ throw new Error('getOrderDetails indisponível no socket.')
149
+ }
150
+ return sock.getOrderDetails(orderId, orderToken)
151
+ }
152
+ }
153
+
154
+ exports.BusinessManager = BusinessManager
155
+ exports.default = BusinessManager
@@ -0,0 +1,144 @@
1
+ "use strict"
2
+
3
+ Object.defineProperty(exports, "__esModule", { value: true })
4
+
5
+ const { JidNormalizer } = require("../Normalizers/JidNormalizer")
6
+
7
+ class ChannelManager {
8
+ /**
9
+ * @param {import('../WaClient').WaClient} client
10
+ */
11
+ constructor(client) {
12
+ this.client = client
13
+ }
14
+
15
+ _getSock() {
16
+ if (!this.client.sock) {
17
+ throw new Error('Socket is not connected.')
18
+ }
19
+ return this.client.sock
20
+ }
21
+
22
+ /**
23
+ * Cria um novo canal / Newsletter
24
+ * @param {string} name
25
+ * @param {string} [description='']
26
+ */
27
+ async create(name, description = '') {
28
+ const sock = this._getSock()
29
+ return sock.newsletterCreate(name, description)
30
+ }
31
+
32
+ /**
33
+ * Obtém metadados de um canal por JID ou código de convite
34
+ * @param {string} channelJidOrInvite
35
+ */
36
+ async getMetadata(channelJidOrInvite) {
37
+ const sock = this._getSock()
38
+ const isInvite = !channelJidOrInvite.includes('@')
39
+ const type = isInvite ? 'invite' : 'jid'
40
+ return sock.newsletterMetadata(type, channelJidOrInvite)
41
+ }
42
+
43
+ /**
44
+ * Segue / Inscreve-se em um canal
45
+ * @param {string} channelJid
46
+ */
47
+ async subscribe(channelJid) {
48
+ const sock = this._getSock()
49
+ const targetJid = JidNormalizer.toJid(channelJid)
50
+ return sock.newsletterFollow(targetJid)
51
+ }
52
+
53
+ /**
54
+ * Deixa de seguir um canal
55
+ * @param {string} channelJid
56
+ */
57
+ async unsubscribe(channelJid) {
58
+ const sock = this._getSock()
59
+ const targetJid = JidNormalizer.toJid(channelJid)
60
+ return sock.newsletterUnfollow(targetJid)
61
+ }
62
+
63
+ /**
64
+ * Silencia notificações do canal
65
+ * @param {string} channelJid
66
+ */
67
+ async mute(channelJid) {
68
+ const sock = this._getSock()
69
+ const targetJid = JidNormalizer.toJid(channelJid)
70
+ return sock.newsletterMute(targetJid)
71
+ }
72
+
73
+ /**
74
+ * Reativa notificações do canal
75
+ * @param {string} channelJid
76
+ */
77
+ async unmute(channelJid) {
78
+ const sock = this._getSock()
79
+ const targetJid = JidNormalizer.toJid(channelJid)
80
+ return sock.newsletterUnmute(targetJid)
81
+ }
82
+
83
+ /**
84
+ * Retorna a lista de todos os canais inscritos da conta
85
+ */
86
+ async listSubscribed() {
87
+ const sock = this._getSock()
88
+ return sock.newsletterSubscribed()
89
+ }
90
+
91
+ /**
92
+ * Busca mensagens recentes do canal
93
+ * @param {string} channelJid
94
+ * @param {number} [count=20]
95
+ * @param {number} [sinceTimestamp]
96
+ */
97
+ async fetchMessages(channelJid, count = 20, sinceTimestamp) {
98
+ const sock = this._getSock()
99
+ const targetJid = JidNormalizer.toJid(channelJid)
100
+ if (!sock.newsletterFetchMessages) {
101
+ throw new Error('newsletterFetchMessages indisponível no socket.')
102
+ }
103
+ return sock.newsletterFetchMessages(targetJid, count, sinceTimestamp)
104
+ }
105
+
106
+ /**
107
+ * Busca as reações de uma mensagem do canal
108
+ * @param {string} channelJid
109
+ * @param {string} messageServerId
110
+ */
111
+ async fetchReactions(channelJid, messageServerId) {
112
+ const sock = this._getSock()
113
+ const targetJid = JidNormalizer.toJid(channelJid)
114
+ if (!sock.newsletterFetchReactions) {
115
+ throw new Error('newsletterFetchReactions indisponível no socket.')
116
+ }
117
+ return sock.newsletterFetchReactions(targetJid, messageServerId)
118
+ }
119
+
120
+ /**
121
+ * Altera o nome do canal
122
+ * @param {string} channelJid
123
+ * @param {string} name
124
+ */
125
+ async setName(channelJid, name) {
126
+ const sock = this._getSock()
127
+ const targetJid = JidNormalizer.toJid(channelJid)
128
+ return sock.newsletterUpdate(targetJid, { name })
129
+ }
130
+
131
+ /**
132
+ * Altera a descrição do canal
133
+ * @param {string} channelJid
134
+ * @param {string} description
135
+ */
136
+ async setDescription(channelJid, description) {
137
+ const sock = this._getSock()
138
+ const targetJid = JidNormalizer.toJid(channelJid)
139
+ return sock.newsletterUpdate(targetJid, { description })
140
+ }
141
+ }
142
+
143
+ exports.ChannelManager = ChannelManager
144
+ exports.default = ChannelManager