@kyyinfinite/lumina 1.0.4 → 1.0.6
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 +0 -82
- package/package.json +1 -1
- package/src/builders/ai-rich.js +59 -209
- package/src/media/video.js +19 -30
- package/src/parsers/code-tokenizer-keywords.js +0 -2
- package/src/parsers/code-tokenizer.js +0 -4
- package/src/proto/updater.js +0 -12
package/README.md
CHANGED
|
@@ -360,88 +360,6 @@ Type-checked features:
|
|
|
360
360
|
- Enum-like catalogs (`MessageType`, `HeaderType`, `LayoutKind`, ...) are `Readonly`
|
|
361
361
|
- `Bot.raw` escape hatch typed as `WASocket`
|
|
362
362
|
|
|
363
|
-
|
|
364
|
-
---
|
|
365
|
-
|
|
366
|
-
## Baileys Compatibility
|
|
367
|
-
|
|
368
|
-
Lumina is not tied to a specific Baileys version or fork. When `new Bot(sock)` is called, Lumina automatically detects the available Baileys package in your project.
|
|
369
|
-
|
|
370
|
-
### Auto-Detect (default)
|
|
371
|
-
|
|
372
|
-
No configuration is required. Lumina tries the following packages in order and uses the first one that can be successfully imported:
|
|
373
|
-
|
|
374
|
-
```
|
|
375
|
-
@kyyinfinite/baileys
|
|
376
|
-
@whiskeysockets/baileys
|
|
377
|
-
baileys
|
|
378
|
-
@adiwajshing/baileys
|
|
379
|
-
@brunocgc/baileys
|
|
380
|
-
@open-wa/baileys
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
```js
|
|
384
|
-
// Lumina automatically detects @kyyinfinite/baileys (or any installed fork)
|
|
385
|
-
const bot = new Bot(sock)
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
---
|
|
389
|
-
|
|
390
|
-
### Explicit Package Name
|
|
391
|
-
|
|
392
|
-
If you're using a custom-named fork, specify it with `baileysPackage`:
|
|
393
|
-
|
|
394
|
-
```js
|
|
395
|
-
const bot = new Bot(sock, {
|
|
396
|
-
baileysPackage: '@namafork/baileys',
|
|
397
|
-
})
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
---
|
|
401
|
-
|
|
402
|
-
### Module Injection
|
|
403
|
-
|
|
404
|
-
The fastest option — zero dynamic imports. Simply inject the module directly:
|
|
405
|
-
|
|
406
|
-
```js
|
|
407
|
-
import * as baileys from '@kyyinfinite/baileys'
|
|
408
|
-
|
|
409
|
-
const bot = new Bot(sock, {
|
|
410
|
-
baileys,
|
|
411
|
-
})
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
This is ideal if you've already imported Baileys elsewhere and want to avoid duplicate imports.
|
|
415
|
-
|
|
416
|
-
---
|
|
417
|
-
|
|
418
|
-
### Socket Fallback
|
|
419
|
-
|
|
420
|
-
If no Baileys package is found, Lumina falls back to its internal socket-based implementation. Core features (text, buttons, carousel, stickers) continue to work. Media uploads require `sock.waUploadToServer` to be available.
|
|
421
|
-
|
|
422
|
-
---
|
|
423
|
-
|
|
424
|
-
### Debug: Check the Active Adapter
|
|
425
|
-
|
|
426
|
-
```js
|
|
427
|
-
const bot = new Bot(sock)
|
|
428
|
-
console.log(await bot.connection.adapterSource())
|
|
429
|
-
// → '@kyyinfinite/baileys'
|
|
430
|
-
// → '@whiskeysockets/baileys'
|
|
431
|
-
// → 'injected-module'
|
|
432
|
-
// → 'socket-fallback'
|
|
433
|
-
```
|
|
434
|
-
|
|
435
|
-
---
|
|
436
|
-
|
|
437
|
-
### Summary
|
|
438
|
-
|
|
439
|
-
| Option | When to use |
|
|
440
|
-
|---|---|
|
|
441
|
-
| _(not set)_ | Auto-detect. Recommended for most use cases. |
|
|
442
|
-
| `baileysPackage: 'name'` | For custom forks that are not included in the auto-detect list. |
|
|
443
|
-
| `baileys: module` | Manual module injection for the best performance. |
|
|
444
|
-
|
|
445
363
|
---
|
|
446
364
|
|
|
447
365
|
## API Reference
|
package/package.json
CHANGED
package/src/builders/ai-rich.js
CHANGED
|
@@ -1,61 +1,43 @@
|
|
|
1
|
-
|
|
2
1
|
import { applyContentFields } from './base.js'
|
|
3
2
|
import { ensureObjectOrArray, ensureString, ensureStringArray } from '../utils/validator.js'
|
|
4
3
|
import { ValidationError } from '../errors.js'
|
|
5
|
-
|
|
6
4
|
import { extractInlineEntities } from '../parsers/inline-entity.js'
|
|
7
5
|
import { tokenizeCode } from '../parsers/code-tokenizer.js'
|
|
8
6
|
import { toTableMetadata } from '../parsers/table-metadata.js'
|
|
9
|
-
|
|
10
7
|
import {
|
|
11
|
-
markdownTextPrimitive,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
searchResultPrimitive,
|
|
15
|
-
reelPrimitive,
|
|
16
|
-
imaginePrimitive,
|
|
17
|
-
productCardPrimitive,
|
|
18
|
-
postPrimitive,
|
|
19
|
-
metadataTextPrimitive,
|
|
20
|
-
followUpSuggestionPillPrimitive,
|
|
21
|
-
shapeSourceEntry,
|
|
22
|
-
shapeReelEntry,
|
|
8
|
+
markdownTextPrimitive, codePrimitive, tablePrimitive, searchResultPrimitive,
|
|
9
|
+
reelPrimitive, imaginePrimitive, productCardPrimitive, postPrimitive,
|
|
10
|
+
metadataTextPrimitive, followUpSuggestionPillPrimitive, shapeSourceEntry, shapeReelEntry,
|
|
23
11
|
} from '../proto/primitives.js'
|
|
24
12
|
import { singleLayout, hscrollLayout, actionRowLayout } from '../proto/layouts.js'
|
|
25
13
|
import { assembleRichResponse } from '../proto/rich-response.js'
|
|
26
14
|
import { MessageType, ImagineType, LayoutKind } from '../proto/enums.js'
|
|
27
15
|
|
|
28
|
-
|
|
29
16
|
export class AIRichBuilder {
|
|
17
|
+
#conn
|
|
18
|
+
#proto
|
|
19
|
+
#media
|
|
20
|
+
|
|
30
21
|
constructor(conn, proto, media) {
|
|
31
22
|
applyContentFields(this)
|
|
32
23
|
this.#conn = conn
|
|
33
24
|
this.#proto = proto
|
|
34
25
|
this.#media = media
|
|
35
|
-
|
|
36
26
|
this._submessages = []
|
|
37
27
|
this._sections = []
|
|
38
28
|
this._richResponseSources = []
|
|
39
|
-
|
|
40
29
|
this._forwarded = true
|
|
41
30
|
this._notification = false
|
|
42
31
|
this._includesUnifiedResponse = true
|
|
43
32
|
this._includesSubmessages = true
|
|
44
|
-
this._quoted
|
|
45
|
-
this._quotedParticipant
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
forwarded(v = true) {
|
|
51
|
-
this._forwarded = v
|
|
52
|
-
return this
|
|
33
|
+
this._quoted = undefined
|
|
34
|
+
this._quotedParticipant = undefined
|
|
53
35
|
}
|
|
54
36
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
37
|
+
forwarded(v = true) { this._forwarded = v; return this }
|
|
38
|
+
notification(v) { this._notification = v; return this }
|
|
39
|
+
includesUnifiedResponse(v = true) { this._includesUnifiedResponse = v; return this }
|
|
40
|
+
includesSubmessages(v = true) { this._includesSubmessages = v; return this }
|
|
59
41
|
|
|
60
42
|
quoted(quoted, quotedParticipant) {
|
|
61
43
|
this._quoted = quoted
|
|
@@ -63,16 +45,6 @@ export class AIRichBuilder {
|
|
|
63
45
|
return this
|
|
64
46
|
}
|
|
65
47
|
|
|
66
|
-
includesUnifiedResponse(v = true) {
|
|
67
|
-
this._includesUnifiedResponse = v
|
|
68
|
-
return this
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
includesSubmessages(v = true) {
|
|
72
|
-
this._includesSubmessages = v
|
|
73
|
-
return this
|
|
74
|
-
}
|
|
75
|
-
|
|
76
48
|
submessage(msg) {
|
|
77
49
|
const items = Array.isArray(msg) ? msg : [msg]
|
|
78
50
|
items.forEach((m) => ensureObjectOrArray(m, 'submessage'))
|
|
@@ -80,42 +52,23 @@ export class AIRichBuilder {
|
|
|
80
52
|
return this
|
|
81
53
|
}
|
|
82
54
|
|
|
83
|
-
|
|
84
55
|
async text(text, opts = {}) {
|
|
85
56
|
ensureString(text, 'text')
|
|
86
57
|
const { hyperlink = true, citation = true, latex = true } = opts
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
citation,
|
|
91
|
-
latex,
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
this._submessages.push({
|
|
95
|
-
messageType: MessageType.TEXT,
|
|
96
|
-
messageText: rewritten,
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
this._sections.push(
|
|
100
|
-
singleLayout(markdownTextPrimitive(rewritten, metadata.length ? metadata : undefined)),
|
|
101
|
-
)
|
|
58
|
+
const { text: rewritten, metadata } = extractInlineEntities(text, { hyperlink, citation, latex })
|
|
59
|
+
this._submessages.push({ messageType: MessageType.TEXT, messageText: rewritten })
|
|
60
|
+
this._sections.push(singleLayout(markdownTextPrimitive(rewritten, metadata.length ? metadata : undefined)))
|
|
102
61
|
return this
|
|
103
62
|
}
|
|
104
63
|
|
|
105
64
|
async code(language, code) {
|
|
106
65
|
ensureString(language, 'language')
|
|
107
66
|
ensureString(code, 'code')
|
|
108
|
-
|
|
109
67
|
const { codeBlock, unifiedBlocks } = tokenizeCode(code, language)
|
|
110
|
-
|
|
111
68
|
this._submessages.push({
|
|
112
69
|
messageType: MessageType.CODE,
|
|
113
|
-
codeMetadata: {
|
|
114
|
-
codeLanguage: language,
|
|
115
|
-
codeBlocks: codeBlock,
|
|
116
|
-
},
|
|
70
|
+
codeMetadata: { codeLanguage: language, codeBlocks: codeBlock },
|
|
117
71
|
})
|
|
118
|
-
|
|
119
72
|
this._sections.push(singleLayout(codePrimitive(language, unifiedBlocks)))
|
|
120
73
|
return this
|
|
121
74
|
}
|
|
@@ -126,34 +79,22 @@ export class AIRichBuilder {
|
|
|
126
79
|
}
|
|
127
80
|
const { title = '', hyperlink = true, citation = true, latex = true } = opts
|
|
128
81
|
const meta = toTableMetadata(table, { title, hyperlink, citation, latex })
|
|
129
|
-
|
|
130
82
|
this._submessages.push({
|
|
131
83
|
messageType: MessageType.TABLE,
|
|
132
84
|
tableMetadata: { title: meta.title, rows: meta.rows },
|
|
133
85
|
})
|
|
134
|
-
|
|
135
86
|
this._sections.push(singleLayout(tablePrimitive(meta.unifiedRows)))
|
|
136
87
|
return this
|
|
137
88
|
}
|
|
138
89
|
|
|
139
90
|
async image(source, opts = {}) {
|
|
140
|
-
const { resolveUrl = false } = opts
|
|
141
91
|
const list = Array.isArray(source) ? source : [source]
|
|
142
|
-
|
|
143
92
|
const resolved = await Promise.all(
|
|
144
93
|
list.map(async (s) => {
|
|
145
|
-
const url = await this.#media.resolve(s, {
|
|
146
|
-
|
|
147
|
-
strategy: resolveUrl ? 'auto' : 'auto',
|
|
148
|
-
})
|
|
149
|
-
return {
|
|
150
|
-
imagePreviewUrl: url,
|
|
151
|
-
imageHighResUrl: url,
|
|
152
|
-
sourceUrl: url,
|
|
153
|
-
}
|
|
94
|
+
const url = await this.#media.resolve(s, { mediaType: 'image', strategy: 'auto' })
|
|
95
|
+
return { imagePreviewUrl: url, imageHighResUrl: url, sourceUrl: url }
|
|
154
96
|
}),
|
|
155
97
|
)
|
|
156
|
-
|
|
157
98
|
this._submessages.push({
|
|
158
99
|
messageType: MessageType.RICH_RESPONSE,
|
|
159
100
|
gridImageMetadata: {
|
|
@@ -161,15 +102,9 @@ export class AIRichBuilder {
|
|
|
161
102
|
imageUrls: resolved,
|
|
162
103
|
},
|
|
163
104
|
})
|
|
164
|
-
|
|
165
105
|
for (const r of resolved) {
|
|
166
106
|
this._sections.push(
|
|
167
|
-
singleLayout(
|
|
168
|
-
imaginePrimitive(
|
|
169
|
-
{ url: r.imagePreviewUrl, mime_type: 'image/png' },
|
|
170
|
-
ImagineType.IMAGE,
|
|
171
|
-
),
|
|
172
|
-
),
|
|
107
|
+
singleLayout(imaginePrimitive({ url: r.imagePreviewUrl, mime_type: 'image/png' }, ImagineType.IMAGE)),
|
|
173
108
|
)
|
|
174
109
|
}
|
|
175
110
|
return this
|
|
@@ -177,22 +112,15 @@ export class AIRichBuilder {
|
|
|
177
112
|
|
|
178
113
|
async video(source, opts = {}) {
|
|
179
114
|
const { autoFill = true } = opts
|
|
180
|
-
const
|
|
115
|
+
const isObjVideo = (v) => v && typeof v === 'object' && v.url
|
|
181
116
|
const items = Array.isArray(source) ? source : [source]
|
|
182
117
|
|
|
183
|
-
this._submessages.push({
|
|
184
|
-
messageType: MessageType.TEXT,
|
|
185
|
-
messageText: '[ CANNOT_LOAD_VIDEO ]',
|
|
186
|
-
})
|
|
118
|
+
this._submessages.push({ messageType: MessageType.TEXT, messageText: '[ CANNOT_LOAD_VIDEO ]' })
|
|
187
119
|
|
|
188
120
|
await Promise.all(
|
|
189
121
|
items.map(async (item) => {
|
|
190
|
-
const isObj =
|
|
191
|
-
const url = await this.#media.resolve(
|
|
192
|
-
isObj ? item.url : item,
|
|
193
|
-
{ mediaType: 'video', strategy: 'auto' },
|
|
194
|
-
)
|
|
195
|
-
|
|
122
|
+
const isObj = isObjVideo(item)
|
|
123
|
+
const url = await this.#media.resolve(isObj ? item.url : item, { mediaType: 'video', strategy: 'auto' })
|
|
196
124
|
let fileLength = isObj && item.file_length != null ? item.file_length : 0
|
|
197
125
|
let duration = isObj && item.duration != null ? item.duration : 0
|
|
198
126
|
let thumbnailB64 = null
|
|
@@ -204,26 +132,18 @@ export class AIRichBuilder {
|
|
|
204
132
|
fileLength = buf.length
|
|
205
133
|
duration = this.#media.duration(buf)
|
|
206
134
|
thumbnailB64 = await this.#media.videoThumbnail(buf, {
|
|
207
|
-
time: 0,
|
|
208
|
-
result: 'base64',
|
|
209
|
-
resizeOutput: true,
|
|
210
|
-
width: 300,
|
|
211
|
-
height: 300,
|
|
135
|
+
time: 0, result: 'base64', resizeOutput: true, width: 300, height: 300,
|
|
212
136
|
})
|
|
213
137
|
}
|
|
214
|
-
} catch {
|
|
215
|
-
}
|
|
138
|
+
} catch {}
|
|
216
139
|
}
|
|
217
140
|
|
|
218
141
|
const mimeType = isObj ? item.mime_type ?? 'video/mp4' : 'video/mp4'
|
|
219
|
-
|
|
220
142
|
this._sections.push(
|
|
221
143
|
singleLayout(
|
|
222
144
|
imaginePrimitive(
|
|
223
145
|
{ url, mime_type: mimeType, file_length: fileLength, duration },
|
|
224
|
-
ImagineType.ANIMATE,
|
|
225
|
-
'READY',
|
|
226
|
-
thumbnailB64 || undefined,
|
|
146
|
+
ImagineType.ANIMATE, 'READY', thumbnailB64 || undefined,
|
|
227
147
|
),
|
|
228
148
|
),
|
|
229
149
|
)
|
|
@@ -236,12 +156,7 @@ export class AIRichBuilder {
|
|
|
236
156
|
if (!Array.isArray(sources)) {
|
|
237
157
|
throw new ValidationError('sources must be an array', { code: 'LUMINA_VALIDATION_SOURCE' })
|
|
238
158
|
}
|
|
239
|
-
|
|
240
|
-
const normalised =
|
|
241
|
-
sources.length && typeof sources[0] === 'string'
|
|
242
|
-
? [sources]
|
|
243
|
-
: sources
|
|
244
|
-
|
|
159
|
+
const normalised = sources.length && typeof sources[0] === 'string' ? [sources] : sources
|
|
245
160
|
const entries = await Promise.all(
|
|
246
161
|
normalised.map(async (entry) => {
|
|
247
162
|
const [iconUrl, url, text] = entry
|
|
@@ -251,7 +166,6 @@ export class AIRichBuilder {
|
|
|
251
166
|
return shapeSourceEntry({ iconUrl: resolvedIcon, url, text })
|
|
252
167
|
}),
|
|
253
168
|
)
|
|
254
|
-
|
|
255
169
|
this._sections.push(singleLayout(searchResultPrimitive(entries)))
|
|
256
170
|
return this
|
|
257
171
|
}
|
|
@@ -259,27 +173,17 @@ export class AIRichBuilder {
|
|
|
259
173
|
async reels(items) {
|
|
260
174
|
ensureObjectOrArray(items, 'reels')
|
|
261
175
|
const list = Array.isArray(items) ? items : [items]
|
|
262
|
-
|
|
263
176
|
const resolved = await Promise.all(
|
|
264
177
|
list.map(async (item) => {
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
})
|
|
271
|
-
: Promise.resolve(''),
|
|
272
|
-
item.thumbnailUrl ?? item.thumbnail
|
|
273
|
-
? this.#media.resolve(item.thumbnailUrl ?? item.thumbnail, {
|
|
274
|
-
mediaType: 'image',
|
|
275
|
-
strategy: 'auto',
|
|
276
|
-
})
|
|
277
|
-
: Promise.resolve(''),
|
|
178
|
+
const avatarSrc = item.profileIconUrl ?? item.profile_url ?? item.profile
|
|
179
|
+
const thumbSrc = item.thumbnailUrl ?? item.thumbnail
|
|
180
|
+
const [avatar, thumbnail] = await Promise.all([
|
|
181
|
+
avatarSrc ? this.#media.resolve(avatarSrc, { mediaType: 'image', strategy: 'auto' }) : Promise.resolve(''),
|
|
182
|
+
thumbSrc ? this.#media.resolve(thumbSrc, { mediaType: 'image', strategy: 'auto' }) : Promise.resolve(''),
|
|
278
183
|
])
|
|
279
|
-
return { ...item, avatar, thumbnail
|
|
184
|
+
return { ...item, avatar, thumbnail }
|
|
280
185
|
}),
|
|
281
186
|
)
|
|
282
|
-
|
|
283
187
|
this._submessages.push({
|
|
284
188
|
messageType: MessageType.REELS,
|
|
285
189
|
contentItemsMetadata: {
|
|
@@ -294,7 +198,6 @@ export class AIRichBuilder {
|
|
|
294
198
|
})),
|
|
295
199
|
},
|
|
296
200
|
})
|
|
297
|
-
|
|
298
201
|
resolved.forEach((item, idx) => {
|
|
299
202
|
this._richResponseSources.push({
|
|
300
203
|
provider: 'LUMINA',
|
|
@@ -306,110 +209,62 @@ export class AIRichBuilder {
|
|
|
306
209
|
sourceTitle: item.username ?? '',
|
|
307
210
|
})
|
|
308
211
|
})
|
|
309
|
-
|
|
310
|
-
this._sections.push(
|
|
311
|
-
hscrollLayout(resolved.map((item) => reelPrimitive(shapeReelEntry(item)))),
|
|
312
|
-
)
|
|
212
|
+
this._sections.push(hscrollLayout(resolved.map((item) => reelPrimitive(shapeReelEntry(item)))))
|
|
313
213
|
return this
|
|
314
214
|
}
|
|
315
215
|
|
|
316
216
|
async product(item) {
|
|
317
217
|
ensureObjectOrArray(item, 'product')
|
|
318
218
|
const list = Array.isArray(item) ? item : [item]
|
|
319
|
-
|
|
320
|
-
this._submessages.push({
|
|
321
|
-
messageType: MessageType.TEXT,
|
|
322
|
-
messageText: '[ CANNOT_LOAD_PRODUCT ]',
|
|
323
|
-
})
|
|
324
|
-
|
|
219
|
+
this._submessages.push({ messageType: MessageType.TEXT, messageText: '[ CANNOT_LOAD_PRODUCT ]' })
|
|
325
220
|
const products = await Promise.all(
|
|
326
221
|
list.map(async (p) => {
|
|
222
|
+
const imgSrc = p.image_url ?? p.image
|
|
223
|
+
const iconSrc = p.icon_url ?? p.icon
|
|
327
224
|
const [imageUrl, iconUrl] = await Promise.all([
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
: Promise.resolve(''),
|
|
331
|
-
p.icon_url ?? p.icon
|
|
332
|
-
? this.#media.resolve(p.icon_url ?? p.icon, { mediaType: 'image', strategy: 'auto' })
|
|
333
|
-
: Promise.resolve(''),
|
|
225
|
+
imgSrc ? this.#media.resolve(imgSrc, { mediaType: 'image', strategy: 'auto' }) : Promise.resolve(''),
|
|
226
|
+
iconSrc ? this.#media.resolve(iconSrc, { mediaType: 'image', strategy: 'auto' }) : Promise.resolve(''),
|
|
334
227
|
])
|
|
335
228
|
return {
|
|
336
|
-
title: p.title,
|
|
337
|
-
brand: p.brand,
|
|
338
|
-
price: p.price,
|
|
339
|
-
sale_price: p.sale_price,
|
|
229
|
+
title: p.title, brand: p.brand, price: p.price, sale_price: p.sale_price,
|
|
340
230
|
product_url: p.product_url ?? p.url,
|
|
341
231
|
image: { url: imageUrl },
|
|
342
232
|
additional_images: [{ url: iconUrl }],
|
|
343
233
|
}
|
|
344
234
|
}),
|
|
345
235
|
)
|
|
346
|
-
|
|
347
236
|
const primitives = products.map((p) => productCardPrimitive(p))
|
|
348
|
-
this._sections.push(
|
|
349
|
-
list.length === 1 ? singleLayout(primitives[0]) : hscrollLayout(primitives),
|
|
350
|
-
)
|
|
237
|
+
this._sections.push(list.length === 1 ? singleLayout(primitives[0]) : hscrollLayout(primitives))
|
|
351
238
|
return this
|
|
352
239
|
}
|
|
353
240
|
|
|
354
241
|
async post(item) {
|
|
355
242
|
ensureObjectOrArray(item, 'post')
|
|
356
243
|
const list = Array.isArray(item) ? item : [item]
|
|
357
|
-
|
|
358
|
-
this._submessages.push({
|
|
359
|
-
messageType: MessageType.TEXT,
|
|
360
|
-
messageText: '[ CANNOT_LOAD_POST ]',
|
|
361
|
-
})
|
|
362
|
-
|
|
244
|
+
this._submessages.push({ messageType: MessageType.TEXT, messageText: '[ CANNOT_LOAD_POST ]' })
|
|
363
245
|
const isCarousel = list.length > 1
|
|
364
|
-
|
|
365
246
|
const primitives = await Promise.all(
|
|
366
247
|
list.map(async (p) => {
|
|
248
|
+
const profileSrc = p.profile_picture_url ?? p.profile_url ?? p.profile
|
|
249
|
+
const thumbSrc = p.thumbnail_url ?? p.thumbnail
|
|
250
|
+
const footerSrc = p.footer_icon ?? p.icon
|
|
367
251
|
const [profileUrl, thumbUrl, footerIcon] = await Promise.all([
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
strategy: 'auto',
|
|
372
|
-
})
|
|
373
|
-
: Promise.resolve(''),
|
|
374
|
-
p.thumbnail_url ?? p.thumbnail
|
|
375
|
-
? this.#media.resolve(p.thumbnail_url ?? p.thumbnail, {
|
|
376
|
-
mediaType: 'image',
|
|
377
|
-
strategy: 'auto',
|
|
378
|
-
})
|
|
379
|
-
: Promise.resolve(''),
|
|
380
|
-
p.footer_icon ?? p.icon
|
|
381
|
-
? this.#media.resolve(p.footer_icon ?? p.icon, {
|
|
382
|
-
mediaType: 'image',
|
|
383
|
-
strategy: 'auto',
|
|
384
|
-
})
|
|
385
|
-
: Promise.resolve(''),
|
|
252
|
+
profileSrc ? this.#media.resolve(profileSrc, { mediaType: 'image', strategy: 'auto' }) : Promise.resolve(''),
|
|
253
|
+
thumbSrc ? this.#media.resolve(thumbSrc, { mediaType: 'image', strategy: 'auto' }) : Promise.resolve(''),
|
|
254
|
+
footerSrc ? this.#media.resolve(footerSrc, { mediaType: 'image', strategy: 'auto' }) : Promise.resolve(''),
|
|
386
255
|
])
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
likes_count: p.likes_count ?? p.like ?? 0,
|
|
398
|
-
comments_count: p.comments_count ?? p.comment ?? 0,
|
|
399
|
-
shares_count: p.shares_count ?? p.share ?? 0,
|
|
400
|
-
post_url: p.post_url ?? p.url ?? '',
|
|
401
|
-
post_deeplink: p.post_deeplink ?? p.deeplink ?? '',
|
|
402
|
-
source_app: p.source_app ?? p.source ?? 'INSTAGRAM',
|
|
403
|
-
footer_label: p.footer_label ?? p.footer ?? '',
|
|
404
|
-
footer_icon: footerIcon,
|
|
405
|
-
orientation: p.orientation ?? 'LANDSCAPE',
|
|
406
|
-
post_type: p.post_type ?? 'VIDEO',
|
|
407
|
-
},
|
|
408
|
-
isCarousel,
|
|
409
|
-
)
|
|
256
|
+
return postPrimitive({
|
|
257
|
+
title: p.title ?? '', subtitle: p.subtitle ?? '', username: p.username ?? '',
|
|
258
|
+
profile_picture_url: profileUrl, is_verified: !!(p.is_verified ?? p.verified),
|
|
259
|
+
thumbnail_url: thumbUrl, post_caption: p.post_caption ?? p.caption ?? '',
|
|
260
|
+
likes_count: p.likes_count ?? p.like ?? 0, comments_count: p.comments_count ?? p.comment ?? 0,
|
|
261
|
+
shares_count: p.shares_count ?? p.share ?? 0, post_url: p.post_url ?? p.url ?? '',
|
|
262
|
+
post_deeplink: p.post_deeplink ?? p.deeplink ?? '', source_app: p.source_app ?? p.source ?? 'INSTAGRAM',
|
|
263
|
+
footer_label: p.footer_label ?? p.footer ?? '', footer_icon: footerIcon,
|
|
264
|
+
orientation: p.orientation ?? 'LANDSCAPE', post_type: p.post_type ?? 'VIDEO',
|
|
265
|
+
}, isCarousel)
|
|
410
266
|
}),
|
|
411
267
|
)
|
|
412
|
-
|
|
413
268
|
this._sections.push(hscrollLayout(primitives))
|
|
414
269
|
return this
|
|
415
270
|
}
|
|
@@ -423,15 +278,12 @@ export class AIRichBuilder {
|
|
|
423
278
|
|
|
424
279
|
async suggest(suggestion, opts = {}) {
|
|
425
280
|
const { scroll = true, layout } = opts
|
|
426
|
-
|
|
427
281
|
if (typeof suggestion === 'string') {
|
|
428
282
|
suggestion = [suggestion]
|
|
429
283
|
} else {
|
|
430
284
|
ensureStringArray(suggestion, 'suggestion')
|
|
431
285
|
}
|
|
432
|
-
|
|
433
286
|
const pills = suggestion.map((text) => followUpSuggestionPillPrimitive(text))
|
|
434
|
-
|
|
435
287
|
let kind
|
|
436
288
|
if (layout) {
|
|
437
289
|
kind = layout
|
|
@@ -440,7 +292,6 @@ export class AIRichBuilder {
|
|
|
440
292
|
} else {
|
|
441
293
|
kind = scroll ? LayoutKind.HSCROLL : LayoutKind.ACTION_ROW
|
|
442
294
|
}
|
|
443
|
-
|
|
444
295
|
if (kind === LayoutKind.SINGLE) {
|
|
445
296
|
this._sections.push(singleLayout(pills[0], { __typename: 'GenAIUnifiedResponseSection' }))
|
|
446
297
|
} else if (kind === LayoutKind.HSCROLL) {
|
|
@@ -451,7 +302,6 @@ export class AIRichBuilder {
|
|
|
451
302
|
return this
|
|
452
303
|
}
|
|
453
304
|
|
|
454
|
-
|
|
455
305
|
async build(opts = {}) {
|
|
456
306
|
return assembleRichResponse({
|
|
457
307
|
title: this._title,
|
package/src/media/video.js
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
*
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
|
|
5
1
|
import { PassThrough, Readable } from 'node:stream'
|
|
6
|
-
|
|
7
2
|
import { MediaError } from '../errors.js'
|
|
8
3
|
import { resize } from './image.js'
|
|
9
4
|
|
|
@@ -16,7 +11,7 @@ async function loadFfmpeg() {
|
|
|
16
11
|
return (await import('fluent-ffmpeg')).default
|
|
17
12
|
} catch (err) {
|
|
18
13
|
throw new MediaError(
|
|
19
|
-
|
|
14
|
+
'fluent-ffmpeg is not installed. Run: npm i fluent-ffmpeg',
|
|
20
15
|
{ code: 'LUMINA_MEDIA_FFMPEG_MISSING', cause: err },
|
|
21
16
|
)
|
|
22
17
|
}
|
|
@@ -25,8 +20,6 @@ async function loadFfmpeg() {
|
|
|
25
20
|
return ffmpegPromise
|
|
26
21
|
}
|
|
27
22
|
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
23
|
export function getMp4Duration(buffer, opts = {}) {
|
|
31
24
|
const { silent = true } = opts
|
|
32
25
|
|
|
@@ -36,16 +29,12 @@ export function getMp4Duration(buffer, opts = {}) {
|
|
|
36
29
|
}
|
|
37
30
|
|
|
38
31
|
try {
|
|
39
|
-
if (!Buffer.isBuffer(buffer) || buffer.length < 8)
|
|
40
|
-
return fail('invalid buffer: too short')
|
|
41
|
-
}
|
|
32
|
+
if (!Buffer.isBuffer(buffer) || buffer.length < 8) return fail('invalid buffer: too short')
|
|
42
33
|
|
|
43
34
|
let offset = 0
|
|
44
35
|
while (offset < buffer.length - 8) {
|
|
45
36
|
const size = buffer.readUInt32BE(offset)
|
|
46
|
-
if (size < 8 || offset + size > buffer.length)
|
|
47
|
-
return fail('invalid atom size')
|
|
48
|
-
}
|
|
37
|
+
if (size < 8 || offset + size > buffer.length) return fail('invalid atom size')
|
|
49
38
|
|
|
50
39
|
const type = buffer.toString('ascii', offset + 4, offset + 8)
|
|
51
40
|
if (type === 'moov') {
|
|
@@ -53,9 +42,7 @@ export function getMp4Duration(buffer, opts = {}) {
|
|
|
53
42
|
const moovEnd = offset + size
|
|
54
43
|
while (moovOffset < moovEnd - 8) {
|
|
55
44
|
const childSize = buffer.readUInt32BE(moovOffset)
|
|
56
|
-
if (childSize < 8 || moovOffset + childSize > moovEnd)
|
|
57
|
-
return fail('invalid child atom size')
|
|
58
|
-
}
|
|
45
|
+
if (childSize < 8 || moovOffset + childSize > moovEnd) return fail('invalid child atom size')
|
|
59
46
|
const childType = buffer.toString('ascii', moovOffset + 4, moovOffset + 8)
|
|
60
47
|
if (childType === 'mvhd') {
|
|
61
48
|
const version = buffer.readUInt8(moovOffset + 8)
|
|
@@ -87,8 +74,6 @@ export function getMp4Duration(buffer, opts = {}) {
|
|
|
87
74
|
}
|
|
88
75
|
}
|
|
89
76
|
|
|
90
|
-
|
|
91
|
-
*
|
|
92
77
|
export async function extractThumbnail(videoBuffer, opts = {}) {
|
|
93
78
|
const {
|
|
94
79
|
time,
|
|
@@ -105,10 +90,14 @@ export async function extractThumbnail(videoBuffer, opts = {}) {
|
|
|
105
90
|
return new Promise((resolve, reject) => {
|
|
106
91
|
const fail = (err) => {
|
|
107
92
|
if (silent) return resolve(result === 'base64' ? '' : Buffer.alloc(0))
|
|
108
|
-
reject(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
93
|
+
reject(
|
|
94
|
+
err instanceof MediaError
|
|
95
|
+
? err
|
|
96
|
+
: new MediaError(err?.message ?? String(err), {
|
|
97
|
+
code: 'LUMINA_MEDIA_FFMPEG_FAILED',
|
|
98
|
+
cause: err,
|
|
99
|
+
}),
|
|
100
|
+
)
|
|
112
101
|
}
|
|
113
102
|
|
|
114
103
|
if (!Buffer.isBuffer(videoBuffer) || videoBuffer.length === 0) {
|
|
@@ -130,9 +119,7 @@ export async function extractThumbnail(videoBuffer, opts = {}) {
|
|
|
130
119
|
if (frame.length === 0) {
|
|
131
120
|
return fail(new MediaError('ffmpeg produced empty output', { code: 'LUMINA_MEDIA_FFMPEG_EMPTY' }))
|
|
132
121
|
}
|
|
133
|
-
if (resizeOutput) {
|
|
134
|
-
frame = await resize(frame, { width, height, fit: 'cover', format })
|
|
135
|
-
}
|
|
122
|
+
if (resizeOutput) frame = await resize(frame, { width, height, fit: 'cover', format })
|
|
136
123
|
resolve(result === 'base64' ? frame.toString('base64') : frame)
|
|
137
124
|
} catch (err) {
|
|
138
125
|
fail(err)
|
|
@@ -143,10 +130,12 @@ export async function extractThumbnail(videoBuffer, opts = {}) {
|
|
|
143
130
|
try {
|
|
144
131
|
ffmpeg(input)
|
|
145
132
|
.outputOptions([`-ss ${seekTime}`, '-vframes 1', '-vcodec png', '-f image2pipe'])
|
|
146
|
-
.on('error', (err) =>
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
133
|
+
.on('error', (err) =>
|
|
134
|
+
fail(new MediaError(`ffmpeg error: ${err.message}`, {
|
|
135
|
+
code: 'LUMINA_MEDIA_FFMPEG_FAILED',
|
|
136
|
+
cause: err,
|
|
137
|
+
})),
|
|
138
|
+
)
|
|
150
139
|
.pipe(output, { end: true })
|
|
151
140
|
} catch (err) {
|
|
152
141
|
fail(err)
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
*
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
1
|
|
|
5
2
|
import { HighlightType, HighlightLabel } from '../proto/enums.js'
|
|
6
3
|
import { KEYWORDS, SLASH_COMMENT_LANGS, HASH_COMMENT_LANGS, BLOCK_COMMENT_LANGS } from './code-tokenizer-keywords.js'
|
|
@@ -17,7 +14,6 @@ function identifierChar(lang) {
|
|
|
17
14
|
}
|
|
18
15
|
}
|
|
19
16
|
|
|
20
|
-
*
|
|
21
17
|
export function tokenizeCode(code, lang = 'javascript') {
|
|
22
18
|
if (typeof code !== 'string' || code.length === 0) {
|
|
23
19
|
return { codeBlock: [], unifiedBlocks: [] }
|
package/src/proto/updater.js
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
*
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
1
|
|
|
6
2
|
import { promises as fs } from 'node:fs'
|
|
7
3
|
import path from 'node:path'
|
|
@@ -47,7 +43,6 @@ const RE_CONST_REQUIRE =
|
|
|
47
43
|
const RE_DESTRUCT_REQUIRE =
|
|
48
44
|
/(?:const|let|var)\s*\{([^}]+)\}\s*=\s*require\(\s*(['"])([^'"]+)\2\s*\)/g
|
|
49
45
|
|
|
50
|
-
*
|
|
51
46
|
export function transformToESM(src) {
|
|
52
47
|
let out = src
|
|
53
48
|
out = out.replace(RE_CONST_REQUIRE, (_, name, _q, mod) => `import ${name} from '${mod}'`)
|
|
@@ -67,7 +62,6 @@ export function transformToESM(src) {
|
|
|
67
62
|
return out
|
|
68
63
|
}
|
|
69
64
|
|
|
70
|
-
*
|
|
71
65
|
export function applyKnownFixes(filename, content) {
|
|
72
66
|
const applied = []
|
|
73
67
|
let next = content
|
|
@@ -100,7 +94,6 @@ export class ProtoUpdater {
|
|
|
100
94
|
}
|
|
101
95
|
}
|
|
102
96
|
|
|
103
|
-
*
|
|
104
97
|
async #listJsFiles(dir) {
|
|
105
98
|
const entries = await fs.readdir(dir, { withFileTypes: true })
|
|
106
99
|
const out = []
|
|
@@ -112,7 +105,6 @@ export class ProtoUpdater {
|
|
|
112
105
|
return out
|
|
113
106
|
}
|
|
114
107
|
|
|
115
|
-
*
|
|
116
108
|
async backup() {
|
|
117
109
|
await fs.mkdir(this.backupDir, { recursive: true })
|
|
118
110
|
const timestamp = Date.now()
|
|
@@ -127,7 +119,6 @@ export class ProtoUpdater {
|
|
|
127
119
|
return record
|
|
128
120
|
}
|
|
129
121
|
|
|
130
|
-
*
|
|
131
122
|
async #hashTree(dir) {
|
|
132
123
|
const files = (await this.#listJsFiles(dir)).sort()
|
|
133
124
|
const hashes = await Promise.all(
|
|
@@ -139,7 +130,6 @@ export class ProtoUpdater {
|
|
|
139
130
|
return sha256(hashes.join('\n'))
|
|
140
131
|
}
|
|
141
132
|
|
|
142
|
-
*
|
|
143
133
|
async restore(id) {
|
|
144
134
|
const record = this.history.find((h) => h.id === id)
|
|
145
135
|
if (!record) {
|
|
@@ -160,7 +150,6 @@ export class ProtoUpdater {
|
|
|
160
150
|
await this.restore(last.id)
|
|
161
151
|
}
|
|
162
152
|
|
|
163
|
-
*
|
|
164
153
|
async validate() {
|
|
165
154
|
const files = await this.#listJsFiles(this.protoPath)
|
|
166
155
|
const errors = []
|
|
@@ -176,7 +165,6 @@ export class ProtoUpdater {
|
|
|
176
165
|
return { ok: errors.length === 0, errors }
|
|
177
166
|
}
|
|
178
167
|
|
|
179
|
-
*
|
|
180
168
|
async update(opts = {}) {
|
|
181
169
|
const autoRollback = opts.autoRollback ?? true
|
|
182
170
|
const dryRun = opts.dryRun ?? false
|