@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.
- package/README.md +1987 -1649
- package/lib/Client/Builders/Buttons.js +127 -0
- package/lib/Client/Builders/EventBuilder.js +122 -0
- package/lib/Client/Builders/InteractiveMessage.js +150 -0
- package/lib/Client/Builders/Lists.js +78 -0
- package/lib/Client/Builders/PollBuilder.js +84 -0
- package/lib/Client/Builders/index.js +21 -0
- package/lib/Client/Constants.js +54 -0
- package/lib/Client/Context/CallContext.js +96 -0
- package/lib/Client/Context/MediaObject.js +155 -0
- package/lib/Client/Context/MessageContext.js +146 -0
- package/lib/Client/Modules/BusinessManager.js +155 -0
- package/lib/Client/Modules/ChannelManager.js +144 -0
- package/lib/Client/Modules/ChatManager.js +113 -0
- package/lib/Client/Modules/CommunityManager.js +136 -0
- package/lib/Client/Modules/GroupManager.js +194 -0
- package/lib/Client/Modules/LabelManager.js +123 -0
- package/lib/Client/Modules/MessageManager.js +198 -0
- package/lib/Client/Modules/PresenceManager.js +68 -0
- package/lib/Client/Modules/PrivacyManager.js +98 -0
- package/lib/Client/Modules/ProfileManager.js +92 -0
- package/lib/Client/Modules/UserManager.js +92 -0
- package/lib/Client/Modules/VoipManager.js +117 -0
- package/lib/Client/Modules/index.js +29 -0
- package/lib/Client/Normalizers/EventNormalizer.js +82 -0
- package/lib/Client/Normalizers/JidNormalizer.js +93 -0
- package/lib/Client/Normalizers/MessageNormalizer.js +134 -0
- package/lib/Client/WaClient.js +438 -0
- package/lib/Client/index.d.ts +464 -0
- package/lib/Client/index.js +76 -0
- package/lib/Defaults/baileys-version.json +1 -1
- package/lib/Defaults/connection.js +14 -1
- package/lib/Socket/index.js +51 -48
- package/lib/Socket/messages-recv.js +74 -13
- package/lib/Socket/messages-send.js +50 -25
- package/lib/Socket/socket.js +13 -0
- package/lib/Socket/voip.js +123 -0
- package/lib/Store/make-cache-manager-store.js +14 -5
- package/lib/Types/index.d.ts +4 -0
- package/lib/Utils/auth-utils.js +16 -1
- package/lib/Utils/browser-utils.js +6 -2
- package/lib/Utils/generics.js +47 -8
- package/lib/Utils/index.d.ts +1 -0
- package/lib/Utils/messages-media.js +44 -21
- package/lib/Utils/pre-key-manager.js +16 -1
- package/lib/Utils/validate-connection.js +10 -11
- package/lib/VoIP/VoIPCrypto.js +150 -0
- package/lib/VoIP/VoIPManager.js +469 -0
- package/lib/VoIP/VoIPMediaEngine.js +224 -0
- package/lib/VoIP/VoIPProtocol.js +354 -0
- package/lib/VoIP/VoIPRelayTransport.js +173 -0
- package/lib/VoIP/VoIPSession.js +314 -0
- package/lib/VoIP/assets/wasm/loader.js +5 -0
- package/lib/VoIP/assets/wasm/whatsapp.wasm +0 -0
- package/lib/VoIP/assets/wasm/worker-modules.js +305 -0
- package/lib/VoIP/engine/audio-feeder.d.mts +15 -0
- package/lib/VoIP/engine/audio-feeder.mjs +243 -0
- package/lib/VoIP/engine/index.d.mts +91 -0
- package/lib/VoIP/engine/index.mjs +1683 -0
- package/lib/VoIP/engine/relay-transport.d.mts +43 -0
- package/lib/VoIP/engine/relay-transport.mjs +607 -0
- package/lib/VoIP/engine/signaling.d.mts +44 -0
- package/lib/VoIP/engine/signaling.mjs +893 -0
- package/lib/VoIP/engine/types.d.mts +183 -0
- package/lib/VoIP/engine/types.mjs +17 -0
- package/lib/VoIP/engine/wasm-engine.d.mts +103 -0
- package/lib/VoIP/engine/wasm-engine.mjs +1397 -0
- package/lib/VoIP/engine/worker-bootstrap.mjs +1049 -0
- package/lib/VoIP/index.js +51 -0
- package/lib/VoIP/utils/audio.d.ts +28 -0
- package/lib/VoIP/utils/audio.js +148 -0
- package/lib/WABinary/generic-utils.js +22 -6
- package/lib/index.d.ts +382 -274
- package/lib/index.js +5 -0
- 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
|