@crysnovax/baileys 2.6.0 → 2.6.2
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 +54 -0
- package/lib/Socket/chats.js +16 -2
- package/lib/Socket/chats.js[past] +1213 -0
- package/lib/Utils/bot-planning-replay.js +57 -92
- package/lib/Utils/messages-media.js +74 -0
- package/lib/Utils/messages-media.js[past] +842 -0
- package/lib/Utils/meta-compositing.js +61 -77
- package/package.json +1 -1
|
@@ -2,22 +2,17 @@
|
|
|
2
2
|
* Lia@Changes [WIP]
|
|
3
3
|
* botPlanningReplay — Meta AI-style live reasoning feed.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* with NO "edited" badge, using the same delete-resend flow from
|
|
9
|
-
* meta-compositing.
|
|
10
|
-
*
|
|
11
|
-
* replayPlanning(sock, jid, steps, finalContent, options)
|
|
5
|
+
* UNIVERSAL VERSION: Works on ALL WhatsApp clients.
|
|
6
|
+
* - Native Meta AI rendering for compatible clients
|
|
7
|
+
* - Plain text fallback for regular clients (no "Update WhatsApp")
|
|
12
8
|
*
|
|
13
9
|
* Flow:
|
|
14
|
-
* Send placeholder (all steps
|
|
15
|
-
* → edit: step[0] → DONE
|
|
16
|
-
* → edit: step[1] → DONE
|
|
10
|
+
* Send placeholder (all steps pending)
|
|
11
|
+
* → edit: step[0] → DONE
|
|
12
|
+
* → edit: step[1] → DONE
|
|
17
13
|
* → ...
|
|
18
|
-
* → edit: step[n] → DONE (finalPauseMs)
|
|
19
14
|
* → delete placeholder
|
|
20
|
-
* → send final
|
|
15
|
+
* → send final message fresh
|
|
21
16
|
*
|
|
22
17
|
* If you use or copy this code, please credit @crysnovax/bailey.
|
|
23
18
|
*/
|
|
@@ -26,46 +21,32 @@ import { proto } from '../../WAProto/index.js';
|
|
|
26
21
|
import {
|
|
27
22
|
buildCompositingPlaceholder,
|
|
28
23
|
buildProgressIndicator,
|
|
24
|
+
buildPlainPlaceholder,
|
|
29
25
|
PlanningStepStatus
|
|
30
26
|
} from './meta-compositing.js';
|
|
31
27
|
import { botMetadataSignature, botMetadataCertificate } from './rich-message-utils.js';
|
|
32
28
|
import { BOT_RENDERING_CONFIG_METADATA } from '../Defaults/index.js';
|
|
33
29
|
import { delay } from './generics.js';
|
|
34
30
|
|
|
35
|
-
// ─── Internal:
|
|
31
|
+
// ─── Internal: build native Meta AI frame ──────────────────────────────────
|
|
36
32
|
const buildReplayFrame = (description, steps, placeholderText = '') => {
|
|
37
33
|
return buildCompositingPlaceholder({ description, steps, placeholderText });
|
|
38
34
|
};
|
|
39
35
|
|
|
40
|
-
// ─── Internal:
|
|
36
|
+
// ─── Internal: edit planning bubble (native Meta AI) ───────────────────────
|
|
41
37
|
const editPlanningBubble = async (sock, jid, key, description, steps, placeholderText) => {
|
|
42
38
|
const updated = buildReplayFrame(description, steps, placeholderText);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
await sock.sendMessage(jid, { raw: true, edit: key, ...updated });
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// ─── Internal: edit plain text bubble (universal) ──────────────────────────
|
|
43
|
+
const editPlainBubble = async (sock, jid, key, description, steps, placeholderText) => {
|
|
44
|
+
const updated = buildPlainPlaceholder(description, steps, placeholderText);
|
|
45
|
+
await sock.sendMessage(jid, { edit: key, ...updated });
|
|
49
46
|
};
|
|
50
47
|
|
|
51
48
|
/**
|
|
52
|
-
* replayPlanning — full live planning animation.
|
|
53
|
-
*
|
|
54
|
-
* @param {object} sock - Baileys socket
|
|
55
|
-
* @param {string} jid - Destination JID
|
|
56
|
-
* @param {Array} steps - Array of step definitions:
|
|
57
|
-
* { title, body?, isReasoning?, isEnhancedSearch? }
|
|
58
|
-
* Status is managed automatically — do NOT pass status here.
|
|
59
|
-
* @param {object} finalContent - Rich content for the final message.
|
|
60
|
-
* Same as sendMessage: { code, table, text, richResponse… }
|
|
61
|
-
* @param {object} [options]
|
|
62
|
-
* @param {string} [options.description] - Top label while thinking. Default: 'Thinking…'
|
|
63
|
-
* @param {string} [options.placeholderText] - Body text shown in bubble while loading.
|
|
64
|
-
* @param {number} [options.stepDelayMs] - Ms between each step completing. Default: 900
|
|
65
|
-
* @param {number} [options.finalPauseMs] - Ms to hold after all steps done before cleanup. Default: 600
|
|
66
|
-
* @param {boolean} [options.abortOnDisconnect] - Stop replay loop if socket closes. Default: true
|
|
67
|
-
* @param {object} [options.sendOptions] - Extra options for final sendMessage call.
|
|
68
|
-
* @returns {Promise<object>} - The final sent WAMessage
|
|
49
|
+
* replayPlanning — full live planning animation (UNIVERSAL).
|
|
69
50
|
*/
|
|
70
51
|
export const replayPlanning = async (sock, jid, steps, finalContent, {
|
|
71
52
|
description = 'Thinking…',
|
|
@@ -73,13 +54,13 @@ export const replayPlanning = async (sock, jid, steps, finalContent, {
|
|
|
73
54
|
stepDelayMs = 900,
|
|
74
55
|
finalPauseMs = 600,
|
|
75
56
|
abortOnDisconnect = true,
|
|
76
|
-
sendOptions = {}
|
|
57
|
+
sendOptions = {},
|
|
58
|
+
useNativeMeta = false
|
|
77
59
|
} = {}) => {
|
|
78
60
|
if (!steps?.length) {
|
|
79
61
|
throw new Error('replayPlanning: steps array must have at least one entry');
|
|
80
62
|
}
|
|
81
63
|
|
|
82
|
-
// ── Track abort state if socket disconnects mid-replay ───────────────────
|
|
83
64
|
let aborted = false;
|
|
84
65
|
const onClose = () => { aborted = true; };
|
|
85
66
|
if (abortOnDisconnect) {
|
|
@@ -88,112 +69,96 @@ export const replayPlanning = async (sock, jid, steps, finalContent, {
|
|
|
88
69
|
});
|
|
89
70
|
}
|
|
90
71
|
|
|
91
|
-
//
|
|
72
|
+
// Show typing
|
|
73
|
+
await sock.sendPresenceUpdate('composing', jid);
|
|
74
|
+
|
|
75
|
+
// Build initial steps (all pending)
|
|
92
76
|
const initialSteps = steps.map(step => ({
|
|
93
77
|
...step,
|
|
94
78
|
status: PlanningStepStatus.IN_PROGRESS
|
|
95
79
|
}));
|
|
96
80
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
81
|
+
// Determine mode
|
|
82
|
+
const isNative = useNativeMeta && false; // Always false for now — force universal
|
|
83
|
+
|
|
84
|
+
// Send initial placeholder
|
|
85
|
+
let placeholder;
|
|
86
|
+
if (isNative) {
|
|
87
|
+
placeholder = await sock.sendMessage(jid, {
|
|
88
|
+
raw: true,
|
|
89
|
+
...buildReplayFrame(description, initialSteps, placeholderText)
|
|
90
|
+
});
|
|
91
|
+
} else {
|
|
92
|
+
placeholder = await sock.sendMessage(jid,
|
|
93
|
+
buildPlainPlaceholder(description, initialSteps, placeholderText)
|
|
94
|
+
);
|
|
95
|
+
}
|
|
101
96
|
|
|
102
97
|
const key = placeholder?.key;
|
|
103
98
|
|
|
104
|
-
//
|
|
99
|
+
// Replay loop
|
|
105
100
|
try {
|
|
106
101
|
const currentSteps = [...initialSteps];
|
|
107
102
|
|
|
108
103
|
for (let i = 0; i < currentSteps.length; i++) {
|
|
109
104
|
if (aborted) break;
|
|
110
|
-
|
|
111
105
|
await delay(stepDelayMs);
|
|
112
106
|
if (aborted) break;
|
|
113
107
|
|
|
114
|
-
|
|
115
|
-
currentSteps[i] = {
|
|
116
|
-
...currentSteps[i],
|
|
117
|
-
status: PlanningStepStatus.DONE
|
|
118
|
-
};
|
|
108
|
+
currentSteps[i] = { ...currentSteps[i], status: PlanningStepStatus.DONE };
|
|
119
109
|
|
|
120
110
|
if (key) {
|
|
121
|
-
|
|
122
|
-
sock, jid, key,
|
|
123
|
-
|
|
124
|
-
|
|
111
|
+
if (isNative) {
|
|
112
|
+
await editPlanningBubble(sock, jid, key, description, currentSteps, placeholderText);
|
|
113
|
+
} else {
|
|
114
|
+
await editPlainBubble(sock, jid, key, description, currentSteps, placeholderText);
|
|
115
|
+
}
|
|
125
116
|
}
|
|
126
117
|
}
|
|
127
118
|
|
|
128
|
-
// ── 3. Hold with all steps DONE so user sees completion ──────────────
|
|
129
119
|
if (!aborted && finalPauseMs > 0) {
|
|
130
120
|
await delay(finalPauseMs);
|
|
131
121
|
}
|
|
132
122
|
|
|
133
|
-
// ── 4. Delete placeholder silently ───────────────────────────────────
|
|
134
123
|
if (key && !aborted) {
|
|
135
124
|
await sock.sendMessage(jid, { delete: key });
|
|
136
125
|
}
|
|
137
126
|
} catch (err) {
|
|
138
|
-
|
|
139
|
-
try {
|
|
140
|
-
if (key) await sock.sendMessage(jid, { delete: key });
|
|
141
|
-
} catch (_) {}
|
|
127
|
+
try { if (key) await sock.sendMessage(jid, { delete: key }); } catch (_) {}
|
|
142
128
|
}
|
|
143
129
|
|
|
144
|
-
|
|
130
|
+
await sock.sendPresenceUpdate('paused', jid);
|
|
131
|
+
|
|
132
|
+
// Skip final send if _skipFinalSend is set (for replayPlanningOnly)
|
|
133
|
+
if (sendOptions._skipFinalSend) return placeholder;
|
|
134
|
+
|
|
145
135
|
return sock.sendMessage(jid, finalContent, sendOptions);
|
|
146
136
|
};
|
|
147
137
|
|
|
148
138
|
/**
|
|
149
|
-
* replayPlanningOnly —
|
|
150
|
-
* Use when you want to control the final send yourself.
|
|
151
|
-
*
|
|
152
|
-
* Returns { key } of the placeholder (already deleted on completion).
|
|
153
|
-
*
|
|
154
|
-
* @param {object} sock
|
|
155
|
-
* @param {string} jid
|
|
156
|
-
* @param {Array} steps
|
|
157
|
-
* @param {object} [options] - Same options as replayPlanning minus sendOptions
|
|
158
|
-
* @returns {Promise<void>}
|
|
139
|
+
* replayPlanningOnly — animation WITHOUT final message.
|
|
159
140
|
*/
|
|
160
141
|
export const replayPlanningOnly = async (sock, jid, steps, options = {}) => {
|
|
161
142
|
return replayPlanning(sock, jid, steps, null, {
|
|
162
143
|
...options,
|
|
163
|
-
_skipFinalSend: true
|
|
144
|
+
sendOptions: { ...options.sendOptions, _skipFinalSend: true }
|
|
164
145
|
});
|
|
165
146
|
};
|
|
166
147
|
|
|
167
148
|
/**
|
|
168
|
-
* buildReasoningSteps —
|
|
169
|
-
* Renders with the "reasoning" visual treatment in the Meta bubble.
|
|
170
|
-
*
|
|
171
|
-
* @example
|
|
172
|
-
* buildReasoningSteps(['Analyzing the problem…', 'Checking edge cases…'])
|
|
149
|
+
* buildReasoningSteps — steps with isReasoning: true.
|
|
173
150
|
*/
|
|
174
151
|
export const buildReasoningSteps = (titles) =>
|
|
175
152
|
titles.map(title => ({ title, isReasoning: true }));
|
|
176
153
|
|
|
177
154
|
/**
|
|
178
|
-
* buildSearchSteps —
|
|
179
|
-
* Renders with the "enhanced search" visual treatment.
|
|
180
|
-
*
|
|
181
|
-
* @example
|
|
182
|
-
* buildSearchSteps(['Searching the web…', 'Reading top results…'])
|
|
155
|
+
* buildSearchSteps — steps with isEnhancedSearch: true.
|
|
183
156
|
*/
|
|
184
157
|
export const buildSearchSteps = (titles) =>
|
|
185
158
|
titles.map(title => ({ title, isEnhancedSearch: true }));
|
|
186
159
|
|
|
187
160
|
/**
|
|
188
|
-
* mixedSteps —
|
|
189
|
-
* Pass an array of { title, type? } where type is 'reasoning' | 'search' | undefined.
|
|
190
|
-
*
|
|
191
|
-
* @example
|
|
192
|
-
* mixedSteps([
|
|
193
|
-
* { title: 'Understanding your question…', type: 'reasoning' },
|
|
194
|
-
* { title: 'Searching for data…', type: 'search' },
|
|
195
|
-
* { title: 'Writing the answer…' }
|
|
196
|
-
* ])
|
|
161
|
+
* mixedSteps — mix reasoning + search + plain steps.
|
|
197
162
|
*/
|
|
198
163
|
export const mixedSteps = (defs) =>
|
|
199
164
|
defs.map(({ title, body, type }) => ({
|
|
@@ -220,6 +220,80 @@ export const generateProfilePicture = async (mediaUpload, dimensions) => {
|
|
|
220
220
|
img: await img
|
|
221
221
|
};
|
|
222
222
|
};
|
|
223
|
+
// crysnovax@HD-Profile --- Like generateProfilePicture but skips resize/crop.
|
|
224
|
+
// Re-encodes to JPEG so WA always gets valid image bytes.
|
|
225
|
+
// crysnovax@HD-Profile --- HD mode: preserves original aspect ratio, no crop.
|
|
226
|
+
// WhatsApp server accepts max 720px on any side. Images under 720px pass through
|
|
227
|
+
// unchanged. Larger images are scaled down proportionally to fit within 720px.
|
|
228
|
+
export const generateProfilePictureHD = async (mediaUpload) => {
|
|
229
|
+
let buffer;
|
|
230
|
+
if (Buffer.isBuffer(mediaUpload)) {
|
|
231
|
+
buffer = mediaUpload;
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
const { stream } = await getStream(mediaUpload);
|
|
235
|
+
buffer = await toBuffer(stream);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const lib = await getImageProcessingLibrary();
|
|
239
|
+
let img;
|
|
240
|
+
|
|
241
|
+
if ('sharp' in lib && lib.sharp?.default) {
|
|
242
|
+
const metadata = await lib.sharp.default(buffer).metadata();
|
|
243
|
+
const origWidth = metadata.width || 720;
|
|
244
|
+
const origHeight = metadata.height || 720;
|
|
245
|
+
const maxDim = Math.max(origWidth, origHeight);
|
|
246
|
+
|
|
247
|
+
// WhatsApp server limit: 720px max dimension
|
|
248
|
+
// If already under 720, send as-is. If larger, scale down proportionally.
|
|
249
|
+
const needsResize = maxDim > 720;
|
|
250
|
+
|
|
251
|
+
let pipeline = lib.sharp.default(buffer);
|
|
252
|
+
|
|
253
|
+
if (needsResize) {
|
|
254
|
+
pipeline = pipeline.resize(720, 720, {
|
|
255
|
+
fit: 'inside',
|
|
256
|
+
withoutEnlargement: true
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
img = pipeline
|
|
261
|
+
.jpeg({ quality: 85, mozjpeg: true })
|
|
262
|
+
.toBuffer();
|
|
263
|
+
}
|
|
264
|
+
else if ('image' in lib && lib.image?.Transformer) {
|
|
265
|
+
// @napi-rs/image — cap at 720 via resize
|
|
266
|
+
img = new lib.image
|
|
267
|
+
.Transformer(buffer)
|
|
268
|
+
.resize(720, 720)
|
|
269
|
+
.jpeg(85);
|
|
270
|
+
}
|
|
271
|
+
else if ('jimp' in lib && lib.jimp?.Jimp) {
|
|
272
|
+
const jimp = await lib.jimp.Jimp.read(buffer);
|
|
273
|
+
const origWidth = jimp.width;
|
|
274
|
+
const origHeight = jimp.height;
|
|
275
|
+
const maxDim = Math.max(origWidth, origHeight);
|
|
276
|
+
|
|
277
|
+
let processed = jimp;
|
|
278
|
+
if (maxDim > 720) {
|
|
279
|
+
const scale = 720 / maxDim;
|
|
280
|
+
processed = jimp.resize({
|
|
281
|
+
w: Math.round(origWidth * scale),
|
|
282
|
+
h: Math.round(origHeight * scale),
|
|
283
|
+
mode: lib.jimp.ResizeStrategy.BILINEAR
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
img = processed.getBuffer('image/jpeg', { quality: 85 });
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
throw new Boom('No image processing library available');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const result = await img;
|
|
294
|
+
console.log(`[HD-Profile] Output: ${result.length} bytes, dims preserved (max 720px)`);
|
|
295
|
+
return { img: result };
|
|
296
|
+
};
|
|
223
297
|
/** gets the SHA256 of the given media message */
|
|
224
298
|
export const mediaMessageSHA256B64 = (message) => {
|
|
225
299
|
const media = Object.values(message)[0];
|