@areumtecnologia/wasockets 2.3.1 → 2.3.3
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
CHANGED
|
@@ -288,22 +288,37 @@ await ctx.unstar();
|
|
|
288
288
|
```
|
|
289
289
|
|
|
290
290
|
#### Utilitários de Mídia (`MediaObject`):
|
|
291
|
-
Se a mensagem contiver imagem, vídeo, áudio ou documento, `ctx.media`
|
|
291
|
+
Se a mensagem contiver imagem, vídeo, áudio, figurinha ou documento, `ctx.media` centraliza todos os metadados e métodos de manipulação em uma única instância:
|
|
292
292
|
|
|
293
|
+
##### Propriedades de `ctx.media`:
|
|
294
|
+
- `ctx.media.mimetype`: Tipo MIME real (ex: `'image/jpeg'`, `'audio/ogg; codecs=opus'`).
|
|
295
|
+
- `ctx.media.extension`: Extensão inferida do arquivo (ex: `'jpg'`, `'ogg'`, `'pdf'`, `'mp4'`).
|
|
296
|
+
- `ctx.media.fileLength`: Tamanho do arquivo em bytes.
|
|
297
|
+
- `ctx.media.fileName`: Nome original do arquivo (se enviado como documento).
|
|
298
|
+
- `ctx.media.caption`: Legenda de texto anexada à mídia.
|
|
299
|
+
- `ctx.media.seconds` / `duration`: Duração em segundos (para áudios e vídeos).
|
|
300
|
+
- `ctx.media.isVoice` / `isPtt`: Booleano se é áudio gravado no WhatsApp (Push-To-Talk).
|
|
301
|
+
- `ctx.media.isGif`: Booleano se é vídeo reproduzido como GIF animado.
|
|
302
|
+
- `ctx.media.isViewOnce`: Booleano se é mídia de visualização única.
|
|
303
|
+
|
|
304
|
+
##### Métodos de `ctx.media`:
|
|
293
305
|
```javascript
|
|
294
306
|
if (ctx.media) {
|
|
295
|
-
// 1. Salva diretamente em disco
|
|
307
|
+
// 1. Salva diretamente em disco (cria diretórios se não existirem)
|
|
296
308
|
await ctx.media.save('./pasta/comprovante.pdf');
|
|
297
309
|
|
|
298
|
-
// 2.
|
|
299
|
-
const
|
|
300
|
-
|
|
310
|
+
// 2. Obtém como Buffer em memória (com cache automático)
|
|
311
|
+
const buffer = await ctx.media.toBuffer();
|
|
312
|
+
|
|
313
|
+
// 3. Converte para Base64 (ideal para LLMs como Gemini / OpenAI)
|
|
314
|
+
const base64 = await ctx.media.toBase64();
|
|
315
|
+
const dataUri = await ctx.media.toDataUri(); // 'data:image/jpeg;base64,...'
|
|
301
316
|
|
|
302
|
-
//
|
|
303
|
-
const stream = ctx.media.toStream();
|
|
317
|
+
// 4. Obtém como Stream legível (Node Readable)
|
|
318
|
+
const stream = await ctx.media.toStream();
|
|
304
319
|
|
|
305
|
-
//
|
|
306
|
-
const sharpInstance = ctx.media.toSharp();
|
|
320
|
+
// 5. Integração nativa com Sharp (se instalado) para recorte/redimensionamento
|
|
321
|
+
const sharpInstance = await ctx.media.toSharp();
|
|
307
322
|
}
|
|
308
323
|
```
|
|
309
324
|
|
|
@@ -19,8 +19,14 @@ class MediaObject {
|
|
|
19
19
|
this.mimetype = this.mediaNode.mimetype || this._defaultMimetype(this.mediaType)
|
|
20
20
|
this.fileLength = Number(this.mediaNode.fileLength || 0)
|
|
21
21
|
this.fileName = this.mediaNode.fileName || ''
|
|
22
|
+
this.caption = this.mediaNode.caption || ''
|
|
22
23
|
this.seconds = Number(this.mediaNode.seconds || 0)
|
|
24
|
+
this.duration = this.seconds || null
|
|
25
|
+
|
|
23
26
|
this.isVoice = Boolean(this.mediaNode.ptt)
|
|
27
|
+
this.isPtt = this.isVoice
|
|
28
|
+
this.isGif = Boolean(this.mediaNode.gifPlayback)
|
|
29
|
+
this.isViewOnce = Boolean(this.mediaNode.viewOnce)
|
|
24
30
|
this.isAnimated = Boolean(this.mediaNode.gifPlayback || this.mediaNode.isAnimated)
|
|
25
31
|
this.extension = this._inferExtension()
|
|
26
32
|
|
|
@@ -26,11 +26,13 @@ class MessageContext {
|
|
|
26
26
|
|
|
27
27
|
const effectiveMessage = editedMsg || rawMessage?.message || null
|
|
28
28
|
|
|
29
|
-
// 2.
|
|
29
|
+
// 2. Single-pass message unwrapping (eliminates repeated while-loops)
|
|
30
|
+
const unwrapped = MessageNormalizer.unwrapMessage(effectiveMessage)
|
|
31
|
+
|
|
32
|
+
// 3. Identity and Routing
|
|
30
33
|
this.id = effectiveKey.id || rawMessage?.key?.id || ''
|
|
31
34
|
this.chatId = effectiveKey.remoteJid || rawMessage?.key?.remoteJid || ''
|
|
32
35
|
this.fromMe = Boolean(effectiveKey.fromMe)
|
|
33
|
-
this.isFromMe = this.fromMe
|
|
34
36
|
|
|
35
37
|
this.isGroup = JidNormalizer.isGroup(this.chatId)
|
|
36
38
|
this.isChannel = JidNormalizer.isNewsletter(this.chatId)
|
|
@@ -54,10 +56,7 @@ class MessageContext {
|
|
|
54
56
|
this.fromPushName = rawMessage?.pushName || JidNormalizer.toPhoneNumber(this.from) || 'Desconhecido'
|
|
55
57
|
}
|
|
56
58
|
|
|
57
|
-
this.sender = this.from
|
|
58
59
|
this.fromNumber = JidNormalizer.toPhoneNumber(this.from)
|
|
59
|
-
this.senderNumber = this.fromNumber
|
|
60
|
-
this.senderName = this.fromPushName
|
|
61
60
|
|
|
62
61
|
// Timestamps
|
|
63
62
|
const rawTs = Number(rawMessage?.messageTimestamp || Math.floor(Date.now() / 1000))
|
|
@@ -66,25 +65,22 @@ class MessageContext {
|
|
|
66
65
|
? new Date(Number(rawMessage.message.protocolMessage.timestampMs || Date.now()))
|
|
67
66
|
: null
|
|
68
67
|
|
|
69
|
-
//
|
|
70
|
-
this.type = MessageNormalizer.extractType(
|
|
71
|
-
this.body = MessageNormalizer.extractBody(
|
|
72
|
-
this.interactiveReply = MessageNormalizer.extractInteractiveReply(
|
|
73
|
-
this.contacts = MessageNormalizer.extractContacts(
|
|
74
|
-
this.location = MessageNormalizer.extractLocation(
|
|
75
|
-
this.reaction = MessageNormalizer.extractReaction(
|
|
76
|
-
this.poll = MessageNormalizer.extractPollCreation(
|
|
77
|
-
|
|
78
|
-
//
|
|
79
|
-
this.mediaNode = MessageNormalizer.extractMediaNode(
|
|
80
|
-
this.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// 5. Context Info, Mentions & Forwarding
|
|
85
|
-
this.contextInfo = MessageNormalizer.extractContextInfo(effectiveMessage)
|
|
68
|
+
// 4. Content Decoupling & Typed Parsers (Single-pass unwrapped nodes)
|
|
69
|
+
this.type = MessageNormalizer.extractType(unwrapped)
|
|
70
|
+
this.body = MessageNormalizer.extractBody(unwrapped)
|
|
71
|
+
this.interactiveReply = MessageNormalizer.extractInteractiveReply(unwrapped)
|
|
72
|
+
this.contacts = MessageNormalizer.extractContacts(unwrapped)
|
|
73
|
+
this.location = MessageNormalizer.extractLocation(unwrapped)
|
|
74
|
+
this.reaction = MessageNormalizer.extractReaction(unwrapped)
|
|
75
|
+
this.poll = MessageNormalizer.extractPollCreation(unwrapped, rawMessage)
|
|
76
|
+
|
|
77
|
+
// 5. Media Normalization (Centralized directly into MediaObject)
|
|
78
|
+
this.mediaNode = MessageNormalizer.extractMediaNode(unwrapped)
|
|
79
|
+
this.media = this.mediaNode ? new MediaObject(this.mediaNode, this.type) : null
|
|
80
|
+
|
|
81
|
+
// 6. Context Info, Mentions & Forwarding
|
|
82
|
+
this.contextInfo = MessageNormalizer.extractContextInfo(unwrapped)
|
|
86
83
|
this.mentions = this.contextInfo?.mentionedJid || []
|
|
87
|
-
this.mentionedJids = this.mentions
|
|
88
84
|
this.isForwarded = Boolean(this.contextInfo?.isForwarded)
|
|
89
85
|
this.forwardingScore = Number(this.contextInfo?.forwardingScore || 0)
|
|
90
86
|
|
|
@@ -94,7 +90,7 @@ class MessageContext {
|
|
|
94
90
|
(clientLid && this.mentions.includes(clientLid))
|
|
95
91
|
)
|
|
96
92
|
|
|
97
|
-
//
|
|
93
|
+
// 7. Quoted Message Parsing (Recursion with depth limit 1)
|
|
98
94
|
this.isReply = Boolean(this.contextInfo?.quotedMessage)
|
|
99
95
|
this.quoted = null
|
|
100
96
|
|
|
@@ -125,6 +121,58 @@ class MessageContext {
|
|
|
125
121
|
}
|
|
126
122
|
}
|
|
127
123
|
|
|
124
|
+
// ==========================================
|
|
125
|
+
// Zero-Cost Memory Aliases & Compatibility Getters
|
|
126
|
+
// ==========================================
|
|
127
|
+
|
|
128
|
+
/** Alias for this.fromMe */
|
|
129
|
+
get isFromMe() {
|
|
130
|
+
return this.fromMe
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Alias for this.from */
|
|
134
|
+
get sender() {
|
|
135
|
+
return this.from
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Alias for this.fromNumber */
|
|
139
|
+
get senderNumber() {
|
|
140
|
+
return this.fromNumber
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Alias for this.fromPushName */
|
|
144
|
+
get senderName() {
|
|
145
|
+
return this.fromPushName
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Alias for this.mentions */
|
|
149
|
+
get mentionedJids() {
|
|
150
|
+
return this.mentions
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Boolean check whether message contains media */
|
|
154
|
+
get hasMedia() {
|
|
155
|
+
return Boolean(this.media)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Backward-compatible media details accessor backed by MediaObject */
|
|
159
|
+
get mediaDetails() {
|
|
160
|
+
if (!this.media) return null
|
|
161
|
+
return {
|
|
162
|
+
mimetype: this.media.mimetype,
|
|
163
|
+
fileName: this.media.fileName,
|
|
164
|
+
caption: this.media.caption,
|
|
165
|
+
duration: this.media.duration,
|
|
166
|
+
isPtt: this.media.isPtt,
|
|
167
|
+
isGif: this.media.isGif,
|
|
168
|
+
isViewOnce: this.media.isViewOnce
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ==========================================
|
|
173
|
+
// Context Action Methods
|
|
174
|
+
// ==========================================
|
|
175
|
+
|
|
128
176
|
/**
|
|
129
177
|
* Responde citando esta mensagem automaticamente
|
|
130
178
|
* @param {string|Object} content - Texto ou payload de mensagem
|
|
@@ -23,12 +23,13 @@ class EventNormalizer {
|
|
|
23
23
|
// Ignore stub or protocol messages if completely empty
|
|
24
24
|
if (!rawMsg.message && !rawMsg.messageStubType) continue
|
|
25
25
|
|
|
26
|
-
// Handle protocol revoke messages explicitly
|
|
26
|
+
// Handle protocol revoke messages explicitly (delete for everyone)
|
|
27
27
|
const isRevoke = rawMsg.message?.protocolMessage?.type === 0
|
|
28
28
|
if (isRevoke) {
|
|
29
29
|
client.emit(Events.MESSAGE_DELETE, {
|
|
30
30
|
keys: [rawMsg.message.protocolMessage.key || rawMsg.key]
|
|
31
31
|
})
|
|
32
|
+
continue // Do not emit as a new received/sent message
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
const ctx = new MessageContext(client, rawMsg)
|
package/lib/Client/index.d.ts
CHANGED
|
@@ -58,8 +58,13 @@ export declare class MediaObject {
|
|
|
58
58
|
mimetype: string
|
|
59
59
|
fileLength: number
|
|
60
60
|
fileName: string
|
|
61
|
+
caption: string
|
|
61
62
|
seconds: number
|
|
63
|
+
duration: number | null
|
|
62
64
|
isVoice: boolean
|
|
65
|
+
isPtt: boolean
|
|
66
|
+
isGif: boolean
|
|
67
|
+
isViewOnce: boolean
|
|
63
68
|
isAnimated: boolean
|
|
64
69
|
extension: string
|
|
65
70
|
|