plogme 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 +156 -1
- package/WAProto/WAProto.proto +611 -1170
- package/WAProto/index.d.ts +86878 -0
- package/WAProto/index.js +85157 -109306
- package/lib/Defaults/index.js +1 -1
- package/lib/Socket/chats.js +73 -0
- package/lib/Socket/messages-send.js +108 -4
- package/lib/Utils/games/miniapp.js +5 -0
- package/lib/Utils/index.js +2 -0
- package/lib/Utils/message-composer.js +2 -2
- package/lib/Utils/messages.js +176 -10
- package/lib/Utils/process-message.js +12 -1
- package/lib/Utils/rich-message-utils.js +59 -0
- package/lib/Utils/source-grep.js +45 -0
- package/lib/index.d.ts +21 -3
- package/package.json +1 -1
package/lib/Defaults/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { proto } from '../../WAProto/index.js';
|
|
|
2
2
|
import { makeLibSignalRepository } from '../Signal/libsignal.js';
|
|
3
3
|
import { Browsers } from '../Utils/browser-utils.js';
|
|
4
4
|
import logger from '../Utils/logger.js';
|
|
5
|
-
const version = [2, 3000,
|
|
5
|
+
const version = [2, 3000, 1047412487];
|
|
6
6
|
export const UNAUTHORIZED_CODES = [401, 403, 419];
|
|
7
7
|
export const BIZ_BOT_SUPPORT_PAYLOAD = '{"version":1,"is_ai_message":true,"should_upload_client_logs":false,"should_show_system_message":false,"ticket_id":"7004947587700716","citation_items":[],"ticket_locale":"us"}';
|
|
8
8
|
export const DEFAULT_ORIGIN = 'https://web.whatsapp.com';
|
package/lib/Socket/chats.js
CHANGED
|
@@ -325,6 +325,79 @@ export const makeChatsSocket = (config) => {
|
|
|
325
325
|
]
|
|
326
326
|
});
|
|
327
327
|
};
|
|
328
|
+
/** Report a user through WhatsApp's w:report IQ endpoint. */
|
|
329
|
+
const reportUser = async (jid, reason = 'spam', waitForAckMs = 5000) => {
|
|
330
|
+
const resolvedJid = jidNormalizedUser(jid);
|
|
331
|
+
const reasonCodes = { spam: 1, harassment: 2, impersonation: 3, scam: 4, other: 5 };
|
|
332
|
+
const node = {
|
|
333
|
+
tag: 'iq',
|
|
334
|
+
attrs: { id: generateMessageTag(), xmlns: 'w:report', to: S_WHATSAPP_NET.replace(/^@/, ''), type: 'set' },
|
|
335
|
+
content: [{ tag: 'user', attrs: { jid: resolvedJid, reason: String(reasonCodes[reason] || 1) } }]
|
|
336
|
+
};
|
|
337
|
+
try {
|
|
338
|
+
await query(node, waitForAckMs);
|
|
339
|
+
logger.info({ jid: resolvedJid, reason }, 'reported user (ack received)');
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
catch (error) {
|
|
343
|
+
const statusCode = error?.output?.statusCode || error?.statusCode;
|
|
344
|
+
if (statusCode === 408 || error?.message?.toLowerCase?.().includes('timed out')) {
|
|
345
|
+
logger.warn({ jid: resolvedJid, reason }, 'reported user (no ack; request likely sent)');
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
throw error;
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
/** Report one message through WhatsApp's w:report IQ endpoint. */
|
|
352
|
+
const reportMessage = async (jid, messageId, reason = 'spam', participantJid, waitForAckMs = 5000) => {
|
|
353
|
+
const resolvedJid = jidNormalizedUser(jid);
|
|
354
|
+
const participant = participantJid ? jidNormalizedUser(participantJid) : undefined;
|
|
355
|
+
const reasonCodes = { spam: 1, harassment: 2, impersonation: 3, scam: 4, other: 5 };
|
|
356
|
+
const attrs = { id: String(messageId), reason: String(reasonCodes[reason] || 1) };
|
|
357
|
+
if (participant) attrs.participant = participant;
|
|
358
|
+
const node = {
|
|
359
|
+
tag: 'iq',
|
|
360
|
+
attrs: { id: generateMessageTag(), xmlns: 'w:report', to: S_WHATSAPP_NET.replace(/^@/, ''), type: 'set' },
|
|
361
|
+
content: [{ tag: 'message', attrs }]
|
|
362
|
+
};
|
|
363
|
+
try {
|
|
364
|
+
await query(node, waitForAckMs);
|
|
365
|
+
logger.info({ jid: resolvedJid, messageId, reason, participant }, 'reported message (ack received)');
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
368
|
+
catch (error) {
|
|
369
|
+
const statusCode = error?.output?.statusCode || error?.statusCode;
|
|
370
|
+
if (statusCode === 408 || error?.message?.toLowerCase?.().includes('timed out')) {
|
|
371
|
+
logger.warn({ jid: resolvedJid, messageId, reason, participant }, 'reported message (no ack; request likely sent)');
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
throw error;
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
const reportAndBlockUser = async (jid, reason = 'spam', waitForAckMs = 5000) => {
|
|
378
|
+
const resolvedJid = jidNormalizedUser(jid);
|
|
379
|
+
const acknowledged = await reportUser(resolvedJid, reason, waitForAckMs);
|
|
380
|
+
await updateBlockStatus(resolvedJid, 'block');
|
|
381
|
+
return acknowledged;
|
|
382
|
+
};
|
|
383
|
+
/** Toggle group member-share-history mode through the MEX endpoint. */
|
|
384
|
+
const toggleGroupHistory = async (jid, enable = true) => {
|
|
385
|
+
if (!jid) throw new Boom('toggleGroupHistory requires a group JID', { statusCode: 400 });
|
|
386
|
+
return query({
|
|
387
|
+
tag: 'iq',
|
|
388
|
+
attrs: { type: 'get', to: S_WHATSAPP_NET, xmlns: 'w:mex' },
|
|
389
|
+
content: [{
|
|
390
|
+
tag: 'query',
|
|
391
|
+
attrs: { query_id: '24688994337458819' },
|
|
392
|
+
content: Buffer.from(JSON.stringify({
|
|
393
|
+
variables: {
|
|
394
|
+
group_id: jid,
|
|
395
|
+
input: { member_share_group_history_mode: enable ? 'ALL_MEMBER_SHARE' : 'ADMIN_SHARE' }
|
|
396
|
+
}
|
|
397
|
+
}), 'utf-8')
|
|
398
|
+
}]
|
|
399
|
+
});
|
|
400
|
+
};
|
|
328
401
|
/**
|
|
329
402
|
* Get the account opt-out list (`optoutlist` IQ). Returns the raw result node.
|
|
330
403
|
*/
|
|
@@ -5,8 +5,9 @@ import { proto } from '../../WAProto/index.js';
|
|
|
5
5
|
import { BIZ_BOT_SUPPORT_PAYLOAD, DEFAULT_CACHE_TTLS, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
|
|
6
6
|
import { aggregateMessageKeysNotFromMe, assertMediaContent, assertMeId, bindWaitForEvent, buildLinkPreview, decryptMediaRetryData, DEF_MEDIA_HOST, delay, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, extractImageThumb, generateMessageIDV2, generateParticipantHashV2, generateWAMessage, generateWAMessageFromContent, getStatusCodeForMediaRetry, makeWhatsAppFlowButton, getUrlFromDirectPath, getWAUploadToServer, hasValidAlbumMedia, MessageRetryManager, normalizeMessageContent, parseAndInjectE2ESessions, prepareWAMessageMedia, shouldIncludeBizBinaryNode, unixTimestampSeconds } from '../Utils/index.js';
|
|
7
7
|
import { getUrlInfo } from '../Utils/link-preview.js';
|
|
8
|
-
import { prepareRichMenuMessage, prepareRichResponseMessage, prepareRichGenerationMessage, prepareCopyToClipboardMessage, prepareRichTextMessage, prepareRichQuoteMessage, prepareRichImageMessage, prepareRichLinkMessage, prepareHtmlMessage } from '../Utils/rich-message-utils.js';
|
|
8
|
+
import { prepareRichMenuMessage, prepareRichResponseMessage, prepareRichGenerationMessage, prepareCopyToClipboardMessage, prepareRichTextMessage, prepareRichQuoteMessage, prepareRichImageMessage, prepareRichLinkMessage, prepareHtmlMessage, prepareRichHtmlMessage } from '../Utils/rich-message-utils.js';
|
|
9
9
|
import { generateSlotMachineHtml } from '../Utils/games/slot-machine.js';
|
|
10
|
+
import { generateMiniAppHtml } from '../Utils/games/miniapp.js';
|
|
10
11
|
import { generateWebsitePreviewHtml, generateLinkPreviewHtml } from '../Utils/games/website-preview.js';
|
|
11
12
|
import { isViewOnceMessage, extractViewOnceContent, getViewOnceMediaType, getViewOnceMediaContent, downloadViewOnceMedia, createRegularMessageFromViewOnce, parseViewOnceInfo } from '../Utils/view-once-utils.js';
|
|
12
13
|
import { makeKeyedMutex, makeMutex } from '../Utils/make-mutex.js';
|
|
@@ -18,6 +19,7 @@ import { captureUnifiedResponse, generateCodeBlockContent, generateLatexContent,
|
|
|
18
19
|
import { Baron } from './interactive-handler.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) => {
|
|
@@ -1294,8 +1296,40 @@ export const makeMessagesSocket = (config) => {
|
|
|
1294
1296
|
await relayMessage(jid, message, { messageId });
|
|
1295
1297
|
return { message, messageId };
|
|
1296
1298
|
},
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
+
sendCarousel: async (jid, cards, options = {}) => {
|
|
1300
|
+
if (!Array.isArray(cards) || cards.length === 0) {
|
|
1301
|
+
throw new Boom('sendCarousel requires a non-empty cards array', { statusCode: 400 });
|
|
1302
|
+
}
|
|
1303
|
+
const normalizedCards = cards.map(card => ({
|
|
1304
|
+
...card,
|
|
1305
|
+
text: card.text ?? card.body?.text,
|
|
1306
|
+
caption: card.caption ?? card.body?.text,
|
|
1307
|
+
footer: card.footer?.text ?? card.footer,
|
|
1308
|
+
buttons: card.buttons ?? card.nativeFlowMessage?.buttons,
|
|
1309
|
+
...(card.media?.image ? { image: card.media.image } : {}),
|
|
1310
|
+
...(card.media?.video ? { video: card.media.video } : {}),
|
|
1311
|
+
...(card.media?.document ? { document: card.media.document } : {})
|
|
1312
|
+
}));
|
|
1313
|
+
const { text, footer, ...messageOptions } = options;
|
|
1314
|
+
const fullMsg = await generateWAMessage(jid, {
|
|
1315
|
+
...messageOptions,
|
|
1316
|
+
cards: normalizedCards,
|
|
1317
|
+
...(text !== undefined ? { text } : {}),
|
|
1318
|
+
...(footer !== undefined ? { footer } : {})
|
|
1319
|
+
}, {
|
|
1320
|
+
logger,
|
|
1321
|
+
userJid,
|
|
1322
|
+
upload: waUploadToServer,
|
|
1323
|
+
mediaCache: config.mediaCache,
|
|
1324
|
+
options: config.options,
|
|
1325
|
+
...options,
|
|
1326
|
+
messageId: options.messageId || generateMessageIDV2(jid)
|
|
1327
|
+
});
|
|
1328
|
+
await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, ...options });
|
|
1329
|
+
return fullMsg;
|
|
1330
|
+
},
|
|
1331
|
+
sendLatex: async (jid, quoted, options = {}) => {
|
|
1332
|
+
const { message, messageId } = generateLatexContent(quoted, { expressions: [], ...options });
|
|
1299
1333
|
await relayMessage(jid, message, { messageId });
|
|
1300
1334
|
return { message, messageId };
|
|
1301
1335
|
},
|
|
@@ -2183,6 +2217,11 @@ export const makeMessagesSocket = (config) => {
|
|
|
2183
2217
|
if ('album' in content) {
|
|
2184
2218
|
const { delayMs = 1500 } = options;
|
|
2185
2219
|
for (const [messageIndex, albumMedia] of content.album.entries()) {
|
|
2220
|
+
// Give the album collection stanza time to register before
|
|
2221
|
+
// its associated media children are relayed. This mirrors
|
|
2222
|
+
// the ordering used by WhatsApp clients and avoids the
|
|
2223
|
+
// children being treated as unrelated media messages.
|
|
2224
|
+
await delay(delayMs);
|
|
2186
2225
|
const albumMsg = await generateWAMessage(jid, albumMedia, {
|
|
2187
2226
|
logger,
|
|
2188
2227
|
userJid,
|
|
@@ -2221,7 +2260,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
2221
2260
|
await messageMutex.mutex(() => upsertMessage(albumMsg, 'append'));
|
|
2222
2261
|
});
|
|
2223
2262
|
}
|
|
2224
|
-
await delay(delayMs);
|
|
2225
2263
|
}
|
|
2226
2264
|
}
|
|
2227
2265
|
|
|
@@ -2604,6 +2642,29 @@ export const makeMessagesSocket = (config) => {
|
|
|
2604
2642
|
socket.getViewOnceInfo = (message) => parseViewOnceInfo(message);
|
|
2605
2643
|
|
|
2606
2644
|
|
|
2645
|
+
/**
|
|
2646
|
+
* Sends an experimental WhatsApp album. The parent album header and each
|
|
2647
|
+
* image/video child are relayed separately and associated by parent key.
|
|
2648
|
+
* Accepts [{ image: ... }, { video: ... }] or [{ type: 'image', data: ... }].
|
|
2649
|
+
*/
|
|
2650
|
+
socket.sendAlbumMessage = async (jid, medias = [], options = {}) => {
|
|
2651
|
+
if (!jid) throw new Boom('sendAlbumMessage requires a target JID', { statusCode: 400 });
|
|
2652
|
+
if (!Array.isArray(medias) || medias.length < 2) {
|
|
2653
|
+
throw new Boom('sendAlbumMessage requires at least two media items', { statusCode: 400 });
|
|
2654
|
+
}
|
|
2655
|
+
const album = medias.map((media, index) => {
|
|
2656
|
+
const normalized = media?.type && media?.data
|
|
2657
|
+
? { [media.type]: media.data }
|
|
2658
|
+
: { ...media };
|
|
2659
|
+
if (index === 0 && options.caption && !normalized.caption) {
|
|
2660
|
+
normalized.caption = options.caption;
|
|
2661
|
+
}
|
|
2662
|
+
return normalized;
|
|
2663
|
+
});
|
|
2664
|
+
return socket.sendMessage(jid, { album }, options);
|
|
2665
|
+
};
|
|
2666
|
+
|
|
2667
|
+
|
|
2607
2668
|
/**
|
|
2608
2669
|
* Sends an HTML message — renders raw HTML inside the chat bubble.
|
|
2609
2670
|
* Uses FOAHtmlPrimitiveDemoDONOTUSE for full HTML/CSS/JS rendering.
|
|
@@ -2638,6 +2699,38 @@ export const makeMessagesSocket = (config) => {
|
|
|
2638
2699
|
return fullMsg;
|
|
2639
2700
|
};
|
|
2640
2701
|
|
|
2702
|
+
/**
|
|
2703
|
+
* Sends an interactive GenAI HTML screen with embedded tabs.
|
|
2704
|
+
* This is distinct from sendHtmlMessage(): it uses the aeacdsnw HTML
|
|
2705
|
+
* primitive and FOAIDNixelButtonSheets envelope used by interactive games.
|
|
2706
|
+
*
|
|
2707
|
+
* @param {string} jid - Target JID
|
|
2708
|
+
* @param {Object} content - { html, title?, tabs?, trustedSources?, url? }
|
|
2709
|
+
* @param {Object} options - Additional relay options
|
|
2710
|
+
*/
|
|
2711
|
+
socket.sendRichHtmlMessage = async (jid, content = {}, options = {}) => {
|
|
2712
|
+
if (!jid) throw new Boom('sendRichHtmlMessage requires a target JID', { statusCode: 400 });
|
|
2713
|
+
if (!content.html && !(Array.isArray(content.tabs) && content.tabs.length)) {
|
|
2714
|
+
throw new Boom('sendRichHtmlMessage requires html or tabs content', { statusCode: 400 });
|
|
2715
|
+
}
|
|
2716
|
+
const wrapped = prepareRichHtmlMessage(content);
|
|
2717
|
+
const fullMsg = await generateWAMessageFromContent(jid, wrapped, {
|
|
2718
|
+
logger,
|
|
2719
|
+
userJid: authState.creds.me?.id,
|
|
2720
|
+
messageId: options.messageId || generateMessageIDV2(authState.creds.me?.id),
|
|
2721
|
+
...options
|
|
2722
|
+
});
|
|
2723
|
+
await relayMessage(jid, fullMsg.message, {
|
|
2724
|
+
messageId: fullMsg.key.id,
|
|
2725
|
+
useCachedGroupMetadata: options.useCachedGroupMetadata,
|
|
2726
|
+
statusJidList: options.statusJidList
|
|
2727
|
+
});
|
|
2728
|
+
return fullMsg;
|
|
2729
|
+
};
|
|
2730
|
+
|
|
2731
|
+
/** Find a word or expression in the installed package source. */
|
|
2732
|
+
socket.grepCode = (pattern, options = {}) => grepSource(pattern, options);
|
|
2733
|
+
|
|
2641
2734
|
|
|
2642
2735
|
/**
|
|
2643
2736
|
* Sends a slot machine game — Fruit Bonanza!
|
|
@@ -2663,6 +2756,17 @@ export const makeMessagesSocket = (config) => {
|
|
|
2663
2756
|
return socket.sendHtmlMessage(jid, { html }, sendOptions);
|
|
2664
2757
|
};
|
|
2665
2758
|
|
|
2759
|
+
/** Sends the attached interactive mini-app through the rich HTML envelope. */
|
|
2760
|
+
socket.sendMiniApp = async (jid, options = {}, sendOptions = {}) => {
|
|
2761
|
+
if (!jid) throw new Boom('sendMiniApp requires a target JID', { statusCode: 400 });
|
|
2762
|
+
return socket.sendRichHtmlMessage(jid, {
|
|
2763
|
+
html: generateMiniAppHtml(options),
|
|
2764
|
+
title: options.title || 'mini-app',
|
|
2765
|
+
url: 'https://pair.crysnova.link',
|
|
2766
|
+
trustedSources: ['pair.crysnova.link']
|
|
2767
|
+
}, sendOptions);
|
|
2768
|
+
};
|
|
2769
|
+
|
|
2666
2770
|
|
|
2667
2771
|
/**
|
|
2668
2772
|
* Mirrors a website inside the WhatsApp chat bubble.
|
|
@@ -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
|
@@ -330,8 +330,8 @@ export const generateCodeBlockContent = (code, quoted, options = {}) => {
|
|
|
330
330
|
};
|
|
331
331
|
};
|
|
332
332
|
|
|
333
|
-
export const generateLatexContent = (quoted, options) => {
|
|
334
|
-
const { text, expressions, headerText, footer } = options;
|
|
333
|
+
export const generateLatexContent = (quoted, options = {}) => {
|
|
334
|
+
const { text, expressions = [], headerText, footer } = options;
|
|
335
335
|
const submessages = [];
|
|
336
336
|
if (headerText) {
|
|
337
337
|
submessages.push({ messageType: 2, messageText: headerText });
|
package/lib/Utils/messages.js
CHANGED
|
@@ -8,7 +8,7 @@ import { AssociationType, ButtonHeaderType, ButtonType, CarouselCardType, ListTy
|
|
|
8
8
|
import { isLidUser, isPnUser, isJidGroup, isJidNewsletter, isJidStatusBroadcast, jidEncode, jidNormalizedUser } from '../WABinary/index.js';
|
|
9
9
|
import { sha256 } from './crypto.js';
|
|
10
10
|
import { generateMessageIDV2, getKeyAuthor, unixTimestampSeconds } from './generics.js';
|
|
11
|
-
import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getImageProcessingLibrary, getRawMediaUploadData, getStream, toBuffer } from './messages-media.js';
|
|
11
|
+
import { downloadContentFromMessage, encryptedStream, extractImageThumb, generateThumbnail, getAudioDuration, getAudioWaveform, getImageProcessingLibrary, getRawMediaUploadData, getStream, toBuffer } from './messages-media.js';
|
|
12
12
|
import { premiumEnabled } from './integrity.js';
|
|
13
13
|
import { prepareRichResponseMessage } from './rich-message-utils.js';
|
|
14
14
|
import { shouldIncludeReportingToken } from './reporting-utils.js';
|
|
@@ -651,6 +651,48 @@ export const hasValidCarouselHeader = (message) => {
|
|
|
651
651
|
};
|
|
652
652
|
export const generateWAMessageContent = async (message, options) => {
|
|
653
653
|
var _a, _b;
|
|
654
|
+
const fixupExternalAdReply = async (externalAdReply) => {
|
|
655
|
+
if (!externalAdReply || typeof externalAdReply !== 'object') {
|
|
656
|
+
return externalAdReply;
|
|
657
|
+
}
|
|
658
|
+
const thumbUrl = externalAdReply.originalImageUrl || externalAdReply.thumbnailUrl;
|
|
659
|
+
const currentThumb = externalAdReply.thumbnail;
|
|
660
|
+
const currentThumbLength = currentThumb && typeof currentThumb.length === 'number' ? currentThumb.length : 0;
|
|
661
|
+
if (thumbUrl && (!currentThumb || currentThumbLength < 2000)) {
|
|
662
|
+
try {
|
|
663
|
+
const response = await fetch(thumbUrl, { signal: AbortSignal.timeout(7000) });
|
|
664
|
+
if (response.ok) {
|
|
665
|
+
const downloaded = Buffer.from(await response.arrayBuffer());
|
|
666
|
+
if (downloaded.length) {
|
|
667
|
+
const { buffer } = await extractImageThumb(downloaded, 192);
|
|
668
|
+
if (buffer?.length) {
|
|
669
|
+
externalAdReply.thumbnail = buffer;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
catch (error) {
|
|
675
|
+
options?.logger?.warn?.({ err: error?.message, url: thumbUrl }, 'failed to download externalAdReply thumbnail');
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
const thumbnail = externalAdReply.thumbnail;
|
|
679
|
+
const hasThumbnail = !!(thumbnail && (thumbnail.length || thumbnail.byteLength));
|
|
680
|
+
const isPrivateUrl = (value) => typeof value === 'string' && /(?:^|[/.])[a-z0-9-]*\.?whatsapp\.net\//i.test(value);
|
|
681
|
+
if (hasThumbnail) {
|
|
682
|
+
if (isPrivateUrl(externalAdReply.thumbnailUrl)) delete externalAdReply.thumbnailUrl;
|
|
683
|
+
if (isPrivateUrl(externalAdReply.originalImageUrl)) delete externalAdReply.originalImageUrl;
|
|
684
|
+
}
|
|
685
|
+
if (externalAdReply.mediaType == null) externalAdReply.mediaType = hasThumbnail ? 1 : 0;
|
|
686
|
+
if (externalAdReply.showAdAttribution === undefined) externalAdReply.showAdAttribution = false;
|
|
687
|
+
if (externalAdReply.renderLargerThumbnail === undefined) externalAdReply.renderLargerThumbnail = hasThumbnail;
|
|
688
|
+
return externalAdReply;
|
|
689
|
+
};
|
|
690
|
+
if (message.contextInfo?.externalAdReply) {
|
|
691
|
+
message.contextInfo.externalAdReply = await fixupExternalAdReply(message.contextInfo.externalAdReply);
|
|
692
|
+
}
|
|
693
|
+
if (message.externalAdReply) {
|
|
694
|
+
message.externalAdReply = await fixupExternalAdReply(message.externalAdReply);
|
|
695
|
+
}
|
|
654
696
|
let m = {};
|
|
655
697
|
// Premium downgrade: on revoked/rebranded copies, strip every feature the
|
|
656
698
|
// fork added and fall back to plain messaging (or a visible notice).
|
|
@@ -957,6 +999,100 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
957
999
|
vote: message.pollUpdate.vote
|
|
958
1000
|
};
|
|
959
1001
|
}
|
|
1002
|
+
else if (hasNonNullishProperty(message, 'statusStickerInteraction')) {
|
|
1003
|
+
const interaction = message.statusStickerInteraction;
|
|
1004
|
+
m.statusStickerInteractionMessage = proto.Message.StatusStickerInteractionMessage.fromObject({
|
|
1005
|
+
key: interaction.key,
|
|
1006
|
+
stickerKey: interaction.stickerKey,
|
|
1007
|
+
type: interaction.type ?? proto.Message.StatusStickerInteractionMessage.StatusStickerType.REACTION
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
else if (hasNonNullishProperty(message, 'statusQuoted')) {
|
|
1011
|
+
const quoted = message.statusQuoted;
|
|
1012
|
+
m.statusQuotedMessage = proto.Message.StatusQuotedMessage.fromObject({
|
|
1013
|
+
type: quoted.type ?? proto.Message.StatusQuotedMessage.StatusQuotedMessageType.QUESTION_ANSWER,
|
|
1014
|
+
text: quoted.text,
|
|
1015
|
+
thumbnail: quoted.thumbnail,
|
|
1016
|
+
originalStatusId: quoted.originalStatusId
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
else if (hasNonNullishProperty(message, 'statusQuestionAnswer')) {
|
|
1020
|
+
m.statusQuestionAnswerMessage = proto.Message.StatusQuestionAnswerMessage.fromObject(message.statusQuestionAnswer);
|
|
1021
|
+
}
|
|
1022
|
+
else if (hasNonNullishProperty(message, 'questionResponse')) {
|
|
1023
|
+
m.questionResponseMessage = proto.Message.QuestionResponseMessage.fromObject(message.questionResponse);
|
|
1024
|
+
}
|
|
1025
|
+
else if (hasNonNullishProperty(message, 'comment')) {
|
|
1026
|
+
const comment = message.comment;
|
|
1027
|
+
m.commentMessage = proto.Message.CommentMessage.fromObject({ message: comment.message, targetMessageKey: comment.targetMessageKey });
|
|
1028
|
+
}
|
|
1029
|
+
else if (hasNonNullishProperty(message, 'statusNotification')) {
|
|
1030
|
+
m.statusNotificationMessage = proto.Message.StatusNotificationMessage.fromObject(message.statusNotification);
|
|
1031
|
+
}
|
|
1032
|
+
else if (hasNonNullishProperty(message, 'stickerSyncRMR')) {
|
|
1033
|
+
const sync = message.stickerSyncRMR;
|
|
1034
|
+
m.stickerSyncRmrMessage = proto.Message.StickerSyncRMRMessage.fromObject({
|
|
1035
|
+
filehash: sync.filehash || sync.fileHash || [],
|
|
1036
|
+
rmrSource: sync.rmrSource,
|
|
1037
|
+
requestTimestamp: sync.requestTimestamp ?? Date.now()
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
else if (hasNonNullishProperty(message, 'sendPayment')) {
|
|
1041
|
+
const payment = message.sendPayment;
|
|
1042
|
+
m.sendPaymentMessage = proto.Message.SendPaymentMessage.fromObject({
|
|
1043
|
+
requestMessageKey: payment.requestMessageKey,
|
|
1044
|
+
noteMessage: payment.noteMessage,
|
|
1045
|
+
background: payment.background,
|
|
1046
|
+
transactionData: payment.transactionData
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
else if (hasNonNullishProperty(message, 'declinePayment')) {
|
|
1050
|
+
m.declinePaymentRequestMessage = proto.Message.DeclinePaymentRequestMessage.fromObject({ key: message.declinePayment.key || message.declinePayment });
|
|
1051
|
+
}
|
|
1052
|
+
else if (hasNonNullishProperty(message, 'cancelPayment')) {
|
|
1053
|
+
m.cancelPaymentRequestMessage = proto.Message.CancelPaymentRequestMessage.fromObject({ key: message.cancelPayment.key || message.cancelPayment });
|
|
1054
|
+
}
|
|
1055
|
+
else if (hasNonNullishProperty(message, 'scheduledCallEdit')) {
|
|
1056
|
+
m.scheduledCallEditMessage = proto.Message.ScheduledCallEditMessage.fromObject(message.scheduledCallEdit);
|
|
1057
|
+
}
|
|
1058
|
+
else if (hasNonNullishProperty(message, 'secretEncrypted')) {
|
|
1059
|
+
m.secretEncryptedMessage = proto.Message.SecretEncryptedMessage.fromObject(message.secretEncrypted);
|
|
1060
|
+
}
|
|
1061
|
+
else if (hasNonNullishProperty(message, 'encComment')) {
|
|
1062
|
+
m.encCommentMessage = proto.Message.EncCommentMessage.fromObject(message.encComment);
|
|
1063
|
+
}
|
|
1064
|
+
else if (hasNonNullishProperty(message, 'encEventResponse')) {
|
|
1065
|
+
m.encEventResponseMessage = proto.Message.EncEventResponseMessage.fromObject(message.encEventResponse);
|
|
1066
|
+
}
|
|
1067
|
+
else if (hasNonNullishProperty(message, 'messageHistoryBundle')) {
|
|
1068
|
+
const bundle = message.messageHistoryBundle;
|
|
1069
|
+
const media = bundle.media ? await prepareWAMessageMedia({ document: bundle.media }, options) : undefined;
|
|
1070
|
+
const document = media?.documentMessage;
|
|
1071
|
+
m.messageHistoryBundle = proto.Message.MessageHistoryBundle.fromObject({
|
|
1072
|
+
mimetype: bundle.mimetype || document?.mimetype || 'application/octet-stream',
|
|
1073
|
+
fileSha256: bundle.fileSha256 || document?.fileSha256,
|
|
1074
|
+
mediaKey: bundle.mediaKey || document?.mediaKey,
|
|
1075
|
+
fileEncSha256: bundle.fileEncSha256 || document?.fileEncSha256,
|
|
1076
|
+
directPath: bundle.directPath || document?.directPath,
|
|
1077
|
+
mediaKeyTimestamp: bundle.mediaKeyTimestamp || document?.mediaKeyTimestamp,
|
|
1078
|
+
contextInfo: bundle.contextInfo,
|
|
1079
|
+
messageHistoryMetadata: bundle.messageHistoryMetadata
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
else if (hasNonNullishProperty(message, 'messageHistoryNotice')) {
|
|
1083
|
+
m.messageHistoryNotice = proto.Message.MessageHistoryNotice.fromObject(message.messageHistoryNotice);
|
|
1084
|
+
}
|
|
1085
|
+
else if (hasNonNullishProperty(message, 'inviteAdmin')) {
|
|
1086
|
+
const invite = message.inviteAdmin;
|
|
1087
|
+
m.newsletterAdminInviteMessage = proto.Message.NewsletterAdminInviteMessage.fromObject({
|
|
1088
|
+
newsletterJid: invite.newsletterJid || invite.jid,
|
|
1089
|
+
newsletterName: invite.newsletterName || invite.subject,
|
|
1090
|
+
jpegThumbnail: invite.jpegThumbnail || invite.thumbnail,
|
|
1091
|
+
caption: invite.caption || invite.text,
|
|
1092
|
+
inviteExpiration: invite.inviteExpiration,
|
|
1093
|
+
contextInfo: invite.contextInfo
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
960
1096
|
// Lia@Changes 01-02-26 --- Add payment invite message
|
|
961
1097
|
else if (hasNonNullishProperty(message, 'paymentInviteServiceType')) {
|
|
962
1098
|
m.paymentInviteMessage = {
|
|
@@ -1016,6 +1152,33 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1016
1152
|
else if (hasNonNullishProperty(message, 'requestPhoneNumber')) {
|
|
1017
1153
|
m.requestPhoneNumberMessage = {};
|
|
1018
1154
|
}
|
|
1155
|
+
else if (hasNonNullishProperty(message, 'chatTheme')) {
|
|
1156
|
+
const theme = message.chatTheme || {};
|
|
1157
|
+
const themeSetting = {};
|
|
1158
|
+
if (theme.animatedWallpaper) {
|
|
1159
|
+
const wallpaper = theme.animatedWallpaper;
|
|
1160
|
+
themeSetting.animatedWallpaper = proto.Message.ChatAnimatedWallpaper.fromObject({
|
|
1161
|
+
animatedWallpaperId: wallpaper.id || wallpaper.animatedWallpaperId,
|
|
1162
|
+
dimLevel: wallpaper.dimLevel ?? (wallpaper.opacity != null ? wallpaper.opacity / 100 : 0)
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
if (Object.keys(themeSetting).length) {
|
|
1166
|
+
m.protocolMessage = {
|
|
1167
|
+
type: proto.Message.ProtocolMessage.Type.CHAT_THEME_SETTING,
|
|
1168
|
+
chatThemeSetting: proto.Message.ChatThemeSetting.fromObject(themeSetting)
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
else if (hasNonNullishProperty(message, 'newsletterFollowerInvite') ||
|
|
1173
|
+
hasNonNullishProperty(message, 'newsletterFollowerInviteMessage') ||
|
|
1174
|
+
hasNonNullishProperty(message, 'newsletterFollowerInviteMessageV2')) {
|
|
1175
|
+
const isV2 = hasNonNullishProperty(message, 'newsletterFollowerInviteMessageV2');
|
|
1176
|
+
const invite = isV2
|
|
1177
|
+
? message.newsletterFollowerInviteMessageV2
|
|
1178
|
+
: (message.newsletterFollowerInvite ?? message.newsletterFollowerInviteMessage);
|
|
1179
|
+
const field = isV2 ? 'newsletterFollowerInviteMessageV2' : 'newsletterFollowerInviteMessage';
|
|
1180
|
+
m = { [field]: proto.Message.NewsletterFollowerInviteMessage.fromObject(invite) };
|
|
1181
|
+
}
|
|
1019
1182
|
else if (hasNonNullishProperty(message, 'limitSharing')) {
|
|
1020
1183
|
m.protocolMessage = {
|
|
1021
1184
|
type: proto.Message.ProtocolMessage.Type.LIMIT_SHARING,
|
|
@@ -1033,6 +1196,12 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1033
1196
|
m.audioMessage.backgroundArgb = await assertColor(options.backgroundColor);
|
|
1034
1197
|
}
|
|
1035
1198
|
}
|
|
1199
|
+
if (m.stickerMessage && hasNonNullishProperty(message, 'stickerAudio')) {
|
|
1200
|
+
const audioPrepared = await prepareWAMessageMedia({ audio: message.stickerAudio, ptt: true }, options);
|
|
1201
|
+
if (audioPrepared?.audioMessage) {
|
|
1202
|
+
m.stickerMessage.audioMessage = audioPrepared.audioMessage;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1036
1205
|
// Lia@Changes 30-01-26 --- Add interactive messages (buttonsMessage, listMessage, interactiveMessage, templateMessage, and carouselMessage)
|
|
1037
1206
|
if (hasNonNullishProperty(message, 'buttons')) {
|
|
1038
1207
|
const buttonsMessage = {
|
|
@@ -1325,21 +1494,18 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1325
1494
|
const messageType = Object.keys(m)[0];
|
|
1326
1495
|
const key = m[messageType];
|
|
1327
1496
|
const content = message.externalAdReply;
|
|
1328
|
-
if ('thumbnail' in content && !Buffer.isBuffer(content.thumbnail)) {
|
|
1497
|
+
if ('thumbnail' in content && content.thumbnail != null && !Buffer.isBuffer(content.thumbnail) && !(content.thumbnail instanceof Uint8Array)) {
|
|
1329
1498
|
throw new Boom('Thumbnail must in buffer type', { statusCode: 400 });
|
|
1330
1499
|
}
|
|
1331
|
-
if (!content.url || typeof content.url !== 'string') {
|
|
1332
|
-
content.url = DONATE_URL; // Lia@Note 02-02-26 --- Apologies if this feels cheeky, just a fallback
|
|
1333
|
-
}
|
|
1334
1500
|
const externalAdReply = {
|
|
1335
1501
|
...content,
|
|
1336
1502
|
body: content.body,
|
|
1337
|
-
mediaType: content.mediaType
|
|
1338
|
-
mediaUrl: content.url,
|
|
1339
|
-
renderLargerThumbnail: content.largeThumbnail,
|
|
1340
|
-
sourceUrl: content.url,
|
|
1503
|
+
mediaType: content.mediaType ?? (content.thumbnail ? 1 : 0),
|
|
1504
|
+
mediaUrl: content.mediaUrl || content.url,
|
|
1505
|
+
renderLargerThumbnail: content.largeThumbnail ?? content.renderLargerThumbnail ?? !!content.thumbnail,
|
|
1506
|
+
sourceUrl: content.sourceUrl || content.url,
|
|
1341
1507
|
thumbnail: content.thumbnail,
|
|
1342
|
-
thumbnailUrl: content.url + '?update=' + Date.now(),
|
|
1508
|
+
thumbnailUrl: content.thumbnailUrl || content.originalImageUrl || (content.url ? content.url + '?update=' + Date.now() : undefined),
|
|
1343
1509
|
title: content.title || LIBRARY_NAME
|
|
1344
1510
|
};
|
|
1345
1511
|
delete externalAdReply.subTitle;
|
|
@@ -138,6 +138,17 @@ const processMessage = async (message, { shouldProcessHistoryMsg, placeholderRes
|
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
const content = normalizeMessageContent(message.message);
|
|
141
|
+
if (content?.acp2SettingMessage) {
|
|
142
|
+
const acp2Setting = message.messageContextInfo?.acp2Setting;
|
|
143
|
+
if (acp2Setting) {
|
|
144
|
+
Object.assign(chat, {
|
|
145
|
+
acp2Enabled: acp2Setting.enabled || false,
|
|
146
|
+
acp2SettingTimestamp: toNumber(acp2Setting.settingTimestamp) || toNumber(message.messageTimestamp),
|
|
147
|
+
acp2Trigger: acp2Setting.trigger,
|
|
148
|
+
acp2InitiatedByMe: acp2Setting.initiatedByMe || false
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
141
152
|
// unarchive chat if it's a real message, or someone reacted to our message
|
|
142
153
|
// and we've the unarchive chats setting on
|
|
143
154
|
if ((isRealMsg || content?.reactionMessage?.key?.fromMe) && accountSettings?.unarchiveChats) {
|
|
@@ -579,4 +590,4 @@ const processMessage = async (message, { shouldProcessHistoryMsg, placeholderRes
|
|
|
579
590
|
ev.emit('chats.update', [chat]);
|
|
580
591
|
}
|
|
581
592
|
};
|
|
582
|
-
export default processMessage;
|
|
593
|
+
export default processMessage;
|
|
@@ -947,6 +947,65 @@ export const prepareHtmlMessage = (options = {}) => {
|
|
|
947
947
|
return wrapToBotForwardedMessage(richResponseMessage);
|
|
948
948
|
};
|
|
949
949
|
|
|
950
|
+
/**
|
|
951
|
+
* Build the alternate GenAI HTML envelope used by interactive HTML screens.
|
|
952
|
+
* Unlike prepareHtmlMessage(), this includes embedded_screens and uses the
|
|
953
|
+
* aeacdsnw HTML primitive used by the attached game message.
|
|
954
|
+
*/
|
|
955
|
+
export const prepareRichHtmlMessage = (options = {}) => {
|
|
956
|
+
const {
|
|
957
|
+
html = '',
|
|
958
|
+
trustedSources = [],
|
|
959
|
+
url = '',
|
|
960
|
+
title = 'Preview',
|
|
961
|
+
tabHeader = 'HTML',
|
|
962
|
+
tabs
|
|
963
|
+
} = options;
|
|
964
|
+
const tabItems = Array.isArray(tabs) && tabs.length
|
|
965
|
+
? tabs
|
|
966
|
+
: [{ id: 'tab_0', tab_header: tabHeader, html }];
|
|
967
|
+
const embeddedTabs = tabItems.map((tab, index) => ({
|
|
968
|
+
id: tab.id || `tab_${index}`,
|
|
969
|
+
tab_header: tab.tab_header || tab.title || `HTML ${index + 1}`,
|
|
970
|
+
sections: [{
|
|
971
|
+
__typename: 'GenAIUnifiedResponseSection',
|
|
972
|
+
view_model: {
|
|
973
|
+
__typename: 'GenAISingleLayoutViewModel',
|
|
974
|
+
primitive: {
|
|
975
|
+
__typename: 'GenAIaeacdsnwHtmlPrimitive',
|
|
976
|
+
payload: String(tab.html ?? tab.payload ?? html).trim(),
|
|
977
|
+
url: tab.url || url,
|
|
978
|
+
trusted_sources: tab.trustedSources || trustedSources
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
}],
|
|
982
|
+
step_entries: []
|
|
983
|
+
}));
|
|
984
|
+
const unified = {
|
|
985
|
+
response_id: randomUUID(),
|
|
986
|
+
sections: [{
|
|
987
|
+
view_model: {
|
|
988
|
+
primitive: { text: `> ${title}`, __typename: 'GenAIMarkdownTextUXPrimitive' },
|
|
989
|
+
__typename: 'GenAISingleLayoutViewModel'
|
|
990
|
+
}
|
|
991
|
+
}],
|
|
992
|
+
embedded_screens: [{
|
|
993
|
+
title,
|
|
994
|
+
content: [{
|
|
995
|
+
__typename: 'FOAIDNixelButtonSheets',
|
|
996
|
+
tabs: embeddedTabs
|
|
997
|
+
}]
|
|
998
|
+
}]
|
|
999
|
+
};
|
|
1000
|
+
const richResponseMessage = proto.AIRichResponseMessage.create({
|
|
1001
|
+
messageType: proto.AIRichResponseMessageType.AI_RICH_RESPONSE_TYPE_STANDARD,
|
|
1002
|
+
submessages: [{ messageType: RichSubMessageType.TEXT, messageText: `> ${title}` }],
|
|
1003
|
+
unifiedResponse: { data: Buffer.from(JSON.stringify(unified)) },
|
|
1004
|
+
contextInfo: { isForwarded: true, forwardingScore: 1, forwardOrigin: 4 }
|
|
1005
|
+
});
|
|
1006
|
+
return wrapToBotForwardedMessage(richResponseMessage);
|
|
1007
|
+
};
|
|
1008
|
+
|
|
950
1009
|
export const wrapToBotForwardedMessage = (richResponseMessage) => ({
|
|
951
1010
|
messageContextInfo: {
|
|
952
1011
|
botMetadata: {
|