@kyyinfinite/lumina 1.0.0 → 1.0.2

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 (40) hide show
  1. package/package.json +1 -1
  2. package/src/builders/ai-rich.js +0 -165
  3. package/src/builders/base.js +4 -51
  4. package/src/builders/button-v2.js +13 -78
  5. package/src/builders/button.js +20 -234
  6. package/src/builders/card.js +9 -76
  7. package/src/builders/carousel.js +4 -61
  8. package/src/builders/index.js +1 -7
  9. package/src/builders/sticker.js +102 -0
  10. package/src/client/bot.js +28 -153
  11. package/src/client/connection.js +4 -111
  12. package/src/errors.js +0 -37
  13. package/src/index.d.ts +1 -28
  14. package/src/index.js +23 -121
  15. package/src/media/fetch.js +2 -33
  16. package/src/media/image.js +1 -41
  17. package/src/media/resolver.js +3 -55
  18. package/src/media/sticker.js +124 -0
  19. package/src/media/uploader.js +0 -30
  20. package/src/media/video.js +1 -39
  21. package/src/parsers/code-tokenizer-keywords.js +0 -12
  22. package/src/parsers/code-tokenizer.js +0 -42
  23. package/src/parsers/index.js +0 -7
  24. package/src/parsers/inline-entity.js +8 -117
  25. package/src/parsers/table-metadata.js +1 -35
  26. package/src/proto/enums.js +9 -65
  27. package/src/proto/layouts.js +3 -64
  28. package/src/proto/primitives.js +4 -91
  29. package/src/proto/relay-nodes.js +1 -32
  30. package/src/proto/rich-response.js +6 -57
  31. package/src/proto/updater.js +0 -85
  32. package/src/services/index.js +0 -7
  33. package/src/services/media-service.js +1 -102
  34. package/src/services/message-service.js +16 -158
  35. package/src/services/proto-service.js +3 -57
  36. package/src/utils/id.js +0 -25
  37. package/src/utils/logger.js +2 -39
  38. package/src/utils/mime.js +17 -73
  39. package/src/utils/promise.js +0 -26
  40. package/src/utils/validator.js +6 -71
@@ -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
- const sb = new SectionBuilder(section.rows)
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
- /** @type {Connection} */ #conn
121
- /** @type {ProtoService} */ #proto
122
- /** @type {MediaService} */ #media
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
- /** @param {string | Buffer} source */
144
- image(source, opts) {
145
- return this.media('image', source, opts)
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 = SIMPLE_BUTTON_TYPES[kind]
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
- /** @param {string} displayText @param {string} [id] @param {object} [opts] */
209
- reply(displayText, id, opts) {
210
- return this.simpleButton('reply', displayText, id, opts)
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
- const params = sel.build()
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
- // Legacy code string-matched 'Invalid media type' too brittle.
331
- // We surface the original error but keep the raw `_data` as a
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, {
@@ -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
- /** @type {Connection} */ #conn
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
- /** @param {string | Buffer} source */
65
- image(source, opts) {
66
- return this.media('image', source, opts)
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: SIMPLE_BUTTON_TYPES.reply,
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
- mediaPayload = this._data
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 },
@@ -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
- /** @type {Connection} */ #conn
36
- /** @type {ProtoService} */ #proto
37
- /** @type {MediaService} */ #media
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, {
@@ -1,13 +1,7 @@
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'
11
4
  export { CardBuilder } from './card.js'
12
5
  export { AIRichBuilder } from './ai-rich.js'
6
+ export { StickerBuilder } from './sticker.js'
13
7
  export { applyContentFields, readContentFields } from './base.js'