@kyyinfinite/lumina 1.0.0 → 1.0.1
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/package.json +1 -1
- package/src/builders/ai-rich.js +0 -165
- package/src/builders/base.js +4 -51
- package/src/builders/button-v2.js +13 -78
- package/src/builders/button.js +20 -234
- package/src/builders/card.js +9 -76
- package/src/builders/carousel.js +4 -61
- package/src/builders/index.js +0 -7
- package/src/client/bot.js +29 -160
- package/src/client/connection.js +4 -111
- package/src/errors.js +0 -37
- package/src/index.d.ts +1 -28
- package/src/index.js +20 -121
- package/src/media/fetch.js +2 -33
- package/src/media/image.js +1 -41
- package/src/media/resolver.js +3 -55
- package/src/media/uploader.js +0 -30
- package/src/media/video.js +1 -39
- package/src/parsers/code-tokenizer-keywords.js +0 -12
- package/src/parsers/code-tokenizer.js +0 -42
- package/src/parsers/index.js +0 -7
- package/src/parsers/inline-entity.js +8 -117
- package/src/parsers/table-metadata.js +1 -35
- package/src/proto/enums.js +9 -65
- package/src/proto/layouts.js +3 -64
- package/src/proto/primitives.js +4 -91
- package/src/proto/relay-nodes.js +1 -32
- package/src/proto/rich-response.js +6 -57
- package/src/proto/updater.js +0 -85
- package/src/services/index.js +0 -7
- package/src/services/media-service.js +1 -102
- package/src/services/message-service.js +16 -158
- package/src/services/proto-service.js +3 -57
- package/src/utils/id.js +0 -25
- package/src/utils/logger.js +2 -39
- package/src/utils/mime.js +17 -73
- package/src/utils/promise.js +0 -26
- package/src/utils/validator.js +6 -71
package/src/builders/button.js
CHANGED
|
@@ -1,95 +1,36 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file builders/button.js
|
|
3
|
-
* @module lumina/builders/button
|
|
4
|
-
*
|
|
5
|
-
* ButtonBuilder — modern, chainable builder for native-flow interactive
|
|
6
|
-
* messages.
|
|
7
|
-
*
|
|
8
|
-
* Key improvements over the legacy `Button` class:
|
|
9
|
-
*
|
|
10
|
-
* 1. Verb-first fluent API (`title()`, `body()`, `reply()`, `url()`, …)
|
|
11
|
-
* instead of Java-style `setTitle()` / `addReply()`.
|
|
12
|
-
* 2. Single unified `media(type, source)` method replaces the 3x
|
|
13
|
-
* duplicated `setVideo/setImage/setDocument` of the legacy code.
|
|
14
|
-
* 3. Registry-based button factory: the 5 identical `addReply/addCall/
|
|
15
|
-
* addReminder/addCancelReminder/addAddress` methods collapse to one
|
|
16
|
-
* `BUTTON_TYPES` table + one factory.
|
|
17
|
-
* 4. Callback-based selection API (no more mutable `_currentSelectionIndex`
|
|
18
|
-
* / `_currentSectionIndex` state juggling).
|
|
19
|
-
* 5. Interactive relay nodes sourced from `proto/relay-nodes.js` (was
|
|
20
|
-
* duplicated 3x in the legacy code).
|
|
21
|
-
* 6. Dead `paramsList` static field removed entirely.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
1
|
import { applyContentFields } from './base.js'
|
|
25
2
|
import { coerceMediaSource, ensurePlainObject } from '../utils/validator.js'
|
|
26
3
|
import { createInteractiveNodes } from '../proto/relay-nodes.js'
|
|
4
|
+
import { SimpleButtonType } from '../proto/enums.js'
|
|
27
5
|
|
|
28
|
-
/** @typedef {import('../client/connection.js').Connection} Connection */
|
|
29
|
-
/** @typedef {import('../services/proto-service.js').ProtoService} ProtoService */
|
|
30
|
-
/** @typedef {import('../services/media-service.js').MediaService} MediaService */
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Registry of "simple" button types — those whose `buttonParamsJson` is just
|
|
34
|
-
* `{ display_text, id, ...opts }`. Adding a new simple type is a one-line change.
|
|
35
|
-
*
|
|
36
|
-
* @type {Record<string, string>}
|
|
37
|
-
*/
|
|
38
|
-
const SIMPLE_BUTTON_TYPES = {
|
|
39
|
-
reply: 'quick_reply',
|
|
40
|
-
call: 'cta_call',
|
|
41
|
-
reminder: 'cta_reminder',
|
|
42
|
-
cancelReminder: 'cta_cancel_reminder',
|
|
43
|
-
address: 'address_message',
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Selection builder — passed to the `selection()` callback so users can
|
|
48
|
-
* compose sections & rows safely without mutating outer state.
|
|
49
|
-
*/
|
|
50
6
|
class SelectionBuilder {
|
|
51
7
|
constructor() {
|
|
52
8
|
this._title = ''
|
|
53
9
|
this._sections = []
|
|
54
|
-
this._currentSection = null
|
|
55
10
|
}
|
|
56
11
|
|
|
57
|
-
/** @param {string} t */
|
|
58
12
|
title(t) {
|
|
59
13
|
this._title = t
|
|
60
14
|
return this
|
|
61
15
|
}
|
|
62
16
|
|
|
63
|
-
/**
|
|
64
|
-
* @param {string} title
|
|
65
|
-
* @param {(s: SectionBuilder) => void} builder
|
|
66
|
-
*/
|
|
67
17
|
section(title, builder) {
|
|
68
18
|
const section = { title, highlight_label: '', rows: [] }
|
|
69
|
-
|
|
70
|
-
builder(sb)
|
|
19
|
+
builder(new SectionBuilder(section.rows))
|
|
71
20
|
this._sections.push(section)
|
|
72
21
|
return this
|
|
73
22
|
}
|
|
74
23
|
|
|
75
|
-
/** @returns {object} proto-ready selection params */
|
|
76
24
|
build() {
|
|
77
25
|
return { title: this._title, sections: this._sections }
|
|
78
26
|
}
|
|
79
27
|
}
|
|
80
28
|
|
|
81
29
|
class SectionBuilder {
|
|
82
|
-
/** @param {Array} rowsRef */
|
|
83
30
|
constructor(rowsRef) {
|
|
84
31
|
this._rows = rowsRef
|
|
85
32
|
}
|
|
86
33
|
|
|
87
|
-
/**
|
|
88
|
-
* @param {string} header
|
|
89
|
-
* @param {string} title
|
|
90
|
-
* @param {string} description
|
|
91
|
-
* @param {string} id
|
|
92
|
-
*/
|
|
93
34
|
row(header, title, description, id) {
|
|
94
35
|
this._rows.push({ header, title, description, id })
|
|
95
36
|
return this
|
|
@@ -97,40 +38,20 @@ class SectionBuilder {
|
|
|
97
38
|
}
|
|
98
39
|
|
|
99
40
|
export class ButtonBuilder {
|
|
100
|
-
/**
|
|
101
|
-
* @param {Connection} conn
|
|
102
|
-
* @param {ProtoService} proto
|
|
103
|
-
* @param {MediaService} media
|
|
104
|
-
*/
|
|
105
41
|
constructor(conn, proto, media) {
|
|
106
42
|
applyContentFields(this)
|
|
107
|
-
|
|
108
43
|
this.#conn = conn
|
|
109
44
|
this.#proto = proto
|
|
110
45
|
this.#media = media
|
|
111
|
-
|
|
112
|
-
/** @type {Array<{ name: string, buttonParamsJson: string }>} */
|
|
113
46
|
this._buttons = []
|
|
114
|
-
/** @type {object | null} */
|
|
115
47
|
this._data = null
|
|
116
|
-
/** @type {object} */
|
|
117
48
|
this._params = {}
|
|
118
49
|
}
|
|
119
50
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
// ─── Media ──────────────────────────────────────────────────────────
|
|
51
|
+
#conn
|
|
52
|
+
#proto
|
|
53
|
+
#media
|
|
125
54
|
|
|
126
|
-
/**
|
|
127
|
-
* Unified media setter. Replaces the legacy `setVideo/setImage/setDocument`
|
|
128
|
-
* triplicate.
|
|
129
|
-
*
|
|
130
|
-
* @param {'image'|'video'|'document'} type
|
|
131
|
-
* @param {string | Buffer | object} source
|
|
132
|
-
* @param {object} [opts] Additional fields merged into the media descriptor.
|
|
133
|
-
*/
|
|
134
55
|
media(type, source, opts = {}) {
|
|
135
56
|
if (!['image', 'video', 'document'].includes(type)) {
|
|
136
57
|
throw new TypeError(`media type must be image | video | document, got: ${type}`)
|
|
@@ -140,31 +61,10 @@ export class ButtonBuilder {
|
|
|
140
61
|
return this
|
|
141
62
|
}
|
|
142
63
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
64
|
+
image(source, opts) { return this.media('image', source, opts) }
|
|
65
|
+
video(source, opts) { return this.media('video', source, opts) }
|
|
66
|
+
document(source, opts) { return this.media('document', source, opts) }
|
|
147
67
|
|
|
148
|
-
/** @param {string | Buffer} source */
|
|
149
|
-
video(source, opts) {
|
|
150
|
-
return this.media('video', source, opts)
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/** @param {string | Buffer} source */
|
|
154
|
-
document(source, opts) {
|
|
155
|
-
return this.media('document', source, opts)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// ─── Buttons ────────────────────────────────────────────────────────
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Generic raw-button pusher. Use this to attach button types Lumina does
|
|
162
|
-
* not have a shortcut for.
|
|
163
|
-
*
|
|
164
|
-
* @param {string} name Button name as expected by WAProto.
|
|
165
|
-
* @param {object | string} params Object (will be JSON.stringify-ed) or pre-stringified JSON.
|
|
166
|
-
* @param {object} [extra] Extra top-level fields merged onto the button entry.
|
|
167
|
-
*/
|
|
168
68
|
button(name, params, extra = {}) {
|
|
169
69
|
this._buttons.push({
|
|
170
70
|
name,
|
|
@@ -174,29 +74,14 @@ export class ButtonBuilder {
|
|
|
174
74
|
return this
|
|
175
75
|
}
|
|
176
76
|
|
|
177
|
-
/**
|
|
178
|
-
* Configure `messageParamsJson` (top-level interactive message params).
|
|
179
|
-
*
|
|
180
|
-
* @param {object} obj
|
|
181
|
-
*/
|
|
182
77
|
params(obj) {
|
|
183
78
|
ensurePlainObject(obj, 'params')
|
|
184
79
|
this._params = obj
|
|
185
80
|
return this
|
|
186
81
|
}
|
|
187
82
|
|
|
188
|
-
/**
|
|
189
|
-
* Build a "simple" button (reply / call / reminder / cancelReminder /
|
|
190
|
-
* address) via the SIMPLE_BUTTON_TYPES registry. Each shortcut below
|
|
191
|
-
* delegates here.
|
|
192
|
-
*
|
|
193
|
-
* @param {keyof typeof SIMPLE_BUTTON_TYPES} kind
|
|
194
|
-
* @param {string} displayText
|
|
195
|
-
* @param {string} [id] Auto-generated UUID when omitted.
|
|
196
|
-
* @param {object} [opts] Extra params merged into buttonParamsJson.
|
|
197
|
-
*/
|
|
198
83
|
simpleButton(kind, displayText, id, opts = {}) {
|
|
199
|
-
const name =
|
|
84
|
+
const name = SimpleButtonType[kind]
|
|
200
85
|
if (!name) throw new TypeError(`unknown simple button kind: ${kind}`)
|
|
201
86
|
this._buttons.push({
|
|
202
87
|
name,
|
|
@@ -205,36 +90,12 @@ export class ButtonBuilder {
|
|
|
205
90
|
return this
|
|
206
91
|
}
|
|
207
92
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/** @param {string} displayText @param {string} [id] @param {object} [opts] */
|
|
214
|
-
call(displayText, id, opts) {
|
|
215
|
-
return this.simpleButton('call', displayText, id, opts)
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/** @param {string} displayText @param {string} [id] @param {object} [opts] */
|
|
219
|
-
reminder(displayText, id, opts) {
|
|
220
|
-
return this.simpleButton('reminder', displayText, id, opts)
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/** @param {string} displayText @param {string} [id] @param {object} [opts] */
|
|
224
|
-
cancelReminder(displayText, id, opts) {
|
|
225
|
-
return this.simpleButton('cancelReminder', displayText, id, opts)
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/** @param {string} displayText @param {string} [id] @param {object} [opts] */
|
|
229
|
-
address(displayText, id, opts) {
|
|
230
|
-
return this.simpleButton('address', displayText, id, opts)
|
|
231
|
-
}
|
|
93
|
+
reply(displayText, id, opts) { return this.simpleButton('reply', displayText, id, opts) }
|
|
94
|
+
call(displayText, id, opts) { return this.simpleButton('call', displayText, id, opts) }
|
|
95
|
+
reminder(displayText, id, opts) { return this.simpleButton('reminder', displayText, id, opts) }
|
|
96
|
+
cancelReminder(displayText, id, opts) { return this.simpleButton('cancelReminder', displayText, id, opts) }
|
|
97
|
+
address(displayText, id, opts) { return this.simpleButton('address', displayText, id, opts) }
|
|
232
98
|
|
|
233
|
-
/**
|
|
234
|
-
* @param {string} displayText
|
|
235
|
-
* @param {string} url
|
|
236
|
-
* @param {object} [opts] { webview_interaction, ... }
|
|
237
|
-
*/
|
|
238
99
|
url(displayText, url, opts = {}) {
|
|
239
100
|
this._buttons.push({
|
|
240
101
|
name: 'cta_url',
|
|
@@ -248,97 +109,42 @@ export class ButtonBuilder {
|
|
|
248
109
|
return this
|
|
249
110
|
}
|
|
250
111
|
|
|
251
|
-
/**
|
|
252
|
-
* @param {string} displayText
|
|
253
|
-
* @param {string} copyCode
|
|
254
|
-
* @param {object} [opts]
|
|
255
|
-
*/
|
|
256
112
|
copy(displayText, copyCode, opts = {}) {
|
|
257
113
|
this._buttons.push({
|
|
258
114
|
name: 'cta_copy',
|
|
259
|
-
buttonParamsJson: JSON.stringify({
|
|
260
|
-
display_text: displayText,
|
|
261
|
-
copy_code: copyCode,
|
|
262
|
-
...opts,
|
|
263
|
-
}),
|
|
115
|
+
buttonParamsJson: JSON.stringify({ display_text: displayText, copy_code: copyCode, ...opts }),
|
|
264
116
|
})
|
|
265
117
|
return this
|
|
266
118
|
}
|
|
267
119
|
|
|
268
|
-
/**
|
|
269
|
-
* Send-location button. No display text — the params object is opaque.
|
|
270
|
-
*
|
|
271
|
-
* @param {object} [opts]
|
|
272
|
-
*/
|
|
273
120
|
location(opts = {}) {
|
|
274
|
-
this._buttons.push({
|
|
275
|
-
name: 'send_location',
|
|
276
|
-
buttonParamsJson: JSON.stringify(opts),
|
|
277
|
-
})
|
|
121
|
+
this._buttons.push({ name: 'send_location', buttonParamsJson: JSON.stringify(opts) })
|
|
278
122
|
return this
|
|
279
123
|
}
|
|
280
124
|
|
|
281
|
-
/**
|
|
282
|
-
* Add a single-select button via a callback. The callback receives a
|
|
283
|
-
* {@link SelectionBuilder} so sections & rows can be composed without
|
|
284
|
-
* touching outer state.
|
|
285
|
-
*
|
|
286
|
-
* @example
|
|
287
|
-
* button.selection('Menu', sel => sel
|
|
288
|
-
* .section('Main', s => s.row('A', 'Sub A', 'desc', 'a_id').row('B', 'Sub B', 'desc', 'b_id'))
|
|
289
|
-
* .section('Extras', s => s.row('C', 'Sub C', 'desc', 'c_id'))
|
|
290
|
-
* )
|
|
291
|
-
*
|
|
292
|
-
* @param {string} title
|
|
293
|
-
* @param {(sel: SelectionBuilder) => void} builder
|
|
294
|
-
* @param {object} [extra]
|
|
295
|
-
*/
|
|
296
125
|
selection(title, builder, extra = {}) {
|
|
297
126
|
const sel = new SelectionBuilder()
|
|
298
127
|
sel.title(title)
|
|
299
128
|
builder(sel)
|
|
300
|
-
|
|
301
|
-
this._buttons.push({
|
|
302
|
-
...extra,
|
|
303
|
-
name: 'single_select',
|
|
304
|
-
buttonParamsJson: JSON.stringify(params),
|
|
305
|
-
})
|
|
129
|
+
this._buttons.push({ ...extra, name: 'single_select', buttonParamsJson: JSON.stringify(sel.build()) })
|
|
306
130
|
return this
|
|
307
131
|
}
|
|
308
132
|
|
|
309
|
-
/** Remove all configured buttons. */
|
|
310
133
|
clear() {
|
|
311
134
|
this._buttons = []
|
|
312
135
|
return this
|
|
313
136
|
}
|
|
314
137
|
|
|
315
|
-
// ─── Lifecycle ──────────────────────────────────────────────────────
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Build a proto-ready "card" object — useful when this button is being
|
|
319
|
-
* embedded inside a carousel card rather than sent standalone.
|
|
320
|
-
*
|
|
321
|
-
* @returns {Promise<object>}
|
|
322
|
-
*/
|
|
323
138
|
async toCard() {
|
|
324
139
|
let mediaPayload = {}
|
|
325
140
|
if (this._data) {
|
|
326
141
|
try {
|
|
327
|
-
// Use Connection.uploadMedia — Layer 4 must not touch Baileys directly.
|
|
328
142
|
mediaPayload = await this.#conn.uploadMedia(this._data)
|
|
329
143
|
} catch (err) {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
// fallback so a builder with a non-uploadable source still produces
|
|
333
|
-
// something usable.
|
|
334
|
-
if (err?.message?.includes('Invalid media type')) {
|
|
335
|
-
mediaPayload = this._data
|
|
336
|
-
} else {
|
|
337
|
-
throw err
|
|
338
|
-
}
|
|
144
|
+
if (err?.message?.includes('Invalid media type')) mediaPayload = this._data
|
|
145
|
+
else throw err
|
|
339
146
|
}
|
|
340
147
|
}
|
|
341
|
-
|
|
342
148
|
return {
|
|
343
149
|
body: { text: this._body },
|
|
344
150
|
footer: { text: this._footer },
|
|
@@ -355,35 +161,15 @@ export class ButtonBuilder {
|
|
|
355
161
|
}
|
|
356
162
|
}
|
|
357
163
|
|
|
358
|
-
/**
|
|
359
|
-
* Build the full proto message object (without relaying).
|
|
360
|
-
*
|
|
361
|
-
* @param {string} jid
|
|
362
|
-
* @param {object} [opts]
|
|
363
|
-
* @returns {Promise<object>}
|
|
364
|
-
*/
|
|
365
164
|
async build(jid, opts = {}) {
|
|
366
165
|
const card = await this.toCard()
|
|
367
166
|
return this.#conn.generateMessage(
|
|
368
167
|
jid,
|
|
369
|
-
{
|
|
370
|
-
...this._extraPayload,
|
|
371
|
-
interactiveMessage: {
|
|
372
|
-
...card,
|
|
373
|
-
contextInfo: this._contextInfo,
|
|
374
|
-
},
|
|
375
|
-
},
|
|
168
|
+
{ ...this._extraPayload, interactiveMessage: { ...card, contextInfo: this._contextInfo } },
|
|
376
169
|
opts,
|
|
377
170
|
)
|
|
378
171
|
}
|
|
379
172
|
|
|
380
|
-
/**
|
|
381
|
-
* Build + relay via the interactive channel (with biz/native_flow nodes).
|
|
382
|
-
*
|
|
383
|
-
* @param {string} jid
|
|
384
|
-
* @param {object} [opts]
|
|
385
|
-
* @returns {Promise<object>}
|
|
386
|
-
*/
|
|
387
173
|
async send(jid, opts = {}) {
|
|
388
174
|
const msg = await this.build(jid, opts)
|
|
389
175
|
await this.#conn.relayMessage(msg.key.remoteJid, msg.message, {
|
package/src/builders/card.js
CHANGED
|
@@ -1,57 +1,19 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file builders/card.js
|
|
3
|
-
* @module lumina/builders/card
|
|
4
|
-
*
|
|
5
|
-
* CardBuilder — composes a single carousel card. The legacy `Carousel` class
|
|
6
|
-
* required the user to construct card objects manually (including calling
|
|
7
|
-
* `prepareWAMessageMedia` themselves), which leaked the proto layer.
|
|
8
|
-
*
|
|
9
|
-
* CardBuilder is structurally identical to {@link ButtonBuilder} minus the
|
|
10
|
-
* `send()` method — cards are not standalone messages, they're embedded
|
|
11
|
-
* inside a carousel.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
1
|
import { applyContentFields } from './base.js'
|
|
15
2
|
import { coerceMediaSource, ensurePlainObject } from '../utils/validator.js'
|
|
3
|
+
import { SimpleButtonType } from '../proto/enums.js'
|
|
16
4
|
import { uuid } from '../utils/id.js'
|
|
17
5
|
|
|
18
|
-
/** @typedef {import('../client/connection.js').Connection} Connection */
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Registry of simple button types (same as ButtonBuilder, kept local so the
|
|
22
|
-
* card module stays self-contained for tree-shaking).
|
|
23
|
-
* @type {Record<string, string>}
|
|
24
|
-
*/
|
|
25
|
-
const SIMPLE_BUTTON_TYPES = {
|
|
26
|
-
reply: 'quick_reply',
|
|
27
|
-
call: 'cta_call',
|
|
28
|
-
reminder: 'cta_reminder',
|
|
29
|
-
cancelReminder: 'cta_cancel_reminder',
|
|
30
|
-
address: 'address_message',
|
|
31
|
-
}
|
|
32
|
-
|
|
33
6
|
export class CardBuilder {
|
|
34
|
-
/** @param {Connection} conn */
|
|
35
7
|
constructor(conn) {
|
|
36
8
|
applyContentFields(this)
|
|
37
9
|
this.#conn = conn
|
|
38
|
-
/** @type {Array<{ name: string, buttonParamsJson: string }>} */
|
|
39
10
|
this._buttons = []
|
|
40
|
-
/** @type {object | null} */
|
|
41
11
|
this._data = null
|
|
42
|
-
/** @type {object} */
|
|
43
12
|
this._params = {}
|
|
44
13
|
}
|
|
45
14
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
// ─── Media ──────────────────────────────────────────────────────────
|
|
15
|
+
#conn
|
|
49
16
|
|
|
50
|
-
/**
|
|
51
|
-
* @param {'image'|'video'|'document'} type
|
|
52
|
-
* @param {string | Buffer | object} source
|
|
53
|
-
* @param {object} [opts]
|
|
54
|
-
*/
|
|
55
17
|
media(type, source, opts = {}) {
|
|
56
18
|
if (!['image', 'video', 'document'].includes(type)) {
|
|
57
19
|
throw new TypeError(`media type must be image | video | document, got: ${type}`)
|
|
@@ -61,22 +23,9 @@ export class CardBuilder {
|
|
|
61
23
|
return this
|
|
62
24
|
}
|
|
63
25
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** @param {string | Buffer} source */
|
|
70
|
-
video(source, opts) {
|
|
71
|
-
return this.media('video', source, opts)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** @param {string | Buffer} source */
|
|
75
|
-
document(source, opts) {
|
|
76
|
-
return this.media('document', source, opts)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// ─── Buttons ────────────────────────────────────────────────────────
|
|
26
|
+
image(source, opts) { return this.media('image', source, opts) }
|
|
27
|
+
video(source, opts) { return this.media('video', source, opts) }
|
|
28
|
+
document(source, opts) { return this.media('document', source, opts) }
|
|
80
29
|
|
|
81
30
|
params(obj) {
|
|
82
31
|
ensurePlainObject(obj, 'params')
|
|
@@ -95,7 +44,7 @@ export class CardBuilder {
|
|
|
95
44
|
|
|
96
45
|
reply(displayText, id = uuid(), opts = {}) {
|
|
97
46
|
this._buttons.push({
|
|
98
|
-
name:
|
|
47
|
+
name: SimpleButtonType.reply,
|
|
99
48
|
buttonParamsJson: JSON.stringify({ display_text: displayText, id, ...opts }),
|
|
100
49
|
})
|
|
101
50
|
return this
|
|
@@ -117,37 +66,21 @@ export class CardBuilder {
|
|
|
117
66
|
copy(displayText, copyCode, opts = {}) {
|
|
118
67
|
this._buttons.push({
|
|
119
68
|
name: 'cta_copy',
|
|
120
|
-
buttonParamsJson: JSON.stringify({
|
|
121
|
-
display_text: displayText,
|
|
122
|
-
copy_code: copyCode,
|
|
123
|
-
...opts,
|
|
124
|
-
}),
|
|
69
|
+
buttonParamsJson: JSON.stringify({ display_text: displayText, copy_code: copyCode, ...opts }),
|
|
125
70
|
})
|
|
126
71
|
return this
|
|
127
72
|
}
|
|
128
73
|
|
|
129
|
-
// ─── Lifecycle ──────────────────────────────────────────────────────
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Build the proto-ready card object.
|
|
133
|
-
*
|
|
134
|
-
* @returns {Promise<object>}
|
|
135
|
-
*/
|
|
136
74
|
async build() {
|
|
137
75
|
let mediaPayload = {}
|
|
138
76
|
if (this._data) {
|
|
139
77
|
try {
|
|
140
|
-
// Layer 4 must not touch Baileys directly — go through Connection.
|
|
141
78
|
mediaPayload = await this.#conn.uploadMedia(this._data)
|
|
142
79
|
} catch (err) {
|
|
143
|
-
if (err?.message?.includes('Invalid media type'))
|
|
144
|
-
|
|
145
|
-
} else {
|
|
146
|
-
throw err
|
|
147
|
-
}
|
|
80
|
+
if (err?.message?.includes('Invalid media type')) mediaPayload = this._data
|
|
81
|
+
else throw err
|
|
148
82
|
}
|
|
149
83
|
}
|
|
150
|
-
|
|
151
84
|
return {
|
|
152
85
|
body: { text: this._body },
|
|
153
86
|
footer: { text: this._footer },
|
package/src/builders/carousel.js
CHANGED
|
@@ -1,62 +1,24 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file builders/carousel.js
|
|
3
|
-
* @module lumina/builders/carousel
|
|
4
|
-
*
|
|
5
|
-
* CarouselBuilder — composes a `carouselMessage` containing one or more
|
|
6
|
-
* cards produced by {@link CardBuilder}.
|
|
7
|
-
*
|
|
8
|
-
* Bug fix vs legacy: the original `Carousel.send()` forgot to `await`
|
|
9
|
-
* `build()`. Lumina's version is consistently async and awaits throughout.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
1
|
import { applyContentFields } from './base.js'
|
|
13
2
|
import { createInteractiveNodes } from '../proto/relay-nodes.js'
|
|
14
3
|
import { CardBuilder } from './card.js'
|
|
15
4
|
|
|
16
|
-
/** @typedef {import('../client/connection.js').Connection} Connection */
|
|
17
|
-
/** @typedef {import('../services/proto-service.js').ProtoService} ProtoService */
|
|
18
|
-
/** @typedef {import('../services/media-service.js').MediaService} MediaService */
|
|
19
|
-
|
|
20
5
|
export class CarouselBuilder {
|
|
21
|
-
/**
|
|
22
|
-
* @param {Connection} conn
|
|
23
|
-
* @param {ProtoService} proto
|
|
24
|
-
* @param {MediaService} media
|
|
25
|
-
*/
|
|
26
6
|
constructor(conn, proto, media) {
|
|
27
7
|
applyContentFields(this, { body: '', footer: '' })
|
|
28
8
|
this.#conn = conn
|
|
29
9
|
this.#proto = proto
|
|
30
10
|
this.#media = media
|
|
31
|
-
/** @type {Array<object>} raw, already-built card objects */
|
|
32
11
|
this._cards = []
|
|
33
12
|
}
|
|
34
13
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
14
|
+
#conn
|
|
15
|
+
#proto
|
|
16
|
+
#media
|
|
38
17
|
|
|
39
|
-
/**
|
|
40
|
-
* Create a fresh {@link CardBuilder} bound to this carousel's connection.
|
|
41
|
-
* User builds it inline, calls `.build()` on it, and passes the result
|
|
42
|
-
* back to {@link card}.
|
|
43
|
-
*
|
|
44
|
-
* @example
|
|
45
|
-
* const card = await carousel.newCard()
|
|
46
|
-
* .title('A').image('a.jpg').reply('Buy', 'buy_a').build()
|
|
47
|
-
* carousel.card(card)
|
|
48
|
-
*
|
|
49
|
-
* @returns {CardBuilder}
|
|
50
|
-
*/
|
|
51
18
|
newCard() {
|
|
52
19
|
return new CardBuilder(this.#conn)
|
|
53
20
|
}
|
|
54
21
|
|
|
55
|
-
/**
|
|
56
|
-
* Append one or more pre-built cards.
|
|
57
|
-
*
|
|
58
|
-
* @param {object | object[]} card
|
|
59
|
-
*/
|
|
60
22
|
card(card) {
|
|
61
23
|
const cards = Array.isArray(card) ? card : [card]
|
|
62
24
|
for (const [i, c] of cards.entries()) {
|
|
@@ -68,22 +30,10 @@ export class CarouselBuilder {
|
|
|
68
30
|
return this
|
|
69
31
|
}
|
|
70
32
|
|
|
71
|
-
/**
|
|
72
|
-
* Variadic alias for {@link card}.
|
|
73
|
-
*
|
|
74
|
-
* @param {...object} cards
|
|
75
|
-
*/
|
|
76
33
|
cards(...cards) {
|
|
77
|
-
return this.card(cards)
|
|
34
|
+
return this.card(cards.flat())
|
|
78
35
|
}
|
|
79
36
|
|
|
80
|
-
/**
|
|
81
|
-
* Build the proto message object (without relaying).
|
|
82
|
-
*
|
|
83
|
-
* @param {string} jid
|
|
84
|
-
* @param {object} [opts]
|
|
85
|
-
* @returns {Promise<object>}
|
|
86
|
-
*/
|
|
87
37
|
async build(jid, opts = {}) {
|
|
88
38
|
return this.#conn.generateMessage(
|
|
89
39
|
jid,
|
|
@@ -101,13 +51,6 @@ export class CarouselBuilder {
|
|
|
101
51
|
)
|
|
102
52
|
}
|
|
103
53
|
|
|
104
|
-
/**
|
|
105
|
-
* Build + relay.
|
|
106
|
-
*
|
|
107
|
-
* @param {string} jid
|
|
108
|
-
* @param {object} [opts]
|
|
109
|
-
* @returns {Promise<object>}
|
|
110
|
-
*/
|
|
111
54
|
async send(jid, opts = {}) {
|
|
112
55
|
const msg = await this.build(jid, opts)
|
|
113
56
|
await this.#conn.relayMessage(msg.key.remoteJid, msg.message, {
|
package/src/builders/index.js
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file builders/index.js
|
|
3
|
-
* @module lumina/builders
|
|
4
|
-
*
|
|
5
|
-
* Barrel re-export for the builders layer.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
1
|
export { ButtonBuilder } from './button.js'
|
|
9
2
|
export { ButtonV2Builder } from './button-v2.js'
|
|
10
3
|
export { CarouselBuilder } from './carousel.js'
|