@codexverified/baileys 2.14.14 β 2.17.18
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 +277 -8
- package/lib/Socket/messages-send.js +303 -3
- package/lib/Socket/newsletter.js +19 -0
- package/lib/Utils/bot-capabilities.js +84 -0
- package/lib/Utils/games/miniapp.js +5 -0
- package/lib/Utils/index.js +4 -0
- package/lib/Utils/rich-message-utils.js +506 -1
- package/lib/Utils/source-grep.js +46 -0
- package/lib/index.d.ts +124 -1
- package/package.json +1 -1
|
@@ -12,12 +12,14 @@ import { areJidsSameUser, getBinaryNodeChild, getBinaryNodeChildren, getBizBinar
|
|
|
12
12
|
import { USyncQuery, USyncUser } from '../WAUSync/index.js';
|
|
13
13
|
import { captureUnifiedResponse, generateButtonGridContent, generateCodeBlockContent, generateLatexContent, generateListContent, generateRichMenuContent, generateTableContent, generateUnifiedResponseContent } from '../Utils/message-composer.js';
|
|
14
14
|
import { Baron } from './interactive-handler.js';
|
|
15
|
-
import { prepareRichResponseMessage, prepareRichGenerationMessage, prepareRichTextMessage, prepareRichImageMessage, prepareRichLinkMessage, prepareHtmlMessage, prepareRichHtmlMessage } from '../Utils/rich-message-utils.js';
|
|
15
|
+
import { prepareRichResponseMessage, prepareRichGenerationMessage, prepareRichTextMessage, prepareRichImageMessage, prepareRichLinkMessage, prepareA2UICommandMenu, prepareAIRichCommandMenu, prepareVideoGridMessage, prepareVerticalAlbumMessage, prepareHtmlMessage, prepareRichHtmlMessage, prepareA2UIMessage, prepareBotPromptSuggestionsMessage, prepareBotPttTranscriptMessage, prepareBotSourcesMessage, prepareInstagramProfileMessage, prepareRichDynamicMediaMessage, prepareRichMapMessage, prepareSocialEntityMessage } from '../Utils/rich-message-utils.js';
|
|
16
16
|
import { generateSlotMachineHtml } from '../Utils/games/slot-machine.js';
|
|
17
|
+
import { generateMiniAppHtml } from '../Utils/games/miniapp.js';
|
|
17
18
|
import { generateWebsitePreviewHtml, generateLinkPreviewHtml } from '../Utils/website-preview.js';
|
|
18
19
|
import { isViewOnceMessage, downloadViewOnceMedia, createRegularMessageFromViewOnce, parseViewOnceInfo } from '../Utils/view-once-utils.js';
|
|
19
20
|
import { collectMetaAIBotParticipantJids, isMetaAIBotResponse, META_AI_BOT_JID } from './aigroups.js';
|
|
20
21
|
import { makeUsernameSocket } from './username.js';
|
|
22
|
+
import { grepSource } from '../Utils/source-grep.js';
|
|
21
23
|
|
|
22
24
|
const STATUS_JID = 'status@broadcast';
|
|
23
25
|
export const normalizeStatusJidList = (value) => {
|
|
@@ -1242,11 +1244,38 @@ export const makeMessagesSocket = (config) => {
|
|
|
1242
1244
|
if (!content.copyText) throw new Boom('sendQuoteCopy requires copyText content', { statusCode: 400 });
|
|
1243
1245
|
return socket.sendRichTextCopy(jid, content, options);
|
|
1244
1246
|
},
|
|
1247
|
+
/** Send a full-width stacked AIRich album with one large image/video per row. */
|
|
1245
1248
|
sendAlbumMessage: async (jid, medias = [], options = {}) => {
|
|
1246
1249
|
if (!jid) throw new Boom('sendAlbumMessage requires a target JID', { statusCode: 400 });
|
|
1247
1250
|
if (!Array.isArray(medias) || medias.length < 2) throw new Boom('sendAlbumMessage requires at least two media items', { statusCode: 400 });
|
|
1248
|
-
const
|
|
1249
|
-
|
|
1251
|
+
const albumItems = medias.map((media) => {
|
|
1252
|
+
const raw = media?.type && media?.data ? media.data : (media?.image ?? media);
|
|
1253
|
+
if (media?.video || media?.type === 'video') {
|
|
1254
|
+
const video = media?.video || raw;
|
|
1255
|
+
const sourceUrl = typeof video === 'string' ? video.trim() : String(video?.url || video?.videoUrl || '').trim();
|
|
1256
|
+
if (!sourceUrl) throw new Boom('Each video album item requires a video URL', { statusCode: 400 });
|
|
1257
|
+
return { type: 'video', video: { ...video, url: sourceUrl } };
|
|
1258
|
+
}
|
|
1259
|
+
const image = typeof raw === 'string' ? raw.trim() : { ...raw, url: String(raw?.url || '').trim() };
|
|
1260
|
+
if (!image || (typeof image === 'object' && !image.url && !image.sourceUrl)) {
|
|
1261
|
+
throw new Boom('Each image album item requires an image URL', { statusCode: 400 });
|
|
1262
|
+
}
|
|
1263
|
+
return { type: 'image', image };
|
|
1264
|
+
});
|
|
1265
|
+
const wrapped = prepareVerticalAlbumMessage({ items: albumItems, verificationMetadata: options.verificationMetadata });
|
|
1266
|
+
const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
|
|
1267
|
+
logger,
|
|
1268
|
+
userJid: authState.creds.me?.id,
|
|
1269
|
+
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id || jid),
|
|
1270
|
+
...options
|
|
1271
|
+
});
|
|
1272
|
+
await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, ...options });
|
|
1273
|
+
if (config.emitOwnEvents) {
|
|
1274
|
+
process.nextTick(async () => {
|
|
1275
|
+
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
return fullMsg;
|
|
1250
1279
|
},
|
|
1251
1280
|
sendBotDiagnostics: async (jid, content = {}, options = {}) => {
|
|
1252
1281
|
if (!jid) throw new Boom('sendBotDiagnostics requires a target JID', { statusCode: 400 });
|
|
@@ -1258,6 +1287,29 @@ export const makeMessagesSocket = (config) => {
|
|
|
1258
1287
|
const { text = 'Bot command test', commandName = '', commandDescription = '', commandPrompt = '', botJid = '867051314767696@bot' } = content;
|
|
1259
1288
|
return socket.sendRichGeneration(jid, { text, botMetadata: { commandMetadata: { commandName, commandDescription, commandPrompt } }, botJid }, undefined, options);
|
|
1260
1289
|
},
|
|
1290
|
+
/** Sends a planning/execution-plan rich response with WAProto capabilities. */
|
|
1291
|
+
sendBotPlanning: async (jid, content = {}, options = {}) => {
|
|
1292
|
+
if (!jid) throw new Boom('sendBotPlanning requires a target JID', { statusCode: 400 });
|
|
1293
|
+
const { text = 'Execution plan', steps = [], queryPlan = true, botJid = '867051314767696@bot' } = content;
|
|
1294
|
+
const submessages = [
|
|
1295
|
+
{ messageType: proto.AIRichResponseSubMessageType.AI_RICH_RESPONSE_TEXT, messageText: text },
|
|
1296
|
+
...steps.map(step => ({
|
|
1297
|
+
messageType: proto.AIRichResponseSubMessageType.AI_RICH_RESPONSE_TEXT,
|
|
1298
|
+
messageText: typeof step === 'string' ? step : `${step.title || step.name || 'Step'}${step.body ? `: ${step.body}` : ''}`
|
|
1299
|
+
}))
|
|
1300
|
+
];
|
|
1301
|
+
const wrapped = buildBotRichResponse({
|
|
1302
|
+
botJid,
|
|
1303
|
+
submessages,
|
|
1304
|
+
capabilities: [
|
|
1305
|
+
BotCapabilityType.AGENTIC_PLANNING,
|
|
1306
|
+
...(queryPlan ? [BotCapabilityType.QUERY_PLAN] : [])
|
|
1307
|
+
]
|
|
1308
|
+
});
|
|
1309
|
+
const fullMsg = await generateWAMessageFromContent(jid, wrapped, { logger, userJid: authState.creds.me?.id, messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id), ...options });
|
|
1310
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
1311
|
+
return fullMsg;
|
|
1312
|
+
},
|
|
1261
1313
|
sendBotToolResult: async (jid, content = {}, options = {}) => {
|
|
1262
1314
|
if (!jid) throw new Boom('sendBotToolResult requires a target JID', { statusCode: 400 });
|
|
1263
1315
|
const { text = 'Bot tool result test', toolCallId = 'tool-' + Date.now(), resolutionData, resolutionDataSerialized, botJid = '867051314767696@bot' } = content;
|
|
@@ -1270,6 +1322,153 @@ export const makeMessagesSocket = (config) => {
|
|
|
1270
1322
|
await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, useCachedGroupMetadata: options.useCachedGroupMetadata, statusJidList: options.statusJidList });
|
|
1271
1323
|
return fullMsg;
|
|
1272
1324
|
},
|
|
1325
|
+
sendA2UIMessage: async (jid, content = {}, options = {}) => {
|
|
1326
|
+
if (!jid) throw new Boom('sendA2UIMessage requires a target JID', { statusCode: 400 });
|
|
1327
|
+
const { image, ...payload } = content || {};
|
|
1328
|
+
let imageMessage;
|
|
1329
|
+
if (image) {
|
|
1330
|
+
const prepared = await prepareWAMessageMedia({ image }, { upload: waUploadToServer });
|
|
1331
|
+
imageMessage = prepared.imageMessage;
|
|
1332
|
+
}
|
|
1333
|
+
const wrapped = prepareA2UIMessage({ ...payload, imageMessage });
|
|
1334
|
+
const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
|
|
1335
|
+
logger,
|
|
1336
|
+
userJid: authState.creds.me?.id,
|
|
1337
|
+
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id || jid),
|
|
1338
|
+
...options,
|
|
1339
|
+
});
|
|
1340
|
+
await relayRichMessage(jid, fullMsg, { ...options, forceRichEdit: true });
|
|
1341
|
+
if (config.emitOwnEvents) process.nextTick(async () => { await messageMutex.mutex(() => upsertMessage(fullMsg, 'append')); });
|
|
1342
|
+
return fullMsg;
|
|
1343
|
+
},
|
|
1344
|
+
sendBotPromptSuggestions: async (jid, content = {}, options = {}) => {
|
|
1345
|
+
if (!jid) throw new Boom('sendBotPromptSuggestions requires a target JID', { statusCode: 400 });
|
|
1346
|
+
const wrapped = prepareBotPromptSuggestionsMessage(content);
|
|
1347
|
+
const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
|
|
1348
|
+
logger,
|
|
1349
|
+
userJid: authState.creds.me?.id,
|
|
1350
|
+
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id),
|
|
1351
|
+
...options
|
|
1352
|
+
});
|
|
1353
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
1354
|
+
return fullMsg;
|
|
1355
|
+
},
|
|
1356
|
+
sendBotPttTranscript: async (jid, content = {}, options = {}) => {
|
|
1357
|
+
if (!jid) throw new Boom('sendBotPttTranscript requires a target JID', { statusCode: 400 });
|
|
1358
|
+
const wrapped = prepareBotPttTranscriptMessage(content);
|
|
1359
|
+
const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
|
|
1360
|
+
logger,
|
|
1361
|
+
userJid: authState.creds.me?.id,
|
|
1362
|
+
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id),
|
|
1363
|
+
...options
|
|
1364
|
+
});
|
|
1365
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
1366
|
+
return fullMsg;
|
|
1367
|
+
},
|
|
1368
|
+
sendBotSources: async (jid, content = {}, options = {}) => {
|
|
1369
|
+
if (!jid) throw new Boom('sendBotSources requires a target JID', { statusCode: 400 });
|
|
1370
|
+
const wrapped = prepareBotSourcesMessage(content);
|
|
1371
|
+
const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
|
|
1372
|
+
logger,
|
|
1373
|
+
userJid: authState.creds.me?.id,
|
|
1374
|
+
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id),
|
|
1375
|
+
...options
|
|
1376
|
+
});
|
|
1377
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
1378
|
+
return fullMsg;
|
|
1379
|
+
},
|
|
1380
|
+
sendInstagramProfile: async (jid, content = {}, options = {}) => {
|
|
1381
|
+
if (!jid) throw new Boom('sendInstagramProfile requires a target JID', { statusCode: 400 });
|
|
1382
|
+
const wrapped = prepareInstagramProfileMessage(content);
|
|
1383
|
+
const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
|
|
1384
|
+
logger,
|
|
1385
|
+
userJid: authState.creds.me?.id,
|
|
1386
|
+
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id || jid),
|
|
1387
|
+
...options
|
|
1388
|
+
});
|
|
1389
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
1390
|
+
if (config.emitOwnEvents) process.nextTick(async () => { await messageMutex.mutex(() => upsertMessage(fullMsg, 'append')); });
|
|
1391
|
+
return fullMsg;
|
|
1392
|
+
},
|
|
1393
|
+
sendRichDynamicMedia: async (jid, content = {}, options = {}) => {
|
|
1394
|
+
if (!jid) throw new Boom('sendRichDynamicMedia requires a target JID', { statusCode: 400 });
|
|
1395
|
+
const wrapped = prepareRichDynamicMediaMessage(content);
|
|
1396
|
+
const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
|
|
1397
|
+
logger,
|
|
1398
|
+
userJid: authState.creds.me?.id,
|
|
1399
|
+
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id),
|
|
1400
|
+
...options
|
|
1401
|
+
});
|
|
1402
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
1403
|
+
return fullMsg;
|
|
1404
|
+
},
|
|
1405
|
+
sendRichMap: async (jid, content = {}, options = {}) => {
|
|
1406
|
+
if (!jid) throw new Boom('sendRichMap requires a target JID', { statusCode: 400 });
|
|
1407
|
+
const wrapped = prepareRichMapMessage(content);
|
|
1408
|
+
const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
|
|
1409
|
+
logger,
|
|
1410
|
+
userJid: authState.creds.me?.id,
|
|
1411
|
+
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id),
|
|
1412
|
+
...options
|
|
1413
|
+
});
|
|
1414
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
1415
|
+
return fullMsg;
|
|
1416
|
+
},
|
|
1417
|
+
sendSocialEntity: async (jid, content = {}, options = {}) => {
|
|
1418
|
+
if (!jid) throw new Boom('sendSocialEntity requires a target JID', { statusCode: 400 });
|
|
1419
|
+
const wrapped = prepareSocialEntityMessage(content);
|
|
1420
|
+
const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
|
|
1421
|
+
logger,
|
|
1422
|
+
userJid: authState.creds.me?.id,
|
|
1423
|
+
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id || jid),
|
|
1424
|
+
...options,
|
|
1425
|
+
});
|
|
1426
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
1427
|
+
if (config.emitOwnEvents) process.nextTick(async () => { await messageMutex.mutex(() => upsertMessage(fullMsg, 'append')); });
|
|
1428
|
+
return fullMsg;
|
|
1429
|
+
},
|
|
1430
|
+
/** Send the large A2UI command menu with an uploaded image header, rows, and URL buttons. */
|
|
1431
|
+
/** Sends the manually serialized AIRich command card shown in the screenshot. */
|
|
1432
|
+
sendAIRichCommandMenu: async (jid, content = {}, options = {}) => {
|
|
1433
|
+
if (!jid) throw new Boom('sendAIRichCommandMenu requires a target JID', { statusCode: 400 });
|
|
1434
|
+
const wrapped = prepareAIRichCommandMenu(content);
|
|
1435
|
+
const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
|
|
1436
|
+
logger,
|
|
1437
|
+
userJid: authState.creds.me?.id,
|
|
1438
|
+
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id || jid),
|
|
1439
|
+
...options,
|
|
1440
|
+
});
|
|
1441
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
1442
|
+
if (config.emitOwnEvents) {
|
|
1443
|
+
process.nextTick(async () => {
|
|
1444
|
+
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
1445
|
+
});
|
|
1446
|
+
}
|
|
1447
|
+
return fullMsg;
|
|
1448
|
+
},
|
|
1449
|
+
sendA2UICommandMenu: async (jid, content = {}, options = {}) => {
|
|
1450
|
+
if (!jid) throw new Boom('sendA2UICommandMenu requires a target JID', { statusCode: 400 });
|
|
1451
|
+
const { image, ...menuContent } = content || {};
|
|
1452
|
+
let imageMessage;
|
|
1453
|
+
if (image) {
|
|
1454
|
+
const prepared = await prepareWAMessageMedia({ image }, { upload: waUploadToServer });
|
|
1455
|
+
imageMessage = prepared.imageMessage;
|
|
1456
|
+
}
|
|
1457
|
+
const wrapped = prepareA2UICommandMenu({ ...menuContent, imageMessage });
|
|
1458
|
+
const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
|
|
1459
|
+
logger,
|
|
1460
|
+
userJid: authState.creds.me?.id,
|
|
1461
|
+
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id || jid),
|
|
1462
|
+
...options
|
|
1463
|
+
});
|
|
1464
|
+
await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, ...options.relayOptions });
|
|
1465
|
+
if (config.emitOwnEvents) {
|
|
1466
|
+
process.nextTick(async () => {
|
|
1467
|
+
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
1468
|
+
});
|
|
1469
|
+
}
|
|
1470
|
+
return fullMsg;
|
|
1471
|
+
},
|
|
1273
1472
|
sendRichButtonGrid: async (jid, grid, options = {}) => {
|
|
1274
1473
|
const message = generateButtonGridContent(grid, options.quoted, options);
|
|
1275
1474
|
const fullMsg = await generateWAMessageFromContent(jid, message, { logger, userJid: authState.creds.me?.id, messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id), ...options });
|
|
@@ -1289,6 +1488,8 @@ export const makeMessagesSocket = (config) => {
|
|
|
1289
1488
|
const lines = [title, headers.length ? `| ${headers.map(String).join(' | ')} |` : '', headers.length ? `| ${headers.map(() => '---').join(' | ')} |` : '', ...rows.map(row => `| ${(Array.isArray(row) ? row : [row]).map(value => String(value ?? '')).join(' | ')} |`)].filter(Boolean);
|
|
1290
1489
|
return sock.sendMessage(jid, { ...(image ? { image, caption: lines.join('\\n') } : { text: lines.join('\\n') }), buttons, ...(footer ? { footer } : {}) }, options);
|
|
1291
1490
|
},
|
|
1491
|
+
/** Compatibility alias for the former Gen4/grid menu entry point. */
|
|
1492
|
+
HoldOn: async (jid, grid, options = {}) => socket.sendRichButtonGrid(jid, grid, options),
|
|
1292
1493
|
sendWhatsAppFlow: async (jid, flow = {}, options = {}) => {
|
|
1293
1494
|
const { text, footer, image, caption, flowId, flowToken, flowName = 'flow', flowAction = 'navigate', flowActionPayload = {} } = flow;
|
|
1294
1495
|
if (!flowId || !flowToken) throw new Boom('sendWhatsAppFlow requires flowId and flowToken', { statusCode: 400 });
|
|
@@ -1313,6 +1514,38 @@ export const makeMessagesSocket = (config) => {
|
|
|
1313
1514
|
await relayMessage(jid, message, { messageId });
|
|
1314
1515
|
return { message, messageId };
|
|
1315
1516
|
},
|
|
1517
|
+
sendCarousel: async (jid, cards, options = {}) => {
|
|
1518
|
+
if (!Array.isArray(cards) || cards.length === 0) {
|
|
1519
|
+
throw new Boom('sendCarousel requires a non-empty cards array', { statusCode: 400 });
|
|
1520
|
+
}
|
|
1521
|
+
const normalizedCards = cards.map(card => ({
|
|
1522
|
+
...card,
|
|
1523
|
+
text: card.text ?? card.body?.text,
|
|
1524
|
+
caption: card.caption ?? card.body?.text,
|
|
1525
|
+
footer: card.footer?.text ?? card.footer,
|
|
1526
|
+
buttons: card.buttons ?? card.nativeFlowMessage?.buttons,
|
|
1527
|
+
...(card.media?.image ? { image: card.media.image } : {}),
|
|
1528
|
+
...(card.media?.video ? { video: card.media.video } : {}),
|
|
1529
|
+
...(card.media?.document ? { document: card.media.document } : {})
|
|
1530
|
+
}));
|
|
1531
|
+
const { text, footer, ...messageOptions } = options;
|
|
1532
|
+
const fullMsg = await generateWAMessage(jid, {
|
|
1533
|
+
...messageOptions,
|
|
1534
|
+
cards: normalizedCards,
|
|
1535
|
+
...(text !== undefined ? { text } : {}),
|
|
1536
|
+
...(footer !== undefined ? { footer } : {})
|
|
1537
|
+
}, {
|
|
1538
|
+
logger,
|
|
1539
|
+
userJid: authState.creds.me?.id,
|
|
1540
|
+
upload: waUploadToServer,
|
|
1541
|
+
mediaCache: config.mediaCache,
|
|
1542
|
+
options: config.options,
|
|
1543
|
+
...options,
|
|
1544
|
+
messageId: options.messageId || generateMessageIDV2(jid)
|
|
1545
|
+
});
|
|
1546
|
+
await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, ...options });
|
|
1547
|
+
return fullMsg;
|
|
1548
|
+
},
|
|
1316
1549
|
sendLatex: async (jid, quoted, options) => {
|
|
1317
1550
|
const { message, messageId } = generateLatexContent(quoted, options);
|
|
1318
1551
|
await relayMessage(jid, message, { messageId });
|
|
@@ -1519,6 +1752,31 @@ export const makeMessagesSocket = (config) => {
|
|
|
1519
1752
|
await relayMessage(jid, message, { messageId });
|
|
1520
1753
|
return { message, messageId };
|
|
1521
1754
|
},
|
|
1755
|
+
/** Send one tolerant AIRich response containing every supported section supplied in content. */
|
|
1756
|
+
sendCodexMessage: async (jid, content = {}, quoted, options = {}) => {
|
|
1757
|
+
if (!jid) throw new Boom('sendCodexMessage requires a target JID', { statusCode: 400 });
|
|
1758
|
+
if (!content || typeof content !== 'object' || Array.isArray(content)) {
|
|
1759
|
+
throw new Boom('sendCodexMessage content must be an object', { statusCode: 400 });
|
|
1760
|
+
}
|
|
1761
|
+
const message = prepareRichResponseMessage({
|
|
1762
|
+
...content,
|
|
1763
|
+
noDonation: options.noDonation ?? true,
|
|
1764
|
+
});
|
|
1765
|
+
const fullMsg = generateWAMessageFromContent(jid, message, {
|
|
1766
|
+
logger,
|
|
1767
|
+
userJid: authState.creds.me?.id,
|
|
1768
|
+
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id || jid),
|
|
1769
|
+
quoted,
|
|
1770
|
+
...options,
|
|
1771
|
+
});
|
|
1772
|
+
await relayMessage(jid, fullMsg, options);
|
|
1773
|
+
if (config.emitOwnEvents) {
|
|
1774
|
+
process.nextTick(async () => {
|
|
1775
|
+
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
1776
|
+
});
|
|
1777
|
+
}
|
|
1778
|
+
return fullMsg;
|
|
1779
|
+
},
|
|
1522
1780
|
captureAndResendUnifiedResponse: async (jid, metaAiMsg, quoted) => {
|
|
1523
1781
|
const captured = captureUnifiedResponse(metaAiMsg);
|
|
1524
1782
|
if (!captured) {
|
|
@@ -2259,6 +2517,35 @@ export const makeMessagesSocket = (config) => {
|
|
|
2259
2517
|
return { message, messageId };
|
|
2260
2518
|
};
|
|
2261
2519
|
|
|
2520
|
+
/**
|
|
2521
|
+
* Sends a unified-response video grid. This uses GenAIVideoPrimitive items
|
|
2522
|
+
* inside GenAIGridLayoutViewModel; it is not the image-only GRID_IMAGE type.
|
|
2523
|
+
*/
|
|
2524
|
+
socket.sendVideoGridMessage = async (jid, videos = [], options = {}) => {
|
|
2525
|
+
if (!jid) throw new Boom('sendVideoGridMessage requires a target JID', { statusCode: 400 });
|
|
2526
|
+
if (!Array.isArray(videos) || videos.length < 2) {
|
|
2527
|
+
throw new Boom('sendVideoGridMessage requires at least two videos', { statusCode: 400 });
|
|
2528
|
+
}
|
|
2529
|
+
const wrapped = prepareVideoGridMessage({
|
|
2530
|
+
videos,
|
|
2531
|
+
verificationMetadata: options.verificationMetadata
|
|
2532
|
+
});
|
|
2533
|
+
const fullMsg = generateWAMessageFromContent(jid, wrapped, {
|
|
2534
|
+
logger,
|
|
2535
|
+
userJid: authState.creds.me?.id,
|
|
2536
|
+
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id || jid),
|
|
2537
|
+
...options
|
|
2538
|
+
});
|
|
2539
|
+
await relayRichMessage(jid, fullMsg, options);
|
|
2540
|
+
if (config.emitOwnEvents) {
|
|
2541
|
+
process.nextTick(async () => {
|
|
2542
|
+
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
2543
|
+
});
|
|
2544
|
+
}
|
|
2545
|
+
return fullMsg;
|
|
2546
|
+
};
|
|
2547
|
+
|
|
2548
|
+
|
|
2262
2549
|
socket.sendHtmlMessage = async (jid, content = {}, options = {}) => {
|
|
2263
2550
|
if (!jid) throw new Boom('sendHtmlMessage requires a target JID', { statusCode: 400 });
|
|
2264
2551
|
if (!content.html) throw new Boom('sendHtmlMessage requires html content', { statusCode: 400 });
|
|
@@ -2276,10 +2563,23 @@ export const makeMessagesSocket = (config) => {
|
|
|
2276
2563
|
await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, useCachedGroupMetadata: options.useCachedGroupMetadata, statusJidList: options.statusJidList });
|
|
2277
2564
|
return fullMsg;
|
|
2278
2565
|
};
|
|
2566
|
+
/** Find a word or expression in the installed package source. */
|
|
2567
|
+
socket.grepCode = (pattern, options = {}) => grepSource(pattern, options);
|
|
2568
|
+
|
|
2279
2569
|
socket.sendSlotMachine = async (jid, options = {}, sendOptions = {}) => {
|
|
2280
2570
|
return socket.sendHtmlMessage(jid, { html: generateSlotMachineHtml(options) }, sendOptions);
|
|
2281
2571
|
};
|
|
2282
2572
|
|
|
2573
|
+
/** Send the built-in interactive Codex mini-app through the rich HTML envelope. */
|
|
2574
|
+
socket.sendMiniApp = async (jid, options = {}, sendOptions = {}) => {
|
|
2575
|
+
if (!jid) throw new Boom('sendMiniApp requires a target JID', { statusCode: 400 });
|
|
2576
|
+
return socket.sendRichHtmlMessage(jid, {
|
|
2577
|
+
html: generateMiniAppHtml(options),
|
|
2578
|
+
title: options.title || 'mini-app',
|
|
2579
|
+
url: 'https://codex-ai.site',
|
|
2580
|
+
trustedSources: ['codex-ai.site']
|
|
2581
|
+
}, sendOptions);
|
|
2582
|
+
};
|
|
2283
2583
|
socket.sendWebsitePreview = async (jid, url, options = {}, sendOptions = {}) => {
|
|
2284
2584
|
if (!url) throw new Boom('sendWebsitePreview requires a URL', { statusCode: 400 });
|
|
2285
2585
|
return socket.sendHtmlMessage(jid, { html: await generateWebsitePreviewHtml(url, options) }, sendOptions);
|
package/lib/Socket/newsletter.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Boom } from '@hapi/boom';
|
|
1
2
|
import { proto } from '../../WAProto/index.js';
|
|
2
3
|
import { QueryIds, XWAPaths } from '../Types/index.js';
|
|
3
4
|
import { generateProfilePicture } from '../Utils/messages-media.js';
|
|
@@ -34,6 +35,15 @@ const parseNewsletterMetadata = (result) => {
|
|
|
34
35
|
}
|
|
35
36
|
return null;
|
|
36
37
|
};
|
|
38
|
+
const normalizeNewsletterInviteCode = (value) => {
|
|
39
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
40
|
+
throw new Boom('newsletterGetInviteInfo requires a non-empty invite code or URL', { statusCode: 400 });
|
|
41
|
+
}
|
|
42
|
+
return value.trim()
|
|
43
|
+
.replace(/^https?:\/\/(?:www\.)?whatsapp\.com\/channel\//i, '')
|
|
44
|
+
.replace(/^https?:\/\/wa\.me\/channel\//i, '')
|
|
45
|
+
.split(/[/?#]/, 1)[0];
|
|
46
|
+
};
|
|
37
47
|
export const makeNewsletterSocket = (config) => {
|
|
38
48
|
const sock = makeAIGroupsSocket(config);
|
|
39
49
|
const { query, generateMessageTag } = sock;
|
|
@@ -84,6 +94,14 @@ export const makeNewsletterSocket = (config) => {
|
|
|
84
94
|
const result = await executeWMexQuery(variables, QueryIds.METADATA, XWAPaths.xwa2_newsletter_metadata);
|
|
85
95
|
return parseNewsletterMetadata(result);
|
|
86
96
|
},
|
|
97
|
+
newsletterGetInviteInfo: async (inviteCodeOrUrl) => {
|
|
98
|
+
const inviteCode = normalizeNewsletterInviteCode(inviteCodeOrUrl);
|
|
99
|
+
return executeWMexQuery(
|
|
100
|
+
{ input: { key: inviteCode, type: 'INVITE' }, fetch_creation_time: true, fetch_full_image: true, fetch_viewer_metadata: true },
|
|
101
|
+
QueryIds.METADATA,
|
|
102
|
+
XWAPaths.xwa2_newsletter_metadata
|
|
103
|
+
).then(parseNewsletterMetadata);
|
|
104
|
+
},
|
|
87
105
|
newsletterFollow: (jid) => {
|
|
88
106
|
return executeWMexQuery({ newsletter_id: jid }, QueryIds.FOLLOW, XWAPaths.xwa2_newsletter_join_v2);
|
|
89
107
|
},
|
|
@@ -224,3 +242,4 @@ export const makeNewsletterSocket = (config) => {
|
|
|
224
242
|
}
|
|
225
243
|
};
|
|
226
244
|
};
|
|
245
|
+
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { proto } from '../../WAProto/index.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Numeric capability values exposed by WAProto.BotCapabilityMetadata.
|
|
5
|
+
* Keep this derived from the generated protobuf so callers cannot drift from
|
|
6
|
+
* the checked-in schema.
|
|
7
|
+
*/
|
|
8
|
+
export const BotCapabilityType = proto.BotCapabilityMetadata.BotCapabilityType;
|
|
9
|
+
|
|
10
|
+
const capabilityNames = new Set(
|
|
11
|
+
Object.keys(BotCapabilityType).filter(name => Number.isNaN(Number(name)))
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
/** Normalize enum names/numbers and reject unknown capability values. */
|
|
15
|
+
export const normalizeBotCapabilities = (capabilities = []) => {
|
|
16
|
+
if (!Array.isArray(capabilities)) {
|
|
17
|
+
throw new TypeError('capabilities must be an array');
|
|
18
|
+
}
|
|
19
|
+
const values = capabilities.map(capability => {
|
|
20
|
+
if (typeof capability === 'string') {
|
|
21
|
+
if (!capabilityNames.has(capability)) {
|
|
22
|
+
throw new TypeError(`Unknown bot capability: ${capability}`);
|
|
23
|
+
}
|
|
24
|
+
return BotCapabilityType[capability];
|
|
25
|
+
}
|
|
26
|
+
if (Number.isInteger(capability) && BotCapabilityType[capability] !== undefined) {
|
|
27
|
+
return capability;
|
|
28
|
+
}
|
|
29
|
+
throw new TypeError(`Invalid bot capability: ${String(capability)}`);
|
|
30
|
+
});
|
|
31
|
+
return [...new Set(values)];
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const buildBotCapabilityMetadata = (capabilities = []) => ({
|
|
35
|
+
capabilities: normalizeBotCapabilities(capabilities)
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Build the canonical wrapper used by WhatsApp's bot rich-response messages.
|
|
40
|
+
* The capability list is deliberately explicit; unknown or duplicate values
|
|
41
|
+
* are never put on the wire.
|
|
42
|
+
*/
|
|
43
|
+
export const buildBotRichResponse = ({
|
|
44
|
+
text,
|
|
45
|
+
submessages,
|
|
46
|
+
capabilities = [],
|
|
47
|
+
botMetadata = {},
|
|
48
|
+
botJid = '867051314767696@bot',
|
|
49
|
+
richResponse = {}
|
|
50
|
+
} = {}) => {
|
|
51
|
+
const resolvedSubmessages = submessages || (text == null ? [] : [{
|
|
52
|
+
messageType: proto.AIRichResponseSubMessageType.AI_RICH_RESPONSE_TEXT,
|
|
53
|
+
messageText: String(text)
|
|
54
|
+
}]);
|
|
55
|
+
return {
|
|
56
|
+
messageContextInfo: {
|
|
57
|
+
botMetadata: {
|
|
58
|
+
...botMetadata,
|
|
59
|
+
capabilityMetadata: buildBotCapabilityMetadata(capabilities)
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
botForwardedMessage: {
|
|
63
|
+
message: {
|
|
64
|
+
richResponseMessage: {
|
|
65
|
+
messageType: proto.AIRichResponseMessageType.AI_RICH_RESPONSE_TYPE_STANDARD,
|
|
66
|
+
submessages: resolvedSubmessages,
|
|
67
|
+
...richResponse,
|
|
68
|
+
contextInfo: {
|
|
69
|
+
isForwarded: true,
|
|
70
|
+
forwardingScore: 1,
|
|
71
|
+
forwardedAiBotMessageInfo: { botJid },
|
|
72
|
+
forwardOrigin: 4,
|
|
73
|
+
...(richResponse.contextInfo || {})
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const capability = (name) => {
|
|
82
|
+
if (!capabilityNames.has(name)) throw new TypeError(`Unknown bot capability: ${name}`);
|
|
83
|
+
return BotCapabilityType[name];
|
|
84
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Generate the attached interactive mini-app HTML game. */
|
|
2
|
+
export const generateMiniAppHtml = (options = {}) => {
|
|
3
|
+
const html = "<style>*{-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none}</style>\n<body style=\"margin:0;background:transparent;font-family:Arial,sans-serif;color:#eee;touch-action:manipulation;cursor:pointer\">\n<div style=\"width:100%;max-width:620px;margin:auto;padding:16px;box-sizing:border-box\">\n<div style=\"background:rgba(255,255,255,.06);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid rgba(255,255,255,.15);border-radius:16px;overflow:hidden;box-shadow:0 8px 32px rgba(0,0,0,.35)\">\n<div style=\"padding:18px 20px;border-bottom:1px solid rgba(255,255,255,.12);display:flex;justify-content:space-between;align-items:center\">\n<div><div style=\"font-size:11px;letter-spacing:1.5px;color:rgba(255,255,255,.45)\">πΆπππππβ¦</div><div style=\"font-size:21px;font-weight:bold;color:#fff\">mini-app</div></div>\n<div style=\"text-align:right\"><div id=\"score\" style=\"font-size:18px;font-weight:bold;color:#fff;text-shadow:0 0 10px rgba(108,92,231,.85);transition:transform .15s\">00000</div><div id=\"best\" style=\"font-size:10px;color:rgba(255,255,255,.4);margin-top:2px\">BEST 00000</div></div>\n</div>\n<div style=\"padding:18px\">\n<canvas id=\"game\" width=\"560\" height=\"190\" style=\"width:100%;height:auto;background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.12);border-radius:12px;display:block\"></canvas>\n<div id=\"status\" style=\"text-align:center;margin-top:10px;font-size:12px;color:rgba(255,255,255,.55)\">VelocitΓ 5.0x</div>\n</div></div></div>\n<script>\nconst c=document.getElementById('game'),x=c.getContext('2d'),scoreEl=document.getElementById('score'),bestEl=document.getElementById('best'),statusEl=document.getElementById('status');\nconst GY=170;\nlet d,o,clouds,particles,ambient,trail,score,best=0,speed,gameOver,last,shake,flash,runT,spawnTimer,milestone,squash;\nfunction loadBest(){\nlet vals=[];\ntry{let v=localStorage.getItem('dino_best');if(v)vals.push(parseInt(v,10))}catch(e){}\ntry{let v=sessionStorage.getItem('dino_best');if(v)vals.push(parseInt(v,10))}catch(e){}\ntry{let m=document.cookie.match(/(?:^|;\\s*)dino_best=(\\d+)/);if(m)vals.push(parseInt(m[1],10))}catch(e){}\nreturn vals.length?Math.max(...vals.filter(v=>!isNaN(v))):0\n}\nfunction saveBest(v){\nlet val=String(Math.floor(v));\ntry{localStorage.setItem('dino_best',val)}catch(e){}\ntry{sessionStorage.setItem('dino_best',val)}catch(e){}\ntry{document.cookie='dino_best='+val+';max-age=31536000;path=/'}catch(e){}\ntry{\nlet rq=indexedDB.open('dino_db',1);\nrq.onupgradeneeded=()=>{rq.result.createObjectStore('kv')};\nrq.onsuccess=()=>{try{rq.result.transaction('kv','readwrite').objectStore('kv').put(val,'dino_best')}catch(e){}}\n}catch(e){}\n}\nfunction loadBestAsync(cb){\ntry{\nlet rq=indexedDB.open('dino_db',1);\nrq.onupgradeneeded=()=>{rq.result.createObjectStore('kv')};\nrq.onsuccess=()=>{\ntry{\nlet gr=rq.result.transaction('kv','readonly').objectStore('kv').get('dino_best');\ngr.onsuccess=()=>{if(gr.result)cb(parseInt(gr.result,10))}\n}catch(e){}\n}\n}catch(e){}\n}\nbest=loadBest();\nloadBestAsync(v=>{if(!isNaN(v)&&v>best){best=v;bestEl.textContent='BEST '+String(Math.floor(best)).padStart(5,'0')}});\nfunction reset(){\nd={x:55,y:132,w:27,h:30,vy:0,jumping:false};\no=[];\nclouds=[{x:120,y:32,w:44,s:.35},{x:300,y:52,w:60,s:.22},{x:460,y:26,w:36,s:.4},{x:560,y:70,w:50,s:.18}];\nparticles=[];\ntrail=[];\nif(!ambient){ambient=[];for(let i=0;i<18;i++)ambient.push({x:Math.random()*c.width,y:Math.random()*c.height,r:.5+Math.random()*1.5,vx:.1+Math.random()*.3,ph:Math.random()*10})}\nscore=0;speed=5;gameOver=false;last=0;shake=0;flash=0;runT=0;milestone=0;squash=1;\nspawnTimer=70+Math.random()*30;\nbestEl.textContent='BEST '+String(Math.floor(best)).padStart(5,'0');\nstatusEl.textContent='Speed 5.0x'\n}\nfunction burst(px,py,n,col,spd){for(let i=0;i<n;i++)particles.push({x:px,y:py,vx:(Math.random()-.5)*spd,vy:-Math.random()*spd,life:1,col,size:2+Math.random()*2})}\nfunction jumpDino(){\nif(gameOver){reset();return}\nif(!d.jumping){d.jumping=true;d.vy=-13;squash=.7;burst(d.x+13,d.y+30,10,'255,255,255',4)}\n}\nfunction cactus(){\nlet h=24+Math.random()*24;\no.push({x:c.width+20,y:GY-h,w:16+Math.random()*6,h});\nif(Math.random()<.22){o.push({x:c.width+20+34+Math.random()*10,y:GY-(20+Math.random()*18),w:16,h:20+Math.random()*18})}\n}\nfunction hit(a,b){return a.x+4<b.x+b.w&&a.x+a.w-4>b.x&&a.y+4<b.y+b.h&&a.y+a.h>b.y}\nfunction drawTrail(){\ntrail.forEach((p,i)=>{x.fillStyle='rgba(108,92,231,'+(.25*(i/trail.length))+')';x.fillRect(p.x,p.y,27,30)})\n}\nfunction drawDino(){\nx.save();\nlet cx=d.x+13,cy=d.y+30;\nx.translate(cx,cy);\nx.scale(1/squash,squash);\nx.translate(-cx,-cy);\nlet legOff=d.jumping?0:Math.sin(runT*.5)*5;\nx.fillStyle='#eaeaea';\nx.fillRect(d.x,d.y,27,30);\nx.fillRect(d.x+22,d.y+5,13,18);\nx.fillStyle='#6c5ce7';\nx.fillRect(d.x+29,d.y+8,4,4);\nx.fillStyle='#eaeaea';\nx.fillRect(d.x+5,d.y+30,6,8+legOff);\nx.fillRect(d.x+20,d.y+30,6,8-legOff);\nx.restore()\n}\nfunction drawCactus(q){\nx.save();\nx.shadowColor='rgba(255,90,90,.35)';x.shadowBlur=10;\nx.fillStyle='#e17a7a';\nx.fillRect(q.x,q.y,q.w,q.h);\nx.fillRect(q.x-7,q.y+10,7,6);\nx.fillRect(q.x-7,q.y+4,6,12);\nx.fillRect(q.x+q.w,q.y+18,7,6);\nx.fillRect(q.x+q.w+1,q.y+12,6,12);\nx.restore()\n}\nfunction drawParticles(){\nparticles.forEach(p=>{x.fillStyle='rgba('+p.col+','+Math.max(p.life,0)+')';x.fillRect(p.x,p.y,p.size,p.size)})\n}\nfunction drawAmbient(){\nambient.forEach(p=>{let a=.15+Math.sin(runT*.05+p.ph)*.1;x.fillStyle='rgba(180,160,255,'+a+')';x.beginPath();x.arc(p.x,p.y,p.r,0,7);x.fill()})\n}\nfunction draw(){\nx.clearRect(0,0,c.width,c.height);\nx.save();\nif(shake>0)x.translate((Math.random()-.5)*shake,(Math.random()-.5)*shake);\ndrawAmbient();\nx.fillStyle='rgba(255,255,255,.35)';\nclouds.forEach(q=>{let b=Math.sin(runT*.03+q.x)*2;x.fillRect(q.x,q.y+b,q.w,5);x.fillRect(q.x+10,q.y+b-5,q.w*.45,10)});\nx.strokeStyle='rgba(255,255,255,.25)';\nx.lineWidth=2;\nx.setLineDash([10,8]);\nx.lineDashOffset=-runT*speed*.6;\nx.beginPath();x.moveTo(0,GY);x.lineTo(c.width,GY);x.stroke();\nx.setLineDash([]);\ndrawTrail();\ndrawDino();\no.forEach(drawCactus);\ndrawParticles();\nif(flash>0){x.fillStyle='rgba(255,60,60,'+(flash*.35)+')';x.fillRect(0,0,c.width,c.height)}\nx.restore();\nif(gameOver){\nx.fillStyle='rgba(15,15,25,.55)';x.fillRect(0,0,c.width,c.height);\nx.fillStyle='#fff';x.textAlign='center';\nx.font='bold 24px Arial';x.fillText('GAME OVER',c.width/2,85);\nx.font='14px Arial';x.fillText('Tocca per giocare di nuovo',c.width/2,112);\nx.textAlign='left'\n}\n}\nfunction loop(t){\nif(!last)last=t;\nlet dt=Math.min((t-last)/16.67,2);\nlast=t;\nrunT+=dt;\nif(!gameOver){\nd.y+=d.vy*dt;d.vy+=.75*dt;\nif(d.y>=132){\nif(d.jumping){burst(d.x+13,GY,10,'255,255,255',3.5);squash=1.35}\nd.y=132;d.vy=0;d.jumping=false\n}\nif(d.jumping)trail.push({x:d.x,y:d.y});\nif(trail.length>6)trail.shift();\nif(!d.jumping)trail.length=0;\nsquash+=(1-squash)*.18*dt;\nif(!d.jumping&&Math.floor(runT)%8===0&&Math.random()<.4)burst(d.x+6,GY-2,1,'255,255,255',1.5);\nambient.forEach(p=>{p.x-=p.vx*dt;if(p.x<-4)p.x=c.width+4});\nspawnTimer-=dt;\nif(spawnTimer<=0){cactus();spawnTimer=Math.max(38,62-speed*1.4)+Math.random()*30}\no.forEach(q=>q.x-=speed*dt);\no=o.filter(q=>q.x>-40);\nclouds.forEach(q=>{q.x-=q.s*dt;if(q.x<-80)q.x=c.width+Math.random()*100});\nparticles.forEach(p=>{p.x+=p.vx*dt;p.y+=p.vy*dt;p.vy+=.3*dt;p.life-=.03*dt});\nparticles=particles.filter(p=>p.life>0);\nspeed=Math.min(11,speed+.0018*dt);\nscore+=dt*.6;\nif(score>best)best=score;\nif(Math.floor(score/500)>milestone){\nmilestone=Math.floor(score/500);\nscoreEl.style.transform='scale(1.35)';\nsetTimeout(()=>scoreEl.style.transform='scale(1)',150)\n}\nscoreEl.textContent=String(Math.floor(score)).padStart(5,'0');\nbestEl.textContent='BEST '+String(Math.floor(best)).padStart(5,'0');\nstatusEl.textContent='Speed '+speed.toFixed(1)+'x';\nfor(const q of o)if(hit(d,q)){\ngameOver=true;shake=14;flash=1;\nsaveBest(best);\nburst(d.x+13,d.y+15,18,'255,90,90',5)\n}\n}\nif(shake>0)shake=Math.max(0,shake-.6*dt);\nif(flash>0)flash=Math.max(0,flash-.05*dt);\ndraw();\nrequestAnimationFrame(loop)\n}\ndocument.addEventListener('pointerdown',e=>{e.preventDefault();jumpDino()});\ndocument.addEventListener('keydown',e=>{if(e.code==='Space'){e.preventDefault();jumpDino()}});\nreset();\nrequestAnimationFrame(loop);\n</script></body>";
|
|
4
|
+
return options.title ? html.replace('mini-app', String(options.title)) : html;
|
|
5
|
+
};
|
package/lib/Utils/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export * from './messages-media.js';
|
|
|
6
6
|
export * from './rich-message-utils.js';
|
|
7
7
|
export * from './meta-compositing.js';
|
|
8
8
|
export * from './bot-planning-replay.js';
|
|
9
|
+
export * from './bot-capabilities.js';
|
|
9
10
|
export * from './welcome-flow.js';
|
|
10
11
|
export * from './message-composer.js';
|
|
11
12
|
export * from './consumer-application.js';
|
|
@@ -38,3 +39,6 @@ export * from './native-flow.js';
|
|
|
38
39
|
export * from './view-once-utils.js';
|
|
39
40
|
export * from './website-preview.js';
|
|
40
41
|
export * from './portable-compat.js';
|
|
42
|
+
export * from './games/miniapp.js';
|
|
43
|
+
export { prepareVerticalAlbumMessage } from './rich-message-utils.js';
|
|
44
|
+
export { prepareAIRichCommandMenu, prepareVideoGridMessage } from './rich-message-utils.js';
|