@vanzxy/baileys 2.0.0
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/NOTICE.md +111 -0
- package/README.md +830 -0
- package/WAProto/index.d.ts +9 -0
- package/WAProto/index.js +142036 -0
- package/engine-requirements.js +13 -0
- package/lib/Defaults/index.d.ts +152 -0
- package/lib/Defaults/index.js +179 -0
- package/lib/Framework/Bot.js +209 -0
- package/lib/Framework/Context.js +90 -0
- package/lib/Framework/MediaManager.js +152 -0
- package/lib/Framework/SessionManager.js +34 -0
- package/lib/Framework/StatsManager.js +120 -0
- package/lib/Framework/Store/SQLiteStore.js +74 -0
- package/lib/Framework/index.js +11 -0
- package/lib/Signal/Group/ciphertext-message.d.ts +9 -0
- package/lib/Signal/Group/ciphertext-message.js +12 -0
- package/lib/Signal/Group/group-session-builder.d.ts +7 -0
- package/lib/Signal/Group/group-session-builder.js +30 -0
- package/lib/Signal/Group/group_cipher.d.ts +10 -0
- package/lib/Signal/Group/group_cipher.js +82 -0
- package/lib/Signal/Group/index.d.ts +11 -0
- package/lib/Signal/Group/index.js +12 -0
- package/lib/Signal/Group/keyhelper.d.ts +6 -0
- package/lib/Signal/Group/keyhelper.js +18 -0
- package/lib/Signal/Group/sender-chain-key.d.ts +13 -0
- package/lib/Signal/Group/sender-chain-key.js +26 -0
- package/lib/Signal/Group/sender-key-distribution-message.d.ts +16 -0
- package/lib/Signal/Group/sender-key-distribution-message.js +63 -0
- package/lib/Signal/Group/sender-key-message.d.ts +18 -0
- package/lib/Signal/Group/sender-key-message.js +66 -0
- package/lib/Signal/Group/sender-key-name.d.ts +11 -0
- package/lib/Signal/Group/sender-key-name.js +48 -0
- package/lib/Signal/Group/sender-key-record.d.ts +12 -0
- package/lib/Signal/Group/sender-key-record.js +41 -0
- package/lib/Signal/Group/sender-key-state.d.ts +16 -0
- package/lib/Signal/Group/sender-key-state.js +84 -0
- package/lib/Signal/Group/sender-message-key.d.ts +11 -0
- package/lib/Signal/Group/sender-message-key.js +26 -0
- package/lib/Signal/libsignal.d.ts +54 -0
- package/lib/Signal/libsignal.js +431 -0
- package/lib/Signal/lid-mapping.d.ts +20 -0
- package/lib/Signal/lid-mapping.js +277 -0
- package/lib/Socket/Client/index.d.ts +2 -0
- package/lib/Socket/Client/index.js +3 -0
- package/lib/Socket/Client/types.d.ts +5 -0
- package/lib/Socket/Client/types.js +11 -0
- package/lib/Socket/Client/websocket.d.ts +12 -0
- package/lib/Socket/Client/websocket.js +54 -0
- package/lib/Socket/business.d.ts +395 -0
- package/lib/Socket/business.js +380 -0
- package/lib/Socket/chats.d.ts +136 -0
- package/lib/Socket/chats.js +1328 -0
- package/lib/Socket/communities.d.ts +561 -0
- package/lib/Socket/communities.js +434 -0
- package/lib/Socket/dugong.d.ts +117 -0
- package/lib/Socket/dugong.js +799 -0
- package/lib/Socket/groups.d.ts +285 -0
- package/lib/Socket/groups.js +355 -0
- package/lib/Socket/index.d.ts +531 -0
- package/lib/Socket/index.js +34 -0
- package/lib/Socket/messages-recv.d.ts +333 -0
- package/lib/Socket/messages-recv.js +1782 -0
- package/lib/Socket/messages-send.d.ts +329 -0
- package/lib/Socket/messages-send.js +1383 -0
- package/lib/Socket/mex.d.ts +1 -0
- package/lib/Socket/mex.js +42 -0
- package/lib/Socket/newsletter.d.ts +281 -0
- package/lib/Socket/newsletter.js +321 -0
- package/lib/Socket/socket.d.ts +60 -0
- package/lib/Socket/socket.js +1001 -0
- package/lib/Socket/username.js +153 -0
- package/lib/Store/index.d.ts +3 -0
- package/lib/Store/index.js +4 -0
- package/lib/Store/make-in-memory-store.d.ts +62 -0
- package/lib/Store/make-in-memory-store.js +429 -0
- package/lib/Store/make-ordered-dictionary.d.ts +12 -0
- package/lib/Store/make-ordered-dictionary.js +79 -0
- package/lib/Store/object-repository.d.ts +10 -0
- package/lib/Store/object-repository.js +24 -0
- package/lib/Types/Auth.d.ts +117 -0
- package/lib/Types/Auth.js +2 -0
- package/lib/Types/Bussines.d.ts +25 -0
- package/lib/Types/Bussines.js +2 -0
- package/lib/Types/Call.d.ts +15 -0
- package/lib/Types/Call.js +2 -0
- package/lib/Types/Chat.d.ts +124 -0
- package/lib/Types/Chat.js +8 -0
- package/lib/Types/Contact.d.ts +26 -0
- package/lib/Types/Contact.js +2 -0
- package/lib/Types/Events.d.ts +256 -0
- package/lib/Types/Events.js +2 -0
- package/lib/Types/GroupMetadata.d.ts +71 -0
- package/lib/Types/GroupMetadata.js +2 -0
- package/lib/Types/Label.d.ts +2 -0
- package/lib/Types/Label.js +25 -0
- package/lib/Types/LabelAssociation.d.ts +2 -0
- package/lib/Types/LabelAssociation.js +7 -0
- package/lib/Types/Message.d.ts +11 -0
- package/lib/Types/Message.js +18 -0
- package/lib/Types/Mex.d.ts +2 -0
- package/lib/Types/Mex.js +39 -0
- package/lib/Types/Product.d.ts +79 -0
- package/lib/Types/Product.js +2 -0
- package/lib/Types/RichType.d.ts +2 -0
- package/lib/Types/RichType.js +23 -0
- package/lib/Types/Signal.d.ts +87 -0
- package/lib/Types/Signal.js +2 -0
- package/lib/Types/Socket.d.ts +136 -0
- package/lib/Types/Socket.js +2 -0
- package/lib/Types/State.d.ts +5 -0
- package/lib/Types/State.js +56 -0
- package/lib/Types/USync.d.ts +26 -0
- package/lib/Types/USync.js +2 -0
- package/lib/Types/index.d.ts +66 -0
- package/lib/Types/index.js +26 -0
- package/lib/Utils/A2UI.js +277 -0
- package/lib/Utils/MessageBuilder.js +4405 -0
- package/lib/Utils/MessageBuilder_d.ts +411 -0
- package/lib/Utils/PersistentStore.js +592 -0
- package/lib/Utils/PersistentStore_d.ts +60 -0
- package/lib/Utils/anti-delete.d.ts +74 -0
- package/lib/Utils/anti-delete.js +222 -0
- package/lib/Utils/auth-utils.d.ts +57 -0
- package/lib/Utils/auth-utils.js +307 -0
- package/lib/Utils/auto-reply.d.ts +47 -0
- package/lib/Utils/auto-reply.js +158 -0
- package/lib/Utils/baileys-event-stream.d.ts +8 -0
- package/lib/Utils/baileys-event-stream.js +62 -0
- package/lib/Utils/browser-utils.d.ts +8 -0
- package/lib/Utils/browser-utils.js +28 -0
- package/lib/Utils/business.d.ts +49 -0
- package/lib/Utils/business.js +240 -0
- package/lib/Utils/button-helper-utils.js +314 -0
- package/lib/Utils/button-sender.js +824 -0
- package/lib/Utils/chat-control.d.ts +159 -0
- package/lib/Utils/chat-control.js +232 -0
- package/lib/Utils/chat-history-helpers.d.ts +21 -0
- package/lib/Utils/chat-history-helpers.js +71 -0
- package/lib/Utils/chat-utils.d.ts +407 -0
- package/lib/Utils/chat-utils.js +886 -0
- package/lib/Utils/companion-reg-client-utils.d.ts +4 -0
- package/lib/Utils/companion-reg-client-utils.js +40 -0
- package/lib/Utils/crypto.d.ts +41 -0
- package/lib/Utils/crypto.js +148 -0
- package/lib/Utils/decode-wa-message.d.ts +83 -0
- package/lib/Utils/decode-wa-message.js +317 -0
- package/lib/Utils/event-buffer.d.ts +12 -0
- package/lib/Utils/event-buffer.js +636 -0
- package/lib/Utils/generics.d.ts +69 -0
- package/lib/Utils/generics.js +439 -0
- package/lib/Utils/history.d.ts +90 -0
- package/lib/Utils/history.js +147 -0
- package/lib/Utils/identity-change-handler.d.ts +13 -0
- package/lib/Utils/identity-change-handler.js +50 -0
- package/lib/Utils/index.d.ts +39 -0
- package/lib/Utils/index.js +46 -0
- package/lib/Utils/link-preview.d.ts +12 -0
- package/lib/Utils/link-preview.js +85 -0
- package/lib/Utils/logger.d.ts +2 -0
- package/lib/Utils/logger.js +3 -0
- package/lib/Utils/lt-hash.d.ts +12 -0
- package/lib/Utils/lt-hash.js +27 -0
- package/lib/Utils/make-mutex.d.ts +6 -0
- package/lib/Utils/make-mutex.js +33 -0
- package/lib/Utils/media-messages.d.ts +18 -0
- package/lib/Utils/media-messages.js +71 -0
- package/lib/Utils/media-set.d.ts +13 -0
- package/lib/Utils/media-set.js +172 -0
- package/lib/Utils/message-kind.js +139 -0
- package/lib/Utils/message-retry-manager.d.ts +79 -0
- package/lib/Utils/message-retry-manager.js +265 -0
- package/lib/Utils/message-search.d.ts +44 -0
- package/lib/Utils/message-search.js +189 -0
- package/lib/Utils/messages-media.d.ts +135 -0
- package/lib/Utils/messages-media.js +869 -0
- package/lib/Utils/messages.d.ts +44 -0
- package/lib/Utils/messages.js +2155 -0
- package/lib/Utils/noise-handler.d.ts +20 -0
- package/lib/Utils/noise-handler.js +201 -0
- package/lib/Utils/offline-node-processor.d.ts +9 -0
- package/lib/Utils/offline-node-processor.js +40 -0
- package/lib/Utils/past-participants.d.ts +14 -0
- package/lib/Utils/past-participants.js +40 -0
- package/lib/Utils/pre-key-manager.d.ts +25 -0
- package/lib/Utils/pre-key-manager.js +106 -0
- package/lib/Utils/process-message.d.ts +82 -0
- package/lib/Utils/process-message.js +809 -0
- package/lib/Utils/reporting-utils.d.ts +12 -0
- package/lib/Utils/reporting-utils.js +258 -0
- package/lib/Utils/rich-message-utils.d.ts +48 -0
- package/lib/Utils/rich-message-utils.js +445 -0
- package/lib/Utils/scheduling.d.ts +42 -0
- package/lib/Utils/scheduling.js +140 -0
- package/lib/Utils/signal.d.ts +82 -0
- package/lib/Utils/signal.js +201 -0
- package/lib/Utils/stanza-ack.d.ts +16 -0
- package/lib/Utils/stanza-ack.js +38 -0
- package/lib/Utils/status.d.ts +50 -0
- package/lib/Utils/status.js +108 -0
- package/lib/Utils/stickerpack.d.ts +51 -0
- package/lib/Utils/stickerpack.js +275 -0
- package/lib/Utils/sync-action-utils.d.ts +2 -0
- package/lib/Utils/sync-action-utils.js +49 -0
- package/lib/Utils/tc-token-utils.d.ts +29 -0
- package/lib/Utils/tc-token-utils.js +163 -0
- package/lib/Utils/templates.d.ts +76 -0
- package/lib/Utils/templates.js +151 -0
- package/lib/Utils/use-cache-manager-auth-state.d.ts +13 -0
- package/lib/Utils/use-cache-manager-auth-state.js +82 -0
- package/lib/Utils/use-multi-file-auth-state.d.ts +10 -0
- package/lib/Utils/use-multi-file-auth-state.js +167 -0
- package/lib/Utils/use-single-file-auth-state.d.ts +10 -0
- package/lib/Utils/use-single-file-auth-state.js +114 -0
- package/lib/Utils/use-sqlite-auth-state.d.ts +11 -0
- package/lib/Utils/use-sqlite-auth-state.js +170 -0
- package/lib/Utils/validate-connection.d.ts +42 -0
- package/lib/Utils/validate-connection.js +199 -0
- package/lib/Utils/vcard.d.ts +58 -0
- package/lib/Utils/vcard.js +105 -0
- package/lib/VoIP/audio-feeder.d.ts +15 -0
- package/lib/VoIP/audio-feeder.js +132 -0
- package/lib/VoIP/index.js +277 -0
- package/lib/VoIP/relay-transport.d.ts +43 -0
- package/lib/VoIP/relay-transport.js +559 -0
- package/lib/VoIP/signaling.js +624 -0
- package/lib/VoIP/types.d.ts +69 -0
- package/lib/VoIP/types.js +17 -0
- package/lib/VoIP/wasm-engine.d.ts +103 -0
- package/lib/VoIP/wasm-engine.js +1214 -0
- package/lib/VoIP/worker-bootstrap.js +1042 -0
- package/lib/WABinary/constants.d.ts +61 -0
- package/lib/WABinary/constants.js +1467 -0
- package/lib/WABinary/decode.d.ts +9 -0
- package/lib/WABinary/decode.js +262 -0
- package/lib/WABinary/encode.d.ts +2 -0
- package/lib/WABinary/encode.js +220 -0
- package/lib/WABinary/generic-utils.d.ts +74 -0
- package/lib/WABinary/generic-utils.js +277 -0
- package/lib/WABinary/index.d.ts +5 -0
- package/lib/WABinary/index.js +6 -0
- package/lib/WABinary/jid-utils.d.ts +28 -0
- package/lib/WABinary/jid-utils.js +96 -0
- package/lib/WABinary/types.d.ts +19 -0
- package/lib/WABinary/types.js +2 -0
- package/lib/WAM/BinaryInfo.d.ts +7 -0
- package/lib/WAM/BinaryInfo.js +10 -0
- package/lib/WAM/constants.d.ts +34926 -0
- package/lib/WAM/constants.js +22853 -0
- package/lib/WAM/encode.d.ts +1 -0
- package/lib/WAM/encode.js +150 -0
- package/lib/WAM/index.d.ts +3 -0
- package/lib/WAM/index.js +4 -0
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +36 -0
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +52 -0
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +29 -0
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +63 -0
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +12 -0
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +27 -0
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +12 -0
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +38 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +9 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +38 -0
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +51 -0
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +14 -0
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +29 -0
- package/lib/WAUSync/Protocols/index.d.ts +7 -0
- package/lib/WAUSync/Protocols/index.js +8 -0
- package/lib/WAUSync/USyncQuery.d.ts +20 -0
- package/lib/WAUSync/USyncQuery.js +122 -0
- package/lib/WAUSync/USyncUser.d.ts +16 -0
- package/lib/WAUSync/USyncUser.js +31 -0
- package/lib/WAUSync/index.d.ts +3 -0
- package/lib/WAUSync/index.js +4 -0
- package/lib/assets/wasm/loader.js +5 -0
- package/lib/assets/wasm/whatsapp.wasm +0 -0
- package/lib/assets/wasm/worker-modules.js +273 -0
- package/lib/index.d.ts +12 -0
- package/lib/index.js +20 -0
- package/package.json +175 -0
- package/postinstall-banner.js +56 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vanz@Add --- ported from Bail-master addons/stickerpack.ts.
|
|
3
|
+
*
|
|
4
|
+
* Adds support for building/sending WhatsApp Sticker Pack messages.
|
|
5
|
+
*
|
|
6
|
+
* Exports:
|
|
7
|
+
* - convertToWebP() — converts a Buffer, URL string, or Stream into a WebP
|
|
8
|
+
* sticker buffer (passthrough if already WebP), sharp → @napi-rs/image
|
|
9
|
+
* fallback chain, 512x512 'inside' fit, quality 80.
|
|
10
|
+
* - generateStickerPackId() — generates a random pack ID
|
|
11
|
+
* - buildStickerPackProto() — builds the proto-level payload for a StickerPackMessage
|
|
12
|
+
* - STICKER_PACK_MESSAGE_TYPE — the message type string 'sticker_pack'
|
|
13
|
+
* - prepareStickerPackMessageItsliaaa() — full ZIP-build + upload + thumbnail
|
|
14
|
+
* pipeline for a ready-to-send stickerPackMessage.
|
|
15
|
+
*
|
|
16
|
+
* NOTE: source imported isWebPBuffer/isAnimatedWebP from its own
|
|
17
|
+
* from-messages.ts, which is not part of this fork's port. Reimplemented
|
|
18
|
+
* below as small standalone binary-sniffing helpers (RIFF/WEBP header +
|
|
19
|
+
* ANIM chunk check) so this file has no external dependency on that addon.
|
|
20
|
+
*/
|
|
21
|
+
import { Boom } from '@hapi/boom';
|
|
22
|
+
import { zip } from 'fflate';
|
|
23
|
+
import { promises as fsPromises } from 'fs';
|
|
24
|
+
import { proto } from '../../WAProto/index.js';
|
|
25
|
+
import { sha256 } from './crypto.js';
|
|
26
|
+
import { generateMessageIDV2, unixTimestampSeconds } from './generics.js';
|
|
27
|
+
import { encryptedStream, getImageProcessingLibrary, getStream, toBuffer } from './messages-media.js';
|
|
28
|
+
/** True if `buffer` starts with a RIFF....WEBP container header. */
|
|
29
|
+
export const isWebPBuffer = (buffer) => {
|
|
30
|
+
return (buffer.length > 12 &&
|
|
31
|
+
buffer.toString('ascii', 0, 4) === 'RIFF' &&
|
|
32
|
+
buffer.toString('ascii', 8, 12) === 'WEBP');
|
|
33
|
+
};
|
|
34
|
+
/** True if a WebP buffer contains an ANIM chunk (animated sticker). */
|
|
35
|
+
export const isAnimatedWebP = (buffer) => {
|
|
36
|
+
return isWebPBuffer(buffer) && buffer.includes(Buffer.from('ANIM', 'ascii'));
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Convert a Buffer, URL string, or Stream into a WebP sticker buffer.
|
|
40
|
+
* If the input is already a valid WebP, it's returned untouched (and
|
|
41
|
+
* `isAnimated` reflects whether it's an animated WebP).
|
|
42
|
+
*/
|
|
43
|
+
export const convertToWebP = async (input) => {
|
|
44
|
+
const { stream } = await getStream(input);
|
|
45
|
+
const buffer = await toBuffer(stream);
|
|
46
|
+
if (isWebPBuffer(buffer)) {
|
|
47
|
+
return { buffer, isAnimated: isAnimatedWebP(buffer) };
|
|
48
|
+
}
|
|
49
|
+
const lib = await getImageProcessingLibrary();
|
|
50
|
+
const hasSharp = 'sharp' in lib && !!lib.sharp?.default;
|
|
51
|
+
const hasImage = 'image' in lib && !!lib.image?.Transformer;
|
|
52
|
+
if (!hasSharp && !hasImage) {
|
|
53
|
+
throw new Boom('No image processing library (sharp or @napi-rs/image) available for converting sticker to WebP.');
|
|
54
|
+
}
|
|
55
|
+
let webpBuffer;
|
|
56
|
+
if (hasSharp) {
|
|
57
|
+
webpBuffer = await lib.sharp
|
|
58
|
+
.default(buffer)
|
|
59
|
+
.resize(512, 512, { fit: 'inside' })
|
|
60
|
+
.webp({ quality: 80 })
|
|
61
|
+
.toBuffer();
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
webpBuffer = await new lib.image.Transformer(buffer).resize(512, 512).webp(80);
|
|
65
|
+
}
|
|
66
|
+
return { buffer: webpBuffer, isAnimated: false };
|
|
67
|
+
};
|
|
68
|
+
/** Generate a random sticker pack ID (16 hex chars). */
|
|
69
|
+
export const generateStickerPackId = () => {
|
|
70
|
+
const arr = new Uint8Array(8);
|
|
71
|
+
for (let i = 0; i < 8; i++)
|
|
72
|
+
arr[i] = Math.floor(Math.random() * 256);
|
|
73
|
+
return Array.from(arr)
|
|
74
|
+
.map((b) => b.toString(16).padStart(2, '0'))
|
|
75
|
+
.join('');
|
|
76
|
+
};
|
|
77
|
+
/** Build the proto-level stickerPackMessage payload (name/publisher/packId/description only). */
|
|
78
|
+
export const buildStickerPackProto = (pack) => ({
|
|
79
|
+
name: pack.name,
|
|
80
|
+
publisher: pack.publisher,
|
|
81
|
+
packId: pack.packId ?? generateStickerPackId(),
|
|
82
|
+
description: pack.description ?? ''
|
|
83
|
+
});
|
|
84
|
+
/** stickerPack message type marker — for use with getMediaType()-style dispatch. */
|
|
85
|
+
export const STICKER_PACK_MESSAGE_TYPE = 'sticker_pack';
|
|
86
|
+
const ITSL_CONCURRENCY_LIMIT = 15;
|
|
87
|
+
/**
|
|
88
|
+
* Build a complete, ready-to-send stickerPackMessage (ZIP built, encrypted,
|
|
89
|
+
* and uploaded) — full pipeline: per-sticker WebP conversion (15-way
|
|
90
|
+
* concurrency batching), 1MB per-sticker size limit, 60-sticker pack limit,
|
|
91
|
+
* cover→trayIcon-in-ZIP, and a separate 252×252 JPEG thumbnail upload.
|
|
92
|
+
*/
|
|
93
|
+
export const prepareStickerPackMessageItsliaaa = async (message, options) => {
|
|
94
|
+
// Vanz@Fix (bug 62): packId was never destructured here, so a caller-supplied
|
|
95
|
+
// pack ID was silently dropped and a fresh one always generated below.
|
|
96
|
+
const { cover, stickers = [], name = '📦 Sticker Pack', publisher = 'GitHub: itsliaaa', description = '🏷️ itsliaaa/baileys', packId } = message;
|
|
97
|
+
if (stickers.length > 60) {
|
|
98
|
+
throw new Boom('Sticker pack exceeds the maximum limit of 60 stickers', { statusCode: 400 });
|
|
99
|
+
}
|
|
100
|
+
if (stickers.length === 0) {
|
|
101
|
+
throw new Boom('Sticker pack must contain at least one sticker', { statusCode: 400 });
|
|
102
|
+
}
|
|
103
|
+
if (!cover) {
|
|
104
|
+
throw new Boom('Sticker pack must contain a cover', { statusCode: 400 });
|
|
105
|
+
}
|
|
106
|
+
const { logger } = options;
|
|
107
|
+
// Media caching (keyed by concatenated sticker URLs, if all stickers are URL-based)
|
|
108
|
+
let cacheableKey = false;
|
|
109
|
+
if (stickers.length && options.mediaCache) {
|
|
110
|
+
const urls = [];
|
|
111
|
+
for (const s of stickers) {
|
|
112
|
+
const data = s.data;
|
|
113
|
+
if (typeof data === 'object' && data?.url)
|
|
114
|
+
urls.push(data.url);
|
|
115
|
+
}
|
|
116
|
+
if (urls.length > 0)
|
|
117
|
+
cacheableKey = 'sticker:' + urls.join('@');
|
|
118
|
+
}
|
|
119
|
+
if (cacheableKey) {
|
|
120
|
+
const mediaBuff = await options.mediaCache.get(cacheableKey);
|
|
121
|
+
if (mediaBuff) {
|
|
122
|
+
logger?.debug({ cacheableKey }, 'got media cache hit');
|
|
123
|
+
return proto.Message.StickerPackMessage.decode(mediaBuff);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const lib = await getImageProcessingLibrary();
|
|
127
|
+
const hasSharp = 'sharp' in lib && !!lib.sharp?.default;
|
|
128
|
+
const hasImage = 'image' in lib && !!lib.image?.Transformer;
|
|
129
|
+
if (!hasSharp && !hasImage) {
|
|
130
|
+
throw new Boom('No image processing library (sharp or @napi-rs/image) available for converting sticker to WebP.');
|
|
131
|
+
}
|
|
132
|
+
const stickerPackIdValue = packId ?? generateMessageIDV2();
|
|
133
|
+
const stickerData = {};
|
|
134
|
+
const stickerMetadata = new Array(stickers.length);
|
|
135
|
+
for (let i = 0; i < stickers.length; i += ITSL_CONCURRENCY_LIMIT) {
|
|
136
|
+
const chunkEnd = Math.min(i + ITSL_CONCURRENCY_LIMIT, stickers.length);
|
|
137
|
+
const promises = [];
|
|
138
|
+
for (let j = i; j < chunkEnd; j++) {
|
|
139
|
+
promises.push((async (index) => {
|
|
140
|
+
const sticker = stickers[index];
|
|
141
|
+
const { stream } = await getStream(sticker.data);
|
|
142
|
+
const buffer = await toBuffer(stream);
|
|
143
|
+
let webpBuffer;
|
|
144
|
+
let isAnimated = false;
|
|
145
|
+
if (isWebPBuffer(buffer)) {
|
|
146
|
+
webpBuffer = buffer;
|
|
147
|
+
isAnimated = isAnimatedWebP(buffer);
|
|
148
|
+
}
|
|
149
|
+
else if (hasSharp) {
|
|
150
|
+
webpBuffer = await lib.sharp
|
|
151
|
+
.default(buffer)
|
|
152
|
+
.resize(512, 512, { fit: 'inside' })
|
|
153
|
+
.webp({ quality: 80 })
|
|
154
|
+
.toBuffer();
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
webpBuffer = await new lib.image.Transformer(buffer).resize(512, 512).webp(80);
|
|
158
|
+
}
|
|
159
|
+
if (webpBuffer.length > 1024 * 1024) {
|
|
160
|
+
throw new Boom(`Sticker at index ${index} exceeds the 1MB size limit`, { statusCode: 400 });
|
|
161
|
+
}
|
|
162
|
+
const hash = sha256(webpBuffer).toString('base64').replace(/\//g, '-');
|
|
163
|
+
const fileName = `${hash}.webp`;
|
|
164
|
+
stickerData[fileName] = [new Uint8Array(webpBuffer), { level: 0 }];
|
|
165
|
+
stickerMetadata[index] = {
|
|
166
|
+
fileName,
|
|
167
|
+
mimetype: 'image/webp',
|
|
168
|
+
isAnimated,
|
|
169
|
+
emojis: sticker.emojis || ['✨'],
|
|
170
|
+
accessibilityLabel: sticker.accessibilityLabel || ''
|
|
171
|
+
};
|
|
172
|
+
})(j));
|
|
173
|
+
}
|
|
174
|
+
await Promise.all(promises);
|
|
175
|
+
}
|
|
176
|
+
const trayIconFileName = `${stickerPackIdValue}.webp`;
|
|
177
|
+
const { stream: coverStream } = await getStream(cover);
|
|
178
|
+
const coverBuffer = await toBuffer(coverStream);
|
|
179
|
+
let coverWebpBuffer;
|
|
180
|
+
if (isWebPBuffer(coverBuffer)) {
|
|
181
|
+
coverWebpBuffer = coverBuffer;
|
|
182
|
+
}
|
|
183
|
+
else if (hasSharp) {
|
|
184
|
+
coverWebpBuffer = await lib.sharp
|
|
185
|
+
.default(coverBuffer)
|
|
186
|
+
.resize(512, 512, { fit: 'inside' })
|
|
187
|
+
.webp({ quality: 80 })
|
|
188
|
+
.toBuffer();
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
coverWebpBuffer = await new lib.image.Transformer(coverBuffer).resize(512, 512).webp(80);
|
|
192
|
+
}
|
|
193
|
+
stickerData[trayIconFileName] = [new Uint8Array(coverWebpBuffer), { level: 0 }];
|
|
194
|
+
const zipBuffer = await new Promise((resolve, reject) => {
|
|
195
|
+
zip(stickerData, (error, data) => (error ? reject(error) : resolve(Buffer.from(data))));
|
|
196
|
+
});
|
|
197
|
+
const stickerPackUpload = await encryptedStream(zipBuffer, 'sticker-pack', { logger, opts: options.options });
|
|
198
|
+
let stickerPackUploadResult;
|
|
199
|
+
try {
|
|
200
|
+
stickerPackUploadResult = await options.upload(stickerPackUpload.encFilePath, {
|
|
201
|
+
fileEncSha256B64: stickerPackUpload.fileEncSha256.toString('base64'),
|
|
202
|
+
mediaType: 'sticker-pack',
|
|
203
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
finally {
|
|
207
|
+
fsPromises.unlink(stickerPackUpload.encFilePath).catch(() => logger?.warn('failed to remove tmp file'));
|
|
208
|
+
}
|
|
209
|
+
const obj = {
|
|
210
|
+
name,
|
|
211
|
+
publisher,
|
|
212
|
+
stickerPackId: stickerPackIdValue,
|
|
213
|
+
packDescription: description,
|
|
214
|
+
stickerPackOrigin: proto.Message.StickerPackMessage.StickerPackOrigin.USER_CREATED,
|
|
215
|
+
stickerPackSize: zipBuffer.length,
|
|
216
|
+
stickers: stickerMetadata,
|
|
217
|
+
fileSha256: stickerPackUpload.fileSha256,
|
|
218
|
+
fileEncSha256: stickerPackUpload.fileEncSha256,
|
|
219
|
+
mediaKey: stickerPackUpload.mediaKey,
|
|
220
|
+
directPath: stickerPackUploadResult.directPath,
|
|
221
|
+
fileLength: stickerPackUpload.fileLength,
|
|
222
|
+
mediaKeyTimestamp: unixTimestampSeconds(),
|
|
223
|
+
trayIconFileName
|
|
224
|
+
};
|
|
225
|
+
try {
|
|
226
|
+
let thumbnailBuffer;
|
|
227
|
+
if (hasSharp) {
|
|
228
|
+
thumbnailBuffer = await lib.sharp.default(coverBuffer).resize(252, 252).jpeg().toBuffer();
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
// hasImage is guaranteed here since hasSharp||hasImage is enforced above.
|
|
232
|
+
// Vanz@Fix (bug 63): removed a jimp fallback branch that could never be
|
|
233
|
+
// reached (the earlier guard already requires sharp or image), and which
|
|
234
|
+
// main sticker/cover conversion above doesn't support anyway — it was
|
|
235
|
+
// dead code presenting a false sense of a jimp-only code path.
|
|
236
|
+
thumbnailBuffer = await new lib.image.Transformer(coverBuffer).resize(252, 252).jpeg();
|
|
237
|
+
}
|
|
238
|
+
if (!thumbnailBuffer || thumbnailBuffer.length === 0) {
|
|
239
|
+
throw new Error('Failed to generate thumbnail buffer');
|
|
240
|
+
}
|
|
241
|
+
const thumbUpload = await encryptedStream(thumbnailBuffer, 'thumbnail-sticker-pack', {
|
|
242
|
+
logger,
|
|
243
|
+
opts: options.options,
|
|
244
|
+
mediaKey: stickerPackUpload.mediaKey
|
|
245
|
+
});
|
|
246
|
+
let thumbUploadResult;
|
|
247
|
+
try {
|
|
248
|
+
thumbUploadResult = await options.upload(thumbUpload.encFilePath, {
|
|
249
|
+
fileEncSha256B64: thumbUpload.fileEncSha256.toString('base64'),
|
|
250
|
+
mediaType: 'thumbnail-sticker-pack',
|
|
251
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
finally {
|
|
255
|
+
fsPromises.unlink(thumbUpload.encFilePath).catch(() => logger?.warn('failed to remove tmp file'));
|
|
256
|
+
}
|
|
257
|
+
Object.assign(obj, {
|
|
258
|
+
thumbnailDirectPath: thumbUploadResult.directPath,
|
|
259
|
+
thumbnailSha256: thumbUpload.fileSha256,
|
|
260
|
+
thumbnailEncSha256: thumbUpload.fileEncSha256,
|
|
261
|
+
thumbnailHeight: 252,
|
|
262
|
+
thumbnailWidth: 252,
|
|
263
|
+
imageDataHash: sha256(thumbnailBuffer).toString('base64')
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
logger?.warn(`Thumbnail generation failed: ${error}`);
|
|
268
|
+
}
|
|
269
|
+
if (cacheableKey) {
|
|
270
|
+
logger?.debug({ cacheableKey }, 'set cache (background)');
|
|
271
|
+
options.mediaCache.set(cacheableKey, Buffer.from(proto.Message.StickerPackMessage.encode(obj).finish()));
|
|
272
|
+
}
|
|
273
|
+
return proto.Message.StickerPackMessage.fromObject(obj);
|
|
274
|
+
};
|
|
275
|
+
//# sourceMappingURL=stickerpack.js.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { proto } from '../../WAProto/index.js';
|
|
2
|
+
import { isLidUser, isPnUser } from '../WABinary/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Process contactAction and return events to emit.
|
|
5
|
+
* Pure function - no side effects.
|
|
6
|
+
*/
|
|
7
|
+
export const processContactAction = (action, id, logger) => {
|
|
8
|
+
const results = [];
|
|
9
|
+
if (!id) {
|
|
10
|
+
logger?.warn({ hasFullName: !!action.fullName, hasLidJid: !!action.lidJid, hasPnJid: !!action.pnJid }, 'contactAction sync: missing id in index');
|
|
11
|
+
return results;
|
|
12
|
+
}
|
|
13
|
+
const lidJid = action.lidJid;
|
|
14
|
+
const idIsPn = isPnUser(id);
|
|
15
|
+
// PN is in index[1], not in contactAction.pnJid which is usually null
|
|
16
|
+
const phoneNumber = idIsPn ? id : action.pnJid || undefined;
|
|
17
|
+
// Always emit contacts.upsert
|
|
18
|
+
results.push({
|
|
19
|
+
event: 'contacts.upsert',
|
|
20
|
+
data: [
|
|
21
|
+
{
|
|
22
|
+
id,
|
|
23
|
+
name: action.fullName || action.firstName || action.username || undefined,
|
|
24
|
+
username: action.username || undefined,
|
|
25
|
+
lid: lidJid || undefined,
|
|
26
|
+
phoneNumber
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
});
|
|
30
|
+
// Emit lid-mapping.update if we have valid LID-PN pair
|
|
31
|
+
if (lidJid && isLidUser(lidJid) && idIsPn) {
|
|
32
|
+
results.push({
|
|
33
|
+
event: 'lid-mapping.update',
|
|
34
|
+
data: { lid: lidJid, pn: id }
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return results;
|
|
38
|
+
};
|
|
39
|
+
export const emitSyncActionResults = (ev, results) => {
|
|
40
|
+
for (const result of results) {
|
|
41
|
+
if (result.event === 'contacts.upsert') {
|
|
42
|
+
ev.emit('contacts.upsert', result.data);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
ev.emit('lid-mapping.update', result.data);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=sync-action-utils.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Read the persisted tctoken JID index and return its entries (never contains the sentinel key itself). */
|
|
2
|
+
export function readTcTokenIndex(keys: any): Promise<any[]>;
|
|
3
|
+
/** Build a SignalDataSet fragment that writes the merged index (persisted ∪ added) under the sentinel key. */
|
|
4
|
+
export function buildMergedTcTokenIndexWrite(keys: any, addedJids: any): Promise<{
|
|
5
|
+
__index: {
|
|
6
|
+
token: any;
|
|
7
|
+
};
|
|
8
|
+
}>;
|
|
9
|
+
export function isTcTokenExpired(timestamp: any): boolean;
|
|
10
|
+
export function shouldSendNewTcToken(senderTimestamp: any): boolean;
|
|
11
|
+
/** Resolve JID to LID for tctoken storage (WA Web stores under LID) */
|
|
12
|
+
export function resolveTcTokenJid(jid: any, getLIDForPN: any): Promise<any>;
|
|
13
|
+
/** Resolve target JID for issuing privacy token based on AB prop 14303 */
|
|
14
|
+
export function resolveIssuanceJid(jid: any, issueToLid: any, getLIDForPN: any, getPNForLID: any): Promise<any>;
|
|
15
|
+
export function buildTcTokenFromJid({ authState, jid, baseContent, getLIDForPN }: {
|
|
16
|
+
authState: any;
|
|
17
|
+
jid: any;
|
|
18
|
+
baseContent?: never[] | undefined;
|
|
19
|
+
getLIDForPN: any;
|
|
20
|
+
}): Promise<any[] | undefined>;
|
|
21
|
+
export function storeTcTokensFromIqResult({ result, fallbackJid, keys, getLIDForPN, onNewJidStored }: {
|
|
22
|
+
result: any;
|
|
23
|
+
fallbackJid: any;
|
|
24
|
+
keys: any;
|
|
25
|
+
getLIDForPN: any;
|
|
26
|
+
onNewJidStored: any;
|
|
27
|
+
}): Promise<void>;
|
|
28
|
+
/** Sentinel key under `tctoken` store holding a JSON array of tracked storage JIDs for cross-session pruning. */
|
|
29
|
+
export const TC_TOKEN_INDEX_KEY: "__index";
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { getBinaryNodeChild, getBinaryNodeChildren, isHostedLidUser, isHostedPnUser, isJidMetaAI, isLidUser, isPnUser, jidNormalizedUser } from '../WABinary/index.js';
|
|
2
|
+
// Same phone-number pattern as WABinary's isJidBot, applied against the user
|
|
3
|
+
// part so the check is invariant to @c.us ↔ @s.whatsapp.net normalization.
|
|
4
|
+
const BOT_PHONE_REGEX = /^1313555\d{4}$|^131655500\d{2}$/;
|
|
5
|
+
/**
|
|
6
|
+
* Mirrors WA Web's `Wid.isRegularUser()` (user ∧ ¬PSA ∧ ¬Bot). Used to gate tctoken
|
|
7
|
+
* storage against malformed notifications — WA Web filters server-side but we
|
|
8
|
+
* defend here for parity with `WAWebSetTcTokenChatAction.handleIncomingTcToken`.
|
|
9
|
+
* Works for both pre- and post-normalized JIDs (`@c.us` vs `@s.whatsapp.net`).
|
|
10
|
+
*/
|
|
11
|
+
function isRegularUser(jid) {
|
|
12
|
+
if (!jid)
|
|
13
|
+
return false;
|
|
14
|
+
const user = jid.split('@')[0] ?? '';
|
|
15
|
+
if (user === '0')
|
|
16
|
+
return false; // PSA
|
|
17
|
+
if (BOT_PHONE_REGEX.test(user))
|
|
18
|
+
return false; // Bot by phone pattern
|
|
19
|
+
if (isJidMetaAI(jid))
|
|
20
|
+
return false; // MetaAI (@bot server)
|
|
21
|
+
return !!(isPnUser(jid) || isLidUser(jid) || isHostedPnUser(jid) || isHostedLidUser(jid) || jid.endsWith('@c.us'));
|
|
22
|
+
}
|
|
23
|
+
const TC_TOKEN_BUCKET_DURATION = 604800; // 7 days
|
|
24
|
+
const TC_TOKEN_NUM_BUCKETS = 4; // ~28-day rolling window
|
|
25
|
+
/** Sentinel key under `tctoken` store holding a JSON array of tracked storage JIDs for cross-session pruning. */
|
|
26
|
+
export const TC_TOKEN_INDEX_KEY = '__index';
|
|
27
|
+
/** Read the persisted tctoken JID index and return its entries (never contains the sentinel key itself). */
|
|
28
|
+
export async function readTcTokenIndex(keys) {
|
|
29
|
+
const data = await keys.get('tctoken', [TC_TOKEN_INDEX_KEY]);
|
|
30
|
+
const entry = data[TC_TOKEN_INDEX_KEY];
|
|
31
|
+
if (!entry?.token?.length)
|
|
32
|
+
return [];
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse(Buffer.from(entry.token).toString());
|
|
35
|
+
if (!Array.isArray(parsed))
|
|
36
|
+
return [];
|
|
37
|
+
return parsed.filter((j) => typeof j === 'string' && j.length > 0 && j !== TC_TOKEN_INDEX_KEY);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Build a SignalDataSet fragment that writes the merged index (persisted ∪ added) under the sentinel key. */
|
|
44
|
+
export async function buildMergedTcTokenIndexWrite(keys, addedJids) {
|
|
45
|
+
const persisted = await readTcTokenIndex(keys);
|
|
46
|
+
const merged = new Set(persisted);
|
|
47
|
+
for (const jid of addedJids) {
|
|
48
|
+
if (jid && jid !== TC_TOKEN_INDEX_KEY)
|
|
49
|
+
merged.add(jid);
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
[TC_TOKEN_INDEX_KEY]: { token: Buffer.from(JSON.stringify([...merged])) }
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
// WA Web has separate sender/receiver AB props for these but they're identical today
|
|
56
|
+
export function isTcTokenExpired(timestamp) {
|
|
57
|
+
if (timestamp === null || timestamp === undefined)
|
|
58
|
+
return true;
|
|
59
|
+
const ts = typeof timestamp === 'string' ? parseInt(timestamp) : timestamp;
|
|
60
|
+
if (isNaN(ts))
|
|
61
|
+
return true;
|
|
62
|
+
const now = Math.floor(Date.now() / 1000);
|
|
63
|
+
const currentBucket = Math.floor(now / TC_TOKEN_BUCKET_DURATION);
|
|
64
|
+
const cutoffBucket = currentBucket - (TC_TOKEN_NUM_BUCKETS - 1);
|
|
65
|
+
const cutoffTimestamp = cutoffBucket * TC_TOKEN_BUCKET_DURATION;
|
|
66
|
+
return ts < cutoffTimestamp;
|
|
67
|
+
}
|
|
68
|
+
export function shouldSendNewTcToken(senderTimestamp) {
|
|
69
|
+
if (senderTimestamp === undefined)
|
|
70
|
+
return true;
|
|
71
|
+
const now = Math.floor(Date.now() / 1000);
|
|
72
|
+
const currentBucket = Math.floor(now / TC_TOKEN_BUCKET_DURATION);
|
|
73
|
+
const senderBucket = Math.floor(senderTimestamp / TC_TOKEN_BUCKET_DURATION);
|
|
74
|
+
return currentBucket > senderBucket;
|
|
75
|
+
}
|
|
76
|
+
/** Resolve JID to LID for tctoken storage (WA Web stores under LID) */
|
|
77
|
+
export async function resolveTcTokenJid(jid, getLIDForPN) {
|
|
78
|
+
if (isLidUser(jid))
|
|
79
|
+
return jid;
|
|
80
|
+
const lid = await getLIDForPN(jid);
|
|
81
|
+
return lid ?? jid;
|
|
82
|
+
}
|
|
83
|
+
/** Resolve target JID for issuing privacy token based on AB prop 14303 */
|
|
84
|
+
export async function resolveIssuanceJid(jid, issueToLid, getLIDForPN, getPNForLID) {
|
|
85
|
+
if (issueToLid) {
|
|
86
|
+
if (isLidUser(jid))
|
|
87
|
+
return jid;
|
|
88
|
+
const lid = await getLIDForPN(jid);
|
|
89
|
+
return lid ?? jid;
|
|
90
|
+
}
|
|
91
|
+
if (!isLidUser(jid))
|
|
92
|
+
return jid;
|
|
93
|
+
if (getPNForLID) {
|
|
94
|
+
const pn = await getPNForLID(jid);
|
|
95
|
+
return pn ?? jid;
|
|
96
|
+
}
|
|
97
|
+
return jid;
|
|
98
|
+
}
|
|
99
|
+
export async function buildTcTokenFromJid({ authState, jid, baseContent = [], getLIDForPN }) {
|
|
100
|
+
try {
|
|
101
|
+
const storageJid = await resolveTcTokenJid(jid, getLIDForPN);
|
|
102
|
+
const tcTokenData = await authState.keys.get('tctoken', [storageJid]);
|
|
103
|
+
const entry = tcTokenData?.[storageJid];
|
|
104
|
+
const tcTokenBuffer = entry?.token;
|
|
105
|
+
if (!tcTokenBuffer?.length || isTcTokenExpired(entry?.timestamp)) {
|
|
106
|
+
if (tcTokenBuffer) {
|
|
107
|
+
// Preserve senderTimestamp so shouldSendNewTcToken() keeps its dedupe state
|
|
108
|
+
// after we drop the unusable peer token. Only wipe the record entirely when
|
|
109
|
+
// there's nothing worth keeping.
|
|
110
|
+
const cleared = entry?.senderTimestamp !== undefined
|
|
111
|
+
? { token: Buffer.alloc(0), senderTimestamp: entry.senderTimestamp }
|
|
112
|
+
: null;
|
|
113
|
+
await authState.keys.set({ tctoken: { [storageJid]: cleared } });
|
|
114
|
+
}
|
|
115
|
+
return baseContent.length > 0 ? baseContent : undefined;
|
|
116
|
+
}
|
|
117
|
+
baseContent.push({
|
|
118
|
+
tag: 'tctoken',
|
|
119
|
+
attrs: {},
|
|
120
|
+
content: tcTokenBuffer
|
|
121
|
+
});
|
|
122
|
+
return baseContent;
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
return baseContent.length > 0 ? baseContent : undefined;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
export async function storeTcTokensFromIqResult({ result, fallbackJid, keys, getLIDForPN, onNewJidStored }) {
|
|
129
|
+
const tokensNode = getBinaryNodeChild(result, 'tokens');
|
|
130
|
+
if (!tokensNode)
|
|
131
|
+
return;
|
|
132
|
+
const tokenNodes = getBinaryNodeChildren(tokensNode, 'token');
|
|
133
|
+
for (const tokenNode of tokenNodes) {
|
|
134
|
+
if (tokenNode.attrs.type !== 'trusted_contact' || !(tokenNode.content instanceof Uint8Array)) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
// In notifications tokenNode.attrs.jid is your own device JID, not the sender's
|
|
138
|
+
const rawJid = jidNormalizedUser(fallbackJid || tokenNode.attrs.jid);
|
|
139
|
+
if (!isRegularUser(rawJid))
|
|
140
|
+
continue;
|
|
141
|
+
const storageJid = await resolveTcTokenJid(rawJid, getLIDForPN);
|
|
142
|
+
const existingTcData = await keys.get('tctoken', [storageJid]);
|
|
143
|
+
const existingEntry = existingTcData[storageJid];
|
|
144
|
+
const existingTs = existingEntry?.timestamp ? Number(existingEntry.timestamp) : 0;
|
|
145
|
+
const incomingTs = tokenNode.attrs.t ? Number(tokenNode.attrs.t) : 0;
|
|
146
|
+
// timestamp-less tokens would be immediately expired
|
|
147
|
+
if (!incomingTs)
|
|
148
|
+
continue;
|
|
149
|
+
if (existingTs > 0 && existingTs > incomingTs)
|
|
150
|
+
continue;
|
|
151
|
+
await keys.set({
|
|
152
|
+
tctoken: {
|
|
153
|
+
[storageJid]: {
|
|
154
|
+
...existingEntry,
|
|
155
|
+
token: Buffer.from(tokenNode.content),
|
|
156
|
+
timestamp: tokenNode.attrs.t
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
onNewJidStored?.(storageJid);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=tc-token-utils.js.map
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export interface TemplateVariable {
|
|
2
|
+
name: string;
|
|
3
|
+
defaultValue?: string;
|
|
4
|
+
required: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface MessageTemplate {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
content: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
category?: string;
|
|
12
|
+
variables: TemplateVariable[];
|
|
13
|
+
createdAt: Date;
|
|
14
|
+
updatedAt: Date;
|
|
15
|
+
}
|
|
16
|
+
export type TemplateData = Record<string, string | number | undefined | null>;
|
|
17
|
+
export declare class TemplateManager {
|
|
18
|
+
private templates;
|
|
19
|
+
private generateId;
|
|
20
|
+
private extractVariables;
|
|
21
|
+
create(options: {
|
|
22
|
+
id?: string;
|
|
23
|
+
name: string;
|
|
24
|
+
content: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
category?: string;
|
|
27
|
+
}): MessageTemplate;
|
|
28
|
+
get(id: string): MessageTemplate | undefined;
|
|
29
|
+
getByName(name: string): MessageTemplate | undefined;
|
|
30
|
+
getAll(): MessageTemplate[];
|
|
31
|
+
getByCategory(category: string): MessageTemplate[];
|
|
32
|
+
update(id: string, updates: Partial<MessageTemplate>): MessageTemplate | undefined;
|
|
33
|
+
delete(id: string): boolean;
|
|
34
|
+
renderContent(content: string, data?: TemplateData): string;
|
|
35
|
+
render(id: string, data?: TemplateData): string;
|
|
36
|
+
validate(id: string, data: TemplateData): {
|
|
37
|
+
valid: boolean;
|
|
38
|
+
missing: string[];
|
|
39
|
+
};
|
|
40
|
+
export(): string;
|
|
41
|
+
import(json: string, overwrite?: boolean): number;
|
|
42
|
+
}
|
|
43
|
+
export declare const PRESET_TEMPLATES: {
|
|
44
|
+
ORDER_CONFIRMATION: {
|
|
45
|
+
name: string;
|
|
46
|
+
category: string;
|
|
47
|
+
content: string;
|
|
48
|
+
};
|
|
49
|
+
WELCOME: {
|
|
50
|
+
name: string;
|
|
51
|
+
category: string;
|
|
52
|
+
content: string;
|
|
53
|
+
};
|
|
54
|
+
REMINDER: {
|
|
55
|
+
name: string;
|
|
56
|
+
category: string;
|
|
57
|
+
content: string;
|
|
58
|
+
};
|
|
59
|
+
SUPPORT_TICKET: {
|
|
60
|
+
name: string;
|
|
61
|
+
category: string;
|
|
62
|
+
content: string;
|
|
63
|
+
};
|
|
64
|
+
BIRTHDAY: {
|
|
65
|
+
name: string;
|
|
66
|
+
category: string;
|
|
67
|
+
content: string;
|
|
68
|
+
};
|
|
69
|
+
INVOICE: {
|
|
70
|
+
name: string;
|
|
71
|
+
category: string;
|
|
72
|
+
content: string;
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
export declare const createTemplateManager: (includePresets?: boolean) => TemplateManager;
|
|
76
|
+
export declare const renderTemplate: (content: string, data?: TemplateData) => string;
|