@isaxn/bailyes 1.0.0 → 2.1.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.
@@ -0,0 +1,457 @@
1
+ "use strict";
2
+
3
+ const crypto = require("crypto");
4
+ const { BaseBuilder } = require("./base-builder.js");
5
+ const { Toolkit } = require("./toolkit.js");
6
+ const { extractIE, waitAllPromises } = require("./inline-entities.js");
7
+ const { tokenize } = require("./tokenizer.js");
8
+ const { toTableMetadata } = require("./table.js");
9
+
10
+ function newLayout(name, data, extra = {}) {
11
+ return {
12
+ ...extra,
13
+ view_model: {
14
+ [Array.isArray(data) ? "primitives" : "primitive"]: data,
15
+ __typename: `GenAI${name}LayoutViewModel`
16
+ }
17
+ };
18
+ }
19
+
20
+ class AIRich extends BaseBuilder {
21
+ #client;
22
+
23
+ constructor(client) {
24
+ if (!client) throw new Error("Socket is required");
25
+
26
+ super();
27
+ this.#client = client;
28
+ this._submessages = [];
29
+ this._sections = [];
30
+ this._richResponseSources = [];
31
+ }
32
+
33
+ addSubmessage(submessage) {
34
+ const items = Array.isArray(submessage) ? submessage : [submessage];
35
+
36
+ for (const item of items) {
37
+ if (typeof item !== "object" || item === null || Array.isArray(item)) {
38
+ throw new TypeError("Submessage must be a plain object or array of plain objects");
39
+ }
40
+ this._submessages.push(item);
41
+ }
42
+
43
+ return this;
44
+ }
45
+
46
+ addSection(section) {
47
+ const items = Array.isArray(section) ? section : [section];
48
+
49
+ for (const item of items) {
50
+ if (typeof item !== "object" || item === null || Array.isArray(item)) {
51
+ throw new TypeError("Section must be a plain object or array of plain objects");
52
+ }
53
+ this._sections.push(item);
54
+ }
55
+
56
+ return this;
57
+ }
58
+
59
+ addText(text, { hyperlink = true, citation = true, latex = true } = {}) {
60
+ if (typeof text !== "string") throw new TypeError("Text must be a string");
61
+
62
+ const { text: extractedText, inline_entities } = extractIE(text, { hyperlink, citation, latex });
63
+
64
+ this._submessages.push({ messageType: 2, messageText: extractedText });
65
+
66
+ this._sections.push(
67
+ newLayout("Single", {
68
+ text: extractedText,
69
+ ...(inline_entities.length && { inline_entities }),
70
+ __typename: "GenAIMarkdownTextUXPrimitive"
71
+ })
72
+ );
73
+
74
+ return this;
75
+ }
76
+
77
+ addCode(language, code) {
78
+ if (typeof language !== "string" || typeof code !== "string") {
79
+ throw new TypeError("Language and code must be a string");
80
+ }
81
+
82
+ const meta = tokenize(code, language);
83
+
84
+ this._submessages.push({
85
+ messageType: 5,
86
+ codeMetadata: { codeLanguage: language, codeBlocks: meta.codeBlock }
87
+ });
88
+
89
+ this._sections.push(
90
+ newLayout("Single", {
91
+ language,
92
+ code_blocks: meta.unified_codeBlock,
93
+ __typename: "GenAICodeUXPrimitive"
94
+ })
95
+ );
96
+
97
+ return this;
98
+ }
99
+
100
+ addTable(table, { hyperlink = true, citation = true, latex = true } = {}) {
101
+ if (!Array.isArray(table)) throw new TypeError("Table must be an array");
102
+
103
+ const meta = toTableMetadata(table, { hyperlink, citation, latex });
104
+
105
+ this._submessages.push({
106
+ messageType: 4,
107
+ tableMetadata: { title: meta.title, rows: meta.rows }
108
+ });
109
+
110
+ this._sections.push(
111
+ newLayout("Single", { rows: meta.unified_rows, __typename: "GenATableUXPrimitive" })
112
+ );
113
+
114
+ return this;
115
+ }
116
+
117
+ addSource(sources = []) {
118
+ const isStringArray = Array.isArray(sources) && sources.every((item) => typeof item === "string");
119
+ const isNestedArray =
120
+ Array.isArray(sources) && sources.every((item) => Array.isArray(item) && item.every((v) => typeof v === "string"));
121
+
122
+ if (!(isStringArray || isNestedArray)) {
123
+ throw new TypeError("Sources must be a string array or an array of string arrays");
124
+ }
125
+
126
+ const normalized = isStringArray ? [sources] : sources;
127
+
128
+ const source = normalized.map(([icon, url, text]) => ({
129
+ source_type: "THIRD_PARTY",
130
+ source_display_name: text ?? "",
131
+ source_subtitle: "AI",
132
+ source_url: url ?? "",
133
+ favicon: {
134
+ url: Toolkit.resolveMedia(this.#client, icon ?? "", "image"),
135
+ mime_type: "image/jpeg",
136
+ width: 16,
137
+ height: 16
138
+ }
139
+ }));
140
+
141
+ this._sections.push(newLayout("Single", { sources: source, __typename: "GenAISearchResultPrimitive" }));
142
+
143
+ return this;
144
+ }
145
+
146
+ addReels(reelsItems = []) {
147
+ const isSingleObject = reelsItems && typeof reelsItems === "object" && !Array.isArray(reelsItems);
148
+ const isObjectArray =
149
+ Array.isArray(reelsItems) && reelsItems.every((item) => item && typeof item === "object" && !Array.isArray(item));
150
+
151
+ if (!(isSingleObject || isObjectArray)) {
152
+ throw new TypeError("Reels items must be an object or an array of objects");
153
+ }
154
+
155
+ const items = Array.isArray(reelsItems) ? reelsItems : [reelsItems];
156
+
157
+ const reels = items.map((item) => ({
158
+ ...item,
159
+ _avatar: Toolkit.resolveMedia(this.#client, item.profileIconUrl ?? item.profile_url ?? item.profile ?? "", "image"),
160
+ _thumbnail: Toolkit.resolveMedia(this.#client, item.thumbnailUrl ?? item.thumbnail ?? "", "image")
161
+ }));
162
+
163
+ this._submessages.push({
164
+ messageType: 9,
165
+ contentItemsMetadata: {
166
+ contentType: 1,
167
+ itemsMetadata: reels.map((item) => ({
168
+ reelItem: {
169
+ title: item.username ?? "",
170
+ profileIconUrl: item._avatar,
171
+ thumbnailUrl: item._thumbnail,
172
+ videoUrl: item.videoUrl ?? item.url ?? ""
173
+ }
174
+ }))
175
+ }
176
+ });
177
+
178
+ reels.forEach((item, idx) => {
179
+ this._richResponseSources.push({
180
+ provider: "NIXEL",
181
+ thumbnailCDNURL: item._thumbnail,
182
+ sourceProviderURL: item.videoUrl ?? item.url ?? "",
183
+ sourceQuery: "",
184
+ faviconCDNURL: item._avatar,
185
+ citationNumber: idx + 1,
186
+ sourceTitle: item.username ?? ""
187
+ });
188
+ });
189
+
190
+ this._sections.push(
191
+ newLayout(
192
+ "HScroll",
193
+ reels.map((item) => ({
194
+ reels_url: item.videoUrl ?? item.url ?? "",
195
+ thumbnail_url: item._thumbnail,
196
+ creator: item.username ?? item.title ?? "",
197
+ avatar_url: item._avatar,
198
+ reels_title: item.reels_title ?? item.title ?? "",
199
+ likes_count: item.likes_count ?? item.like ?? 0,
200
+ shares_count: item.shares_count ?? item.share ?? 0,
201
+ view_count: item.view_count ?? item.view ?? 0,
202
+ reel_source: item.reel_source ?? item.source ?? "IG",
203
+ is_verified: Boolean(item.is_verified || item.verified),
204
+ __typename: "GenAIReelPrimitive"
205
+ }))
206
+ )
207
+ );
208
+
209
+ return this;
210
+ }
211
+
212
+ addImage(imageUrl, { resolveUrl = false } = {}) {
213
+ const isValid =
214
+ typeof imageUrl === "string" ||
215
+ Buffer.isBuffer(imageUrl) ||
216
+ (Array.isArray(imageUrl) && imageUrl.every((v) => typeof v === "string" || Buffer.isBuffer(v)));
217
+
218
+ if (!isValid) {
219
+ throw new TypeError("imageUrl must be string | buffer | array of string/buffer");
220
+ }
221
+
222
+ const items = Array.isArray(imageUrl) ? imageUrl : [imageUrl];
223
+
224
+ const list = items.map((v) => {
225
+ const url = Toolkit.resolveMedia(this.#client, v, "image", { resolveUrl });
226
+ return { imagePreviewUrl: url, imageHighResUrl: url, sourceUrl: url };
227
+ });
228
+
229
+ this._submessages.push({
230
+ messageType: 1,
231
+ gridImageMetadata: {
232
+ gridImageUrl: { imagePreviewUrl: list[0]?.imagePreviewUrl },
233
+ imageUrls: list
234
+ }
235
+ });
236
+
237
+ list.forEach(({ imagePreviewUrl }) => {
238
+ this._sections.push(
239
+ newLayout("Single", {
240
+ media: { url: imagePreviewUrl, mime_type: "image/png" },
241
+ imagine_type: "IMAGE",
242
+ status: { status: "READY" },
243
+ __typename: "GenAIImaginePrimitive"
244
+ })
245
+ );
246
+ });
247
+
248
+ return this;
249
+ }
250
+
251
+ addVideo(videoUrl, { autoFill = true } = {}) {
252
+ const isObjectVideo = (v) => v && typeof v === "object" && v.url;
253
+
254
+ const isValid =
255
+ typeof videoUrl === "string" ||
256
+ Buffer.isBuffer(videoUrl) ||
257
+ isObjectVideo(videoUrl) ||
258
+ (Array.isArray(videoUrl) && videoUrl.every((v) => typeof v === "string" || Buffer.isBuffer(v) || isObjectVideo(v)));
259
+
260
+ if (!isValid) {
261
+ throw new TypeError("videoUrl must be string | buffer | object | array");
262
+ }
263
+
264
+ const items = Array.isArray(videoUrl) ? videoUrl : [videoUrl];
265
+
266
+ this._submessages.push({ messageType: 2, messageText: "[ CANNOT_LOAD_VIDEO - NIXEL ]" });
267
+
268
+ items.forEach((item) => {
269
+ const isObject = isObjectVideo(item);
270
+ const url = isObject ? Toolkit.resolveMedia(this.#client, item.url ?? "", "video") : Toolkit.resolveMedia(this.#client, item, "video");
271
+
272
+ const bufferPromise = autoFill ? Promise.resolve(url).then((u) => Toolkit.fetchBuffer(u)) : null;
273
+
274
+ const file_length =
275
+ isObject && item.file_length != null ? item.file_length : autoFill ? bufferPromise.then((b) => b?.length ?? 0) : 0;
276
+
277
+ const duration =
278
+ isObject && item.duration != null
279
+ ? item.duration
280
+ : autoFill
281
+ ? bufferPromise.then((b) => Toolkit.getMp4Duration(b, { silent: true }))
282
+ : 0;
283
+
284
+ const thumbnail =
285
+ isObject && item.thumbnail
286
+ ? Toolkit.resolveMedia(this.#client, item.thumbnail, "image", { result: "base64", resize: true, width: 300, height: 300 })
287
+ : autoFill && bufferPromise
288
+ ? bufferPromise.then((b) => Toolkit.getMp4Preview(b, { time: 0, result: "base64" }))
289
+ : null;
290
+
291
+ this._sections.push(
292
+ newLayout("Single", {
293
+ media: { url, mime_type: isObject ? item.mime_type ?? "video/mp4" : "video/mp4", file_length, duration },
294
+ imagine_type: "ANIMATE",
295
+ status: { status: "READY" },
296
+ thumbnail: { raw_media: thumbnail },
297
+ __typename: "GenAIImaginePrimitive"
298
+ })
299
+ );
300
+ });
301
+
302
+ return this;
303
+ }
304
+
305
+ addProduct(data = {}) {
306
+ const isSingleObject = data && typeof data === "object" && !Array.isArray(data);
307
+ const isObjectArray = Array.isArray(data) && data.every((item) => item && typeof item === "object" && !Array.isArray(item));
308
+
309
+ if (!(isSingleObject || isObjectArray)) {
310
+ throw new TypeError("Product items must be an object or an array of objects");
311
+ }
312
+
313
+ this._submessages.push({ messageType: 2, messageText: "[ CANNOT_LOAD_PRODUCT - NIXEL ]" });
314
+
315
+ const items = Array.isArray(data) ? data : [data];
316
+
317
+ const product = items.map((item) => ({
318
+ title: item.title,
319
+ brand: item.brand,
320
+ price: item.price,
321
+ sale_price: item.sale_price,
322
+ product_url: item.product_url ?? item.url,
323
+ image: { url: Toolkit.resolveMedia(this.#client, item.image_url ?? item.image, "image") },
324
+ additional_images: [{ url: Toolkit.resolveMedia(this.#client, item.icon_url ?? item.icon, "image") }],
325
+ __typename: "GenAIProductItemCardPrimitive"
326
+ }));
327
+
328
+ this._sections.push(newLayout(Array.isArray(data) ? "HScroll" : "Single", Array.isArray(data) ? product : product[0]));
329
+
330
+ return this;
331
+ }
332
+
333
+ addPost(data = {}) {
334
+ const isSingleObject = data && typeof data === "object" && !Array.isArray(data);
335
+ const isObjectArray = Array.isArray(data) && data.every((item) => item && typeof item === "object" && !Array.isArray(item));
336
+
337
+ if (!(isSingleObject || isObjectArray)) {
338
+ throw new TypeError("Post items must be an object or an array of objects");
339
+ }
340
+
341
+ const posts = Array.isArray(data) ? data : [data];
342
+
343
+ this._submessages.push({ messageType: 2, messageText: "[ CANNOT_LOAD_POST - NIXEL ]" });
344
+
345
+ const primitives = posts.map((p) => ({
346
+ title: p.title ?? "",
347
+ subtitle: p.subtitle ?? "",
348
+ username: p.username ?? "",
349
+ profile_picture_url: Toolkit.resolveMedia(this.#client, p.profile_picture_url ?? p.profile_url ?? p.profile ?? "", "image"),
350
+ is_verified: Boolean(p.is_verified || p.verified),
351
+ thumbnail_url: Toolkit.resolveMedia(this.#client, p.thumbnail_url ?? p.thumbnail ?? "", "image"),
352
+ post_caption: p.post_caption ?? p.caption ?? "",
353
+ likes_count: p.likes_count ?? p.like ?? 0,
354
+ comments_count: p.comments_count ?? p.comment ?? 0,
355
+ shares_count: p.shares_count ?? p.share ?? 0,
356
+ post_url: p.post_url ?? p.url ?? "",
357
+ post_deeplink: p.post_deeplink ?? p.deeplink ?? "",
358
+ source_app: p.source_app || p.source || "INSTAGRAM",
359
+ footer_label: p.footer_label ?? p.footer ?? "",
360
+ footer_icon: Toolkit.resolveMedia(this.#client, p.footer_icon ?? p.icon ?? "", "image"),
361
+ is_carousel: posts.length > 1,
362
+ orientation: p.orientation ?? "LANDSCAPE",
363
+ post_type: p.post_type ?? "VIDEO",
364
+ __typename: "GenAIPostPrimitive"
365
+ }));
366
+
367
+ this._sections.push(newLayout("HScroll", primitives));
368
+
369
+ return this;
370
+ }
371
+
372
+ addTip(text) {
373
+ this._submessages.push({ messageType: 2, messageText: text });
374
+ this._sections.push(newLayout("Single", { text, __typename: "GenAIMetadataTextPrimitive" }));
375
+ return this;
376
+ }
377
+
378
+ addSuggest(suggestion, { scroll = true, layout } = {}) {
379
+ const isValid =
380
+ typeof suggestion === "string" || (Array.isArray(suggestion) && suggestion.every((v) => typeof v === "string"));
381
+
382
+ if (!isValid) throw new TypeError("Suggestion must be a string or array of strings");
383
+
384
+ const suggest = Array.isArray(suggestion)
385
+ ? suggestion.map((text) => ({ prompt_text: text, prompt_type: "SUGGESTED_PROMPT", __typename: "GenAIFollowUpSuggestionPillPrimitive" }))
386
+ : [{ prompt_text: suggestion, prompt_type: "SUGGESTED_PROMPT", __typename: "GenAIFollowUpSuggestionPillPrimitive" }];
387
+
388
+ const type = layout ?? (suggest.length === 1 ? "Single" : scroll ? "HScroll" : "ActionRow");
389
+
390
+ this._sections.push(newLayout(type, type === "Single" ? suggest[0] : suggest, { __typename: "GenAIUnifiedResponseSection" }));
391
+
392
+ return this;
393
+ }
394
+
395
+ async build({ forwarded = true, notification = false, includesUnifiedResponse = true, includesSubmessages = true, quoted, quotedParticipant, ...options } = {}) {
396
+ const forward = forwarded
397
+ ? { forwardingScore: 1, isForwarded: true, forwardedAiBotMessageInfo: { botJid: "0@bot" }, forwardOrigin: 4 }
398
+ : {};
399
+
400
+ const notif = notification
401
+ ? {
402
+ sessionTransparencyMetadata: {
403
+ disclaimerText: this._footer || "AI generated response",
404
+ hcaId: `hca_${Date.now()}`,
405
+ sessionTransparencyType: 1
406
+ }
407
+ }
408
+ : {};
409
+
410
+ const qObj = quoted
411
+ ? {
412
+ stanzaId: quoted?.key?.id || quoted?.id,
413
+ participant: quotedParticipant || quoted?.key?.participant || quoted?.key?.remoteJid,
414
+ quotedType: 0,
415
+ quotedMessage: typeof quoted === "object" && quoted !== null ? quoted.message ?? quoted : undefined
416
+ }
417
+ : {};
418
+
419
+ const sections = this._footer
420
+ ? [...(await waitAllPromises(this._sections)), newLayout("Single", { text: this._footer, __typename: "GenAIMetadataTextPrimitive" })]
421
+ : [...(await waitAllPromises(this._sections))];
422
+
423
+ return {
424
+ messageContextInfo: {
425
+ deviceListMetadata: {},
426
+ deviceListMetadataVersion: 2,
427
+ botMetadata: {
428
+ messageDisclaimerText: this._title,
429
+ richResponseSourcesMetadata: { sources: this._richResponseSources },
430
+ ...notif
431
+ }
432
+ },
433
+ ...this._extraPayload,
434
+ botForwardedMessage: {
435
+ message: {
436
+ richResponseMessage: {
437
+ messageType: 1,
438
+ submessages: includesSubmessages ? await waitAllPromises(this._submessages) : [],
439
+ unifiedResponse: {
440
+ data: includesUnifiedResponse
441
+ ? Buffer.from(JSON.stringify({ response_id: crypto.randomUUID(), sections })).toString("base64")
442
+ : ""
443
+ },
444
+ contextInfo: { ...forward, ...qObj, ...this._contextInfo }
445
+ }
446
+ }
447
+ }
448
+ };
449
+ }
450
+
451
+ async send(jid, { forwarded, notification, includesUnifiedResponse, includesSubmessages, ...options } = {}) {
452
+ const msg = await this.build({ forwarded, notification, includesUnifiedResponse, includesSubmessages, ...options });
453
+ return this.#client.relayMessage(jid, msg, { ...options });
454
+ }
455
+ }
456
+
457
+ module.exports = { AIRich };
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+
3
+ class BaseBuilder {
4
+ constructor() {
5
+ this._title = "";
6
+ this._subtitle = "";
7
+ this._body = "";
8
+ this._footer = "";
9
+ this._contextInfo = {};
10
+ this._extraPayload = {};
11
+ }
12
+
13
+ setTitle(title) {
14
+ if (typeof title !== "string") throw new TypeError("Title must be a string");
15
+ this._title = title;
16
+ return this;
17
+ }
18
+
19
+ setSubtitle(subtitle) {
20
+ if (typeof subtitle !== "string") throw new TypeError("Subtitle must be a string");
21
+ this._subtitle = subtitle;
22
+ return this;
23
+ }
24
+
25
+ setBody(body) {
26
+ if (typeof body !== "string") throw new TypeError("Body must be a string");
27
+ this._body = body;
28
+ return this;
29
+ }
30
+
31
+ setFooter(footer) {
32
+ if (typeof footer !== "string") throw new TypeError("Footer must be a string");
33
+ this._footer = footer;
34
+ return this;
35
+ }
36
+
37
+ setContextInfo(obj) {
38
+ if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
39
+ throw new TypeError("ContextInfo must be a plain object");
40
+ }
41
+ this._contextInfo = obj;
42
+ return this;
43
+ }
44
+
45
+ addPayload(obj) {
46
+ if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
47
+ throw new TypeError("Payload must be a plain object");
48
+ }
49
+ Object.assign(this._extraPayload, obj);
50
+ return this;
51
+ }
52
+ }
53
+
54
+ module.exports = { BaseBuilder };
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+
3
+ const crypto = require("crypto");
4
+ const { getBaileys } = require("../core/baileys-loader.js");
5
+ const { BaseBuilder } = require("./base-builder.js");
6
+ const { Toolkit } = require("./toolkit.js");
7
+ const { relayInteractive } = require("./native-flow.js");
8
+
9
+ class ButtonV2 extends BaseBuilder {
10
+ #client;
11
+
12
+ constructor(client) {
13
+ super();
14
+ if (!client) throw new Error("Socket is required");
15
+
16
+ this.#client = client;
17
+ this._image = undefined;
18
+ this._data = undefined;
19
+ this._buttons = [];
20
+ }
21
+
22
+ addButton(displayText = "", buttonId) {
23
+ this._buttons.push({
24
+ buttonId: buttonId ?? crypto.randomUUID(),
25
+ buttonText: { displayText },
26
+ type: 1
27
+ });
28
+ return this;
29
+ }
30
+
31
+ addRawButton(obj) {
32
+ if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
33
+ throw new TypeError("Buttons must be a plain object");
34
+ }
35
+ this._buttons.push(obj);
36
+ return this;
37
+ }
38
+
39
+ setThumbnail(mediaPath) {
40
+ if (!mediaPath) throw new Error("Url or buffer needed");
41
+ this._image = mediaPath;
42
+ return this;
43
+ }
44
+
45
+ setMedia(obj) {
46
+ if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
47
+ throw new TypeError("Media must be a plain object");
48
+ }
49
+ this._data = obj;
50
+ return this;
51
+ }
52
+
53
+ async build(jid, options = {}) {
54
+ const { generateWAMessageFromContent } = await getBaileys();
55
+
56
+ const thumbnail = this._image
57
+ ? await Toolkit.resize(
58
+ Buffer.isBuffer(this._image) ? this._image : await Toolkit.fetchBuffer(this._image, {}, { silent: true }),
59
+ 300,
60
+ 300
61
+ )
62
+ : null;
63
+
64
+ const header = this._data
65
+ ? this._data
66
+ : {
67
+ headerType: 6,
68
+ locationMessage: {
69
+ degreesLatitude: 0,
70
+ degreesLongitude: 0,
71
+ name: this._title,
72
+ address: this._subtitle,
73
+ jpegThumbnail: thumbnail
74
+ }
75
+ };
76
+
77
+ return generateWAMessageFromContent(
78
+ jid,
79
+ {
80
+ ...this._extraPayload,
81
+ buttonsMessage: {
82
+ contentText: this._body,
83
+ footerText: this._footer,
84
+ ...header,
85
+ viewOnce: true,
86
+ contextInfo: this._contextInfo,
87
+ buttons: [...this._buttons]
88
+ }
89
+ },
90
+ { ...options }
91
+ );
92
+ }
93
+
94
+ async send(jid, options = {}) {
95
+ if (this._buttons.length < 1) {
96
+ throw new Error("ButtonV2 requires at least one button");
97
+ }
98
+
99
+ const msg = await this.build(jid, options);
100
+ return relayInteractive(this.#client, msg, options);
101
+ }
102
+ }
103
+
104
+ module.exports = { ButtonV2 };