aunubaileys 1.0.48 β 1.0.50
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 +8 -0
- package/lib/Socket/aunu-experimental.d.ts +61 -0
- package/lib/Socket/aunu-experimental.js +182 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -64,6 +64,14 @@ Everything else is pure upstream. When upstream moves, we rebase the patch layer
|
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
|
|
67
|
+
### π§ͺ What's new in 1.0.50 / Baru di 1.0.50 β EXPERIMENTAL
|
|
68
|
+
|
|
69
|
+
- π **Six more experimental features.** (1) `sock.aunuCard(jid, options)` β an interactive photo card: the header photo **does not open as a normal photo** when tapped, a title/name and text sit fused below it in the same bubble, and divider-separated buttons (`reply`/`url`/`copy`/`call`) sit underneath; the photo is uploaded from a Buffer/URL and embedded as the header, or supplied prebuilt as `imageMessage`. (2) `sock.aunuFakeQuote(jid, content, quote)` β send a message carrying a fabricated quote, as if replying to someone. (3) `sock.aunuTypewriter(jid, text, options)` β a typing effect: the message is sent in growing pieces by editing it on an interval until the full text stands. (4) `sock.aunuReactSequence(jid, key, emojis, options)` β cycle reactions on one message. (5) `sock.aunuPostStatus(text, options)` β a styled text status (background colour + font) in one call. (6) `sock.aunuPersonalCard(jid, targetJid, options)` β the one-person form of 1.0.49's personalised tag-all: an own-profile-picture + name card for a single target, into a chat or as a DM. All experimental, like the features of 1.0.47/1.0.49.
|
|
70
|
+
|
|
71
|
+
### π§ͺ What's new in 1.0.49 / Baru di 1.0.49 β EXPERIMENTAL
|
|
72
|
+
|
|
73
|
+
- πͺͺ **Personalised tag-all: every member sees themselves.** New socket method `sock.aunuPersonalTagAll(groupJid, options)`: for EVERY group member the bot sends one card carrying **that member's own profile picture** and their own name, mentioning exactly them β not one generic tag-all message. Pictures come from `profilePictureUrl` (the PN form is used when a member is LID-addressed and the LID store knows the pair); the caption is built from a template with `{name}`, `{number}`, `{mention}`, `{jid}` placeholders; names come from a consumer-supplied `getName` resolver when provided, falling back to the number. Members without a profile picture get a text card, or the `fallbackImage` when set. Built-in anti-spam pacing: a delay between sends (`delayMs`, default 1500) and a member cap (`maxMembers`, default 30); `dm: true` delivers each card to the member's private chat instead. Returns a `{ total, sent, failed, skipped }` summary. Experimental, like the five features of 1.0.47.
|
|
74
|
+
|
|
67
75
|
### π What's new in 1.0.48 / Baru di 1.0.48
|
|
68
76
|
|
|
69
77
|
- π©Ή **Group Bad MAC now heals both layers.** The "Bad MAC" error (from Tenka's live log on 11 Nov 2026, the same LID address as the B-01 finding and the 1.0.46 flood) is thrown by `SessionCipher` β the whisper/E2E session layer β and in groups it can equally come from the sender-key layer. The old strike-limit path (1.0.14) for groups deleted only the sender key, so a rotten E2E session of the author was never touched and the failures repeated forever: the same wrong-layer class that 1.0.46 closed for session failures, now closed on the strike path. At the 3-failure limit, groups now reset the author's E2E session (including the alternate PNβLID address record) AND drop the sender key, with the retry receipt carrying fresh keys. DM behaviour and the 3-strike threshold are unchanged. The `messages.decrypt-failure` event now distinguishes `errorClass: 'bad-mac'` from `'session'`/`'other'` (new exported predicate: `isBadMacError(text)`).
|
|
@@ -17,6 +17,60 @@ export interface AunuScheduledSend {
|
|
|
17
17
|
at: number;
|
|
18
18
|
cancel: () => boolean;
|
|
19
19
|
}
|
|
20
|
+
export interface AunuPersonalTagAllOptions {
|
|
21
|
+
/** Caption template; placeholders {name}, {number}, {mention}, {jid}. Default 'Hai {mention} π'. */
|
|
22
|
+
text?: string;
|
|
23
|
+
/** Pause between per-member sends in ms. Default 1500. */
|
|
24
|
+
delayMs?: number;
|
|
25
|
+
/** Maximum members processed. Default 30. */
|
|
26
|
+
maxMembers?: number;
|
|
27
|
+
/** Send each card as a DM to the member instead of into the group. Default false. */
|
|
28
|
+
dm?: boolean;
|
|
29
|
+
/** Image (Buffer or URL) used when a member has no profile picture. Without it, that member gets a text card. */
|
|
30
|
+
fallbackImage?: Buffer | string;
|
|
31
|
+
/** Resolve a display name for a participant JID; falls back to the number. */
|
|
32
|
+
getName?: (jid: string) => string | undefined | Promise<string | undefined>;
|
|
33
|
+
messageOptions?: MiscMessageGenerationOptions;
|
|
34
|
+
}
|
|
35
|
+
export interface AunuPersonalTagAllResult {
|
|
36
|
+
total: number;
|
|
37
|
+
sent: number;
|
|
38
|
+
failed: { jid: string; error: string }[];
|
|
39
|
+
skipped: number;
|
|
40
|
+
}
|
|
41
|
+
export interface AunuCardButton {
|
|
42
|
+
type?: 'reply' | 'url' | 'copy' | 'call';
|
|
43
|
+
displayText?: string;
|
|
44
|
+
text?: string;
|
|
45
|
+
id?: string;
|
|
46
|
+
url?: string;
|
|
47
|
+
copyCode?: string;
|
|
48
|
+
phoneNumber?: string;
|
|
49
|
+
/** Raw native-flow passthrough. */
|
|
50
|
+
name?: string;
|
|
51
|
+
buttonParamsJson?: string | Record<string, unknown>;
|
|
52
|
+
}
|
|
53
|
+
export interface AunuCardOptions {
|
|
54
|
+
title?: string;
|
|
55
|
+
subtitle?: string;
|
|
56
|
+
/** Body text fused under the photo in the same bubble. */
|
|
57
|
+
text?: string;
|
|
58
|
+
footer?: string;
|
|
59
|
+
/** Header photo (Buffer or { url }); uploaded and embedded as the card header β it does not open as a normal photo. */
|
|
60
|
+
image?: Buffer | { url: string };
|
|
61
|
+
/** Prebuilt imageMessage object, used as-is when provided. */
|
|
62
|
+
imageMessage?: Record<string, unknown>;
|
|
63
|
+
buttons?: AunuCardButton[];
|
|
64
|
+
messageOptions?: MiscMessageGenerationOptions;
|
|
65
|
+
}
|
|
66
|
+
export interface AunuFakeQuoteOptions {
|
|
67
|
+
/** JID the fabricated quote pretends to come from. Defaults to the chat JID. */
|
|
68
|
+
from?: string;
|
|
69
|
+
/** Text of the fabricated quoted message. */
|
|
70
|
+
text?: string;
|
|
71
|
+
id?: string;
|
|
72
|
+
quotedMessage?: Record<string, unknown>;
|
|
73
|
+
}
|
|
20
74
|
export interface AunuExperimentalAPI {
|
|
21
75
|
aunuWatermark: (text: string, tag: string) => string;
|
|
22
76
|
aunuReadWatermark: (text: string) => string | null;
|
|
@@ -28,5 +82,12 @@ export interface AunuExperimentalAPI {
|
|
|
28
82
|
aunuSelfDestruct: (jid: string, content: AnyMessageContent, seconds: number, options?: MiscMessageGenerationOptions) => Promise<WAMessage | undefined>;
|
|
29
83
|
aunuCancelSelfDestruct: (messageId: string) => boolean;
|
|
30
84
|
aunuDoctor: () => AunuDoctorReport;
|
|
85
|
+
aunuPersonalTagAll: (groupJid: string, options?: AunuPersonalTagAllOptions) => Promise<AunuPersonalTagAllResult>;
|
|
86
|
+
aunuCard: (jid: string, options?: AunuCardOptions) => Promise<any>;
|
|
87
|
+
aunuFakeQuote: (jid: string, content: Record<string, unknown>, quote?: AunuFakeQuoteOptions, options?: MiscMessageGenerationOptions) => Promise<any>;
|
|
88
|
+
aunuTypewriter: (jid: string, text: string, options?: { steps?: number; intervalMs?: number; messageOptions?: MiscMessageGenerationOptions }) => Promise<any>;
|
|
89
|
+
aunuReactSequence: (jid: string, key: Record<string, unknown>, emojis: string | string[], options?: { intervalMs?: number; messageOptions?: MiscMessageGenerationOptions }) => Promise<number>;
|
|
90
|
+
aunuPostStatus: (text: string, options?: { backgroundColor?: string; font?: number } & MiscMessageGenerationOptions) => Promise<any>;
|
|
91
|
+
aunuPersonalCard: (jid: string | null, targetJid: string, options?: AunuPersonalTagAllOptions) => Promise<any>;
|
|
31
92
|
}
|
|
32
93
|
export declare const attachAunuExperimental: <T>(sock: T) => T & AunuExperimentalAPI;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import baileysVersion from '../Defaults/baileys-version.json' with { type: 'json' };
|
|
2
2
|
import { appendWatermark, computeTypingDuration, extractWatermark, stripWatermark } from '../Utils/aunu-experimental.js';
|
|
3
|
+
import { isLidUser } from '../WABinary/jid-utils.js';
|
|
4
|
+
import { prepareWAMessageMedia } from '../Utils/messages.js';
|
|
5
|
+
import { randomBytes } from 'crypto';
|
|
3
6
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
4
7
|
/**
|
|
5
8
|
* AunuBaileys EXPERIMENTAL socket features β these exist only in
|
|
@@ -19,6 +22,26 @@ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
|
19
22
|
* 5. Connection doctor β `aunuDoctor`: one structured health snapshot
|
|
20
23
|
* (registration, transport, bundled WA version, known LID pairs,
|
|
21
24
|
* pending experimental timers).
|
|
25
|
+
* 6. Personalised tag-all β `aunuPersonalTagAll`: for EVERY group member,
|
|
26
|
+
* one message carrying that member's OWN profile picture and name,
|
|
27
|
+
* mentioning exactly them β each member sees themselves, not a
|
|
28
|
+
* generic tag-all. Paced sends + a member cap guard against spam
|
|
29
|
+
* limits.
|
|
30
|
+
* 7. Photo card β `aunuCard`: an interactive card whose header photo does
|
|
31
|
+
* NOT open as a normal photo when tapped; a title/name and text sit
|
|
32
|
+
* fused below it in the same bubble, with divider-separated buttons
|
|
33
|
+
* (reply/url/copy/call) underneath.
|
|
34
|
+
* 8. Fabricated quote β `aunuFakeQuote`: send any content carrying a
|
|
35
|
+
* made-up quoted message, as if replying to someone.
|
|
36
|
+
* 9. Typewriter β `aunuTypewriter`: send a message in growing pieces by
|
|
37
|
+
* editing it on an interval until the full text stands.
|
|
38
|
+
* 10. Reaction sequence β `aunuReactSequence`: cycle reactions on one
|
|
39
|
+
* message (an animation effect).
|
|
40
|
+
* 11. Styled status β `aunuPostStatus`: post a text status with a
|
|
41
|
+
* background colour and font in one call.
|
|
42
|
+
* 12. Personal card β `aunuPersonalCard`: the one-person form of
|
|
43
|
+
* aunuPersonalTagAll (own profile picture + name card for one target,
|
|
44
|
+
* into a chat or as a DM).
|
|
22
45
|
*/
|
|
23
46
|
export const attachAunuExperimental = (sock) => {
|
|
24
47
|
const scheduled = new Map();
|
|
@@ -88,6 +111,165 @@ export const attachAunuExperimental = (sock) => {
|
|
|
88
111
|
destructs.delete(messageId);
|
|
89
112
|
return true;
|
|
90
113
|
};
|
|
114
|
+
sock.aunuPersonalTagAll = async (groupJid, options = {}) => {
|
|
115
|
+
const metadata = await sock.groupMetadata(groupJid);
|
|
116
|
+
const all = metadata?.participants || [];
|
|
117
|
+
const participants = all.slice(0, options.maxMembers ?? 30);
|
|
118
|
+
const template = options.text || 'Hai {mention} π';
|
|
119
|
+
const delayMs = options.delayMs ?? 1500;
|
|
120
|
+
const results = { total: all.length, sent: 0, failed: [], skipped: all.length - participants.length };
|
|
121
|
+
let first = true;
|
|
122
|
+
for (const participant of participants) {
|
|
123
|
+
const pid = participant.id;
|
|
124
|
+
if (!first && delayMs > 0) {
|
|
125
|
+
await delay(delayMs);
|
|
126
|
+
}
|
|
127
|
+
first = false;
|
|
128
|
+
try {
|
|
129
|
+
const pn = isLidUser(pid) ? (sock.aunuLid?.getPNForLID?.(pid) || pid) : pid;
|
|
130
|
+
const number = pn.split('@')[0].split(':')[0];
|
|
131
|
+
let name = number;
|
|
132
|
+
if (typeof options.getName === 'function') {
|
|
133
|
+
name = (await options.getName(pid)) || number;
|
|
134
|
+
}
|
|
135
|
+
let caption = template
|
|
136
|
+
.replaceAll('{name}', name)
|
|
137
|
+
.replaceAll('{number}', number)
|
|
138
|
+
.replaceAll('{mention}', '@' + number)
|
|
139
|
+
.replaceAll('{jid}', pid);
|
|
140
|
+
if (!caption.includes('@' + number)) {
|
|
141
|
+
caption += ' @' + number;
|
|
142
|
+
}
|
|
143
|
+
let ppUrl;
|
|
144
|
+
try {
|
|
145
|
+
ppUrl = await sock.profilePictureUrl(pn, 'image');
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
ppUrl = undefined;
|
|
149
|
+
}
|
|
150
|
+
const content = ppUrl
|
|
151
|
+
? { image: { url: ppUrl }, caption, mentions: [pid] }
|
|
152
|
+
: options.fallbackImage
|
|
153
|
+
? { image: Buffer.isBuffer(options.fallbackImage) ? options.fallbackImage : { url: options.fallbackImage }, caption, mentions: [pid] }
|
|
154
|
+
: { text: caption, mentions: [pid] };
|
|
155
|
+
await sock.sendMessage(options.dm ? pn : groupJid, content, options.messageOptions || {});
|
|
156
|
+
results.sent += 1;
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
results.failed.push({ jid: pid, error: String(err?.message || err) });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return results;
|
|
163
|
+
};
|
|
164
|
+
const mapCardButton = (b) => {
|
|
165
|
+
if (!b) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
if (b.name) {
|
|
169
|
+
return { name: b.name, buttonParamsJson: typeof b.buttonParamsJson === 'string' ? b.buttonParamsJson : JSON.stringify(b.buttonParamsJson ?? {}) };
|
|
170
|
+
}
|
|
171
|
+
const display_text = b.displayText || b.text || '';
|
|
172
|
+
switch (b.type) {
|
|
173
|
+
case 'url':
|
|
174
|
+
return { name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text, url: b.url || '', merchant_url: b.url || '' }) };
|
|
175
|
+
case 'copy':
|
|
176
|
+
return { name: 'cta_copy', buttonParamsJson: JSON.stringify({ display_text, id: b.id || 'copy', copy_code: b.copyCode || '' }) };
|
|
177
|
+
case 'call':
|
|
178
|
+
return { name: 'cta_call', buttonParamsJson: JSON.stringify({ display_text, id: b.phoneNumber || '' }) };
|
|
179
|
+
case 'reply':
|
|
180
|
+
default:
|
|
181
|
+
return { name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text, id: b.id || display_text }) };
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
sock.aunuCard = async (jid, options = {}) => {
|
|
185
|
+
let imageMessage = options.imageMessage;
|
|
186
|
+
if (!imageMessage && options.image) {
|
|
187
|
+
const prepared = await prepareWAMessageMedia({ image: options.image }, { upload: sock.waUploadToServer });
|
|
188
|
+
imageMessage = prepared.imageMessage;
|
|
189
|
+
}
|
|
190
|
+
const interactive = {
|
|
191
|
+
header: (options.title || options.subtitle || imageMessage)
|
|
192
|
+
? { title: options.title || '', subtitle: options.subtitle, hasMediaAttachment: !!imageMessage, imageMessage }
|
|
193
|
+
: undefined,
|
|
194
|
+
body: { text: options.text || '' },
|
|
195
|
+
footer: options.footer ? { text: options.footer } : undefined,
|
|
196
|
+
buttons: (options.buttons || []).map(mapCardButton).filter(Boolean)
|
|
197
|
+
};
|
|
198
|
+
return sock.sendMessage(jid, { interactive }, options.messageOptions || {});
|
|
199
|
+
};
|
|
200
|
+
sock.aunuFakeQuote = async (jid, content, quote = {}, options = {}) => {
|
|
201
|
+
const contextInfo = {
|
|
202
|
+
...(content?.contextInfo || {}),
|
|
203
|
+
stanzaId: quote.id || ('AUNU' + randomBytes(6).toString('hex').toUpperCase()),
|
|
204
|
+
participant: quote.from || jid,
|
|
205
|
+
quotedMessage: quote.quotedMessage || { conversation: quote.text || '' }
|
|
206
|
+
};
|
|
207
|
+
return sock.sendMessage(jid, { ...content, contextInfo }, options);
|
|
208
|
+
};
|
|
209
|
+
sock.aunuTypewriter = async (jid, text, options = {}) => {
|
|
210
|
+
const full = String(text ?? '');
|
|
211
|
+
const steps = Math.max(1, options.steps ?? 6);
|
|
212
|
+
const intervalMs = options.intervalMs ?? 700;
|
|
213
|
+
const chunk = Math.max(1, Math.ceil(full.length / steps));
|
|
214
|
+
const sentMsg = await sock.sendMessage(jid, { text: full.slice(0, chunk) }, options.messageOptions || {});
|
|
215
|
+
for (let upto = chunk * 2; upto < full.length + chunk; upto += chunk) {
|
|
216
|
+
if (intervalMs > 0) {
|
|
217
|
+
await delay(intervalMs);
|
|
218
|
+
}
|
|
219
|
+
await sock.sendMessage(jid, { text: full.slice(0, upto), edit: sentMsg.key }, options.messageOptions || {});
|
|
220
|
+
if (upto >= full.length) {
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return sentMsg;
|
|
225
|
+
};
|
|
226
|
+
sock.aunuReactSequence = async (jid, key, emojis, options = {}) => {
|
|
227
|
+
const intervalMs = options.intervalMs ?? 800;
|
|
228
|
+
const list = Array.isArray(emojis) ? emojis : [emojis];
|
|
229
|
+
let n = 0;
|
|
230
|
+
for (const emoji of list) {
|
|
231
|
+
if (n > 0 && intervalMs > 0) {
|
|
232
|
+
await delay(intervalMs);
|
|
233
|
+
}
|
|
234
|
+
await sock.sendMessage(jid, { react: { text: emoji, key } }, options.messageOptions || {});
|
|
235
|
+
n += 1;
|
|
236
|
+
}
|
|
237
|
+
return n;
|
|
238
|
+
};
|
|
239
|
+
sock.aunuPostStatus = async (text, options = {}) => {
|
|
240
|
+
const { backgroundColor = '#000000', font = 0, ...rest } = options;
|
|
241
|
+
return sock.sendMessage('status@broadcast', { text: String(text ?? '') }, { backgroundColor, font, ...rest });
|
|
242
|
+
};
|
|
243
|
+
sock.aunuPersonalCard = async (jid, targetJid, options = {}) => {
|
|
244
|
+
const pn = isLidUser(targetJid) ? (sock.aunuLid?.getPNForLID?.(targetJid) || targetJid) : targetJid;
|
|
245
|
+
const number = pn.split('@')[0].split(':')[0];
|
|
246
|
+
let name = number;
|
|
247
|
+
if (typeof options.getName === 'function') {
|
|
248
|
+
name = (await options.getName(targetJid)) || number;
|
|
249
|
+
}
|
|
250
|
+
const template = options.text || 'Hai {mention} π';
|
|
251
|
+
let caption = template
|
|
252
|
+
.replaceAll('{name}', name)
|
|
253
|
+
.replaceAll('{number}', number)
|
|
254
|
+
.replaceAll('{mention}', '@' + number)
|
|
255
|
+
.replaceAll('{jid}', targetJid);
|
|
256
|
+
if (!caption.includes('@' + number)) {
|
|
257
|
+
caption += ' @' + number;
|
|
258
|
+
}
|
|
259
|
+
let ppUrl;
|
|
260
|
+
try {
|
|
261
|
+
ppUrl = await sock.profilePictureUrl(pn, 'image');
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
ppUrl = undefined;
|
|
265
|
+
}
|
|
266
|
+
const content = ppUrl
|
|
267
|
+
? { image: { url: ppUrl }, caption, mentions: [targetJid] }
|
|
268
|
+
: options.fallbackImage
|
|
269
|
+
? { image: Buffer.isBuffer(options.fallbackImage) ? options.fallbackImage : { url: options.fallbackImage }, caption, mentions: [targetJid] }
|
|
270
|
+
: { text: caption, mentions: [targetJid] };
|
|
271
|
+
return sock.sendMessage(jid || pn, content, options.messageOptions || {});
|
|
272
|
+
};
|
|
91
273
|
sock.aunuDoctor = () => ({
|
|
92
274
|
experimental: true,
|
|
93
275
|
bundledWaVersion: Array.isArray(baileysVersion.version) ? baileysVersion.version.join('.') : null,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aunubaileys",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.50",
|
|
5
5
|
"description": "AunuBaileys \u2014 a WhatsApp Web API for bots that never sleep. Full upstream power with the beloved in-memory store restored and pairing that just works. Forged by AunuCore.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"whatsapp",
|