aunubaileys 1.0.2 → 1.0.4
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 +15 -0
- package/lib/Defaults/baileys-version.json +1 -1
- package/lib/Defaults/index.js +1 -1
- package/lib/Socket/Client/websocket.js +3 -0
- package/lib/Socket/chats.js +3 -1
- package/lib/Socket/index.js +4 -1
- package/lib/Socket/newsletter.js +8 -0
- package/lib/Socket/socket.js +33 -1
- package/lib/Types/Newsletter.js +4 -0
- package/lib/Utils/aunu-handler.d.ts +11 -0
- package/lib/Utils/aunu-handler.js +44 -0
- package/lib/Utils/builders.d.ts +31 -0
- package/lib/Utils/builders.js +118 -0
- package/lib/Utils/chat-utils.js +7 -0
- package/lib/Utils/event-buffer.js +15 -1
- package/lib/Utils/generics.js +2 -0
- package/lib/Utils/messages-media.js +8 -5
- package/lib/Utils/messages.js +132 -0
- package/lib/Utils/process-message.js +6 -3
- package/lib/index.d.ts +2 -0
- package/lib/index.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,6 +59,21 @@ sock.ev.on('creds.update', saveCreds)
|
|
|
59
59
|
Everything else is pure upstream. When upstream moves, we rebase the patch layer — that discipline is the whole point of this fork.
|
|
60
60
|
|
|
61
61
|
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
### 🆕 What's new in 1.0.4 / Baru di 1.0.4
|
|
65
|
+
|
|
66
|
+
- 🎨 **Rich builders** — `AIRich`, `Button`, `ButtonV2`, `Carousel`, `Toolkit` (+ aliases) for Meta AI-style rich responses, native-flow buttons and carousels; `aunuHandler.sendInteractive()` with automatic plain-text fallback on iOS-paired devices.
|
|
67
|
+
- 🖼️ **Profile pictures fixed** — center crop (was top-left) and jimp v1 compatibility restored.
|
|
68
|
+
- 🛡️ **Hardening** — undecodable app-state patches are skipped with a warning instead of killing sync, media downloads honour the message's own host, `getChatId` fails loudly, real `ev.destroy()` cleanup, group presence carries `groupOnlineCount`, `Browsers.android` added.
|
|
69
|
+
- ✅ Already verified in this codebase: PBKDF2 pairing derivation and the anti-spoof protocol-message guard (GHSA-qvv5-jq5g-4cgg class) match the reference implementations.
|
|
70
|
+
|
|
71
|
+
### 🆕 What's new in 1.0.3 / Baru di 1.0.3
|
|
72
|
+
|
|
73
|
+
- ⚡ **Modern core** — latest WA version `2.3000.1035194821`, 812 pre-keys, tuned WebSocket (no per-message deflate, 100 MB payload).
|
|
74
|
+
- 🧩 **More message types** — quiz polls (newsletter), poll result snapshots, poll updates, payment invites, order messages.
|
|
75
|
+
- 🛠️ **Developer API** — `sock.aunuHandler` (send/reply/react helpers), `fetchAccountReachoutTimelock()`, `fetchNewChatMessageCap()`, `registerSocketEndHandler()`, `newsletterId()` helper.
|
|
76
|
+
|
|
62
77
|
### 🆕 What's new in 1.0.2 / Baru di 1.0.2
|
|
63
78
|
|
|
64
79
|
- 📦 **Sticker packs** — send full sticker packs with `stickerPack` (cover + up to 60 stickers).
|
package/lib/Defaults/index.js
CHANGED
|
@@ -93,7 +93,7 @@ export const MEDIA_HKDF_KEY_MAPPING = {
|
|
|
93
93
|
};
|
|
94
94
|
export const MEDIA_KEYS = Object.keys(MEDIA_PATH_MAP);
|
|
95
95
|
export const MIN_PREKEY_COUNT = 5;
|
|
96
|
-
export const INITIAL_PREKEY_COUNT =
|
|
96
|
+
export const INITIAL_PREKEY_COUNT = 812;
|
|
97
97
|
export const DEFAULT_CACHE_TTLS = {
|
|
98
98
|
SIGNAL_STORE: 5 * 60, // 5 minutes
|
|
99
99
|
MSG_RETRY: 60 * 60, // 1 hour
|
|
@@ -23,6 +23,9 @@ export class WebSocketClient extends AbstractSocketClient {
|
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
25
|
this.socket = new WebSocket(this.url, {
|
|
26
|
+
// AunuBaileys: stabilitas WS — tanpa kompresi per-message, payload besar aman
|
|
27
|
+
perMessageDeflate: false,
|
|
28
|
+
maxPayload: 100 * 1024 * 1024,
|
|
26
29
|
origin: DEFAULT_ORIGIN,
|
|
27
30
|
headers: this.config.options?.headers,
|
|
28
31
|
handshakeTimeout: this.config.connectTimeoutMs,
|
package/lib/Socket/chats.js
CHANGED
|
@@ -546,7 +546,9 @@ export const makeChatsSocket = (config) => {
|
|
|
546
546
|
if (tag === 'presence') {
|
|
547
547
|
presence = {
|
|
548
548
|
lastKnownPresence: attrs.type === 'unavailable' ? 'unavailable' : 'available',
|
|
549
|
-
lastSeen: attrs.last && attrs.last !== 'deny' ? +attrs.last : undefined
|
|
549
|
+
lastSeen: attrs.last && attrs.last !== 'deny' ? +attrs.last : undefined,
|
|
550
|
+
// AunuBaileys: jumlah online grup bila dikirim WhatsApp
|
|
551
|
+
groupOnlineCount: attrs.count ? +attrs.count : undefined
|
|
550
552
|
};
|
|
551
553
|
}
|
|
552
554
|
else if (Array.isArray(content)) {
|
package/lib/Socket/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_CONNECTION_CONFIG } from '../Defaults/index.js';
|
|
2
2
|
import { makeCommunitiesSocket } from './communities.js';
|
|
3
|
+
import { AunuHandler } from '../Utils/aunu-handler.js';
|
|
3
4
|
// export the last socket layer
|
|
4
5
|
const makeWASocket = (config) => {
|
|
5
6
|
const newConfig = {
|
|
@@ -11,7 +12,9 @@ const makeWASocket = (config) => {
|
|
|
11
12
|
if (config.shouldSyncHistoryMessage === undefined) {
|
|
12
13
|
newConfig.shouldSyncHistoryMessage = () => !!newConfig.syncFullHistory;
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
+
const sock = makeCommunitiesSocket(newConfig);
|
|
16
|
+
sock.aunuHandler = new AunuHandler(sock);
|
|
17
|
+
return sock;
|
|
15
18
|
};
|
|
16
19
|
export default makeWASocket;
|
|
17
20
|
//# sourceMappingURL=index.js.map
|
package/lib/Socket/newsletter.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import Boom from '@hapi/boom';
|
|
1
2
|
import { QueryIds, XWAPaths } from '../Types/index.js';
|
|
2
3
|
import { generateProfilePicture } from '../Utils/messages-media.js';
|
|
3
4
|
import { getBinaryNodeChild } from '../WABinary/index.js';
|
|
@@ -49,6 +50,13 @@ export const makeNewsletterSocket = (sock) => {
|
|
|
49
50
|
};
|
|
50
51
|
return {
|
|
51
52
|
...sock,
|
|
53
|
+
// AunuBaileys: ambil ID numerik dari JID newsletter
|
|
54
|
+
newsletterId: (jid) => {
|
|
55
|
+
if (!jid || !jid.endsWith('@newsletter')) {
|
|
56
|
+
throw new Boom('Invalid newsletter jid', { statusCode: 400 });
|
|
57
|
+
}
|
|
58
|
+
return jid.split('@')[0];
|
|
59
|
+
},
|
|
52
60
|
newsletterCreate: async (name, description) => {
|
|
53
61
|
const variables = {
|
|
54
62
|
input: {
|
package/lib/Socket/socket.js
CHANGED
|
@@ -4,7 +4,9 @@ import { URL } from 'url';
|
|
|
4
4
|
import { promisify } from 'util';
|
|
5
5
|
import { proto } from '../../WAProto/index.js';
|
|
6
6
|
import { DEF_CALLBACK_PREFIX, DEF_TAG_PREFIX, INITIAL_PREKEY_COUNT, MIN_PREKEY_COUNT, NOISE_WA_HEADER } from '../Defaults/index.js';
|
|
7
|
-
import { DisconnectReason } from '../Types/index.js'
|
|
7
|
+
import { DisconnectReason } from '../Types/index.js'
|
|
8
|
+
import { executeWMexQuery } from './mex.js';
|
|
9
|
+
import { QueryIds, XWAPaths } from '../Types/index.js';
|
|
8
10
|
import { addTransactionCapability, aesEncryptCTR, bindWaitForConnectionUpdate, bytesToCrockford, configureSuccessfulPairing, Curve, derivePairingCodeKey, generateLoginNode, generateMdTagPrefix, generateRegistrationNode, getCodeFromWSError, getErrorCodeFromStreamError, getNextPreKeysNode, getPlatformId, makeEventBuffer, makeNoiseHandler, promiseTimeout } from '../Utils/index.js';
|
|
9
11
|
import { assertNodeErrorFree, binaryNodeToString, encodeBinaryNode, getBinaryNodeChild, getBinaryNodeChildren, jidEncode, S_WHATSAPP_NET } from '../WABinary/index.js';
|
|
10
12
|
import { WebSocketClient } from './Client/index.js';
|
|
@@ -242,6 +244,11 @@ export const makeSocket = (config) => {
|
|
|
242
244
|
}
|
|
243
245
|
});
|
|
244
246
|
};
|
|
247
|
+
// AunuBaileys: socket end handlers (pola v7) + fetch Mex (dari @itsliaaa/baileys)
|
|
248
|
+
const socketEndHandlers = [];
|
|
249
|
+
const registerSocketEndHandler = (handler) => {
|
|
250
|
+
socketEndHandlers.push(handler);
|
|
251
|
+
};
|
|
245
252
|
const end = (error) => {
|
|
246
253
|
if (closed) {
|
|
247
254
|
logger.trace({ trace: error?.stack }, 'connection already closed');
|
|
@@ -267,7 +274,14 @@ export const makeSocket = (config) => {
|
|
|
267
274
|
date: new Date()
|
|
268
275
|
}
|
|
269
276
|
});
|
|
277
|
+
for (const handler of socketEndHandlers) {
|
|
278
|
+
try {
|
|
279
|
+
handler();
|
|
280
|
+
}
|
|
281
|
+
catch { }
|
|
282
|
+
}
|
|
270
283
|
ev.removeAllListeners('connection.update');
|
|
284
|
+
ev.destroy?.();
|
|
271
285
|
};
|
|
272
286
|
const waitForSocketOpen = async () => {
|
|
273
287
|
if (ws.isOpen) {
|
|
@@ -577,6 +591,21 @@ export const makeSocket = (config) => {
|
|
|
577
591
|
}
|
|
578
592
|
Object.assign(creds, update);
|
|
579
593
|
});
|
|
594
|
+
const fetchAccountReachoutTimelock = async () => {
|
|
595
|
+
const queryResult = await executeWMexQuery({}, QueryIds.REACHOUT_TIMELOCK, XWAPaths.xwa2_fetch_account_reachout_timelock, query, generateMessageTag);
|
|
596
|
+
const result = {
|
|
597
|
+
isActive: !!queryResult?.is_active,
|
|
598
|
+
timeEnforcementEnds: queryResult?.time_enforcement_ends && queryResult?.time_enforcement_ends !== '0'
|
|
599
|
+
? new Date(parseInt(queryResult.time_enforcement_ends, 10) * 1000)
|
|
600
|
+
: undefined,
|
|
601
|
+
enforcementType: queryResult?.enforcement_type ?? 'DEFAULT'
|
|
602
|
+
};
|
|
603
|
+
ev.emit('connection.update', { reachoutTimeLock: result });
|
|
604
|
+
return result;
|
|
605
|
+
};
|
|
606
|
+
const fetchNewChatMessageCap = async () => {
|
|
607
|
+
return executeWMexQuery({ input: { type: 'INDIVIDUAL_NEW_CHAT_MSG' } }, QueryIds.MESSAGE_CAPPING_INFO, XWAPaths.xwa2_message_capping_info, query, generateMessageTag);
|
|
608
|
+
};
|
|
580
609
|
return {
|
|
581
610
|
type: 'md',
|
|
582
611
|
ws,
|
|
@@ -594,6 +623,9 @@ export const makeSocket = (config) => {
|
|
|
594
623
|
sendNode,
|
|
595
624
|
logout,
|
|
596
625
|
end,
|
|
626
|
+
registerSocketEndHandler,
|
|
627
|
+
fetchAccountReachoutTimelock,
|
|
628
|
+
fetchNewChatMessageCap,
|
|
597
629
|
onUnexpectedError,
|
|
598
630
|
uploadPreKeys,
|
|
599
631
|
uploadPreKeysToServerIfRequired,
|
package/lib/Types/Newsletter.js
CHANGED
|
@@ -12,6 +12,8 @@ export var XWAPaths;
|
|
|
12
12
|
XWAPaths["xwa2_newsletter_change_owner"] = "xwa2_newsletter_change_owner";
|
|
13
13
|
XWAPaths["xwa2_newsletter_demote"] = "xwa2_newsletter_demote";
|
|
14
14
|
XWAPaths["xwa2_newsletter_delete_v2"] = "xwa2_newsletter_delete_v2";
|
|
15
|
+
XWAPaths["xwa2_fetch_account_reachout_timelock"] = "xwa2_fetch_account_reachout_timelock";
|
|
16
|
+
XWAPaths["xwa2_message_capping_info"] = "xwa2_message_capping_info";
|
|
15
17
|
})(XWAPaths || (XWAPaths = {}));
|
|
16
18
|
export var QueryIds;
|
|
17
19
|
(function (QueryIds) {
|
|
@@ -27,5 +29,7 @@ export var QueryIds;
|
|
|
27
29
|
QueryIds["CHANGE_OWNER"] = "7341777602580933";
|
|
28
30
|
QueryIds["DEMOTE"] = "6551828931592903";
|
|
29
31
|
QueryIds["DELETE"] = "30062808666639665";
|
|
32
|
+
QueryIds["REACHOUT_TIMELOCK"] = "23983697327930364";
|
|
33
|
+
QueryIds["MESSAGE_CAPPING_INFO"] = "24503548349331633";
|
|
30
34
|
})(QueryIds || (QueryIds = {}));
|
|
31
35
|
//# sourceMappingURL=Newsletter.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AnyMessageContent, MiscMessageGenerationOptions, WAMessage, WASocket } from '../Types';
|
|
2
|
+
export declare class AunuHandler {
|
|
3
|
+
sock: WASocket;
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly brand: string;
|
|
6
|
+
constructor(sock: WASocket);
|
|
7
|
+
send(jid: string, content: AnyMessageContent, options?: MiscMessageGenerationOptions): Promise<WAMessage | undefined>;
|
|
8
|
+
reply(jid: string, content: AnyMessageContent, quoted: WAMessage, options?: MiscMessageGenerationOptions): Promise<WAMessage | undefined>;
|
|
9
|
+
react(jid: string, emoji: string, key: WAMessage['key']): Promise<WAMessage | undefined>;
|
|
10
|
+
newsletterId(jid: string): string;
|
|
11
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AunuHandler — lapisan kenyamanan khas AunuBaileys di atas socket.
|
|
3
|
+
* Diekspos sebagai `sock.aunuHandler` dan diekspor sebagai kelas dari paket.
|
|
4
|
+
*/
|
|
5
|
+
export class AunuHandler {
|
|
6
|
+
constructor(sock) {
|
|
7
|
+
this.sock = sock;
|
|
8
|
+
this.name = 'AunuBaileys';
|
|
9
|
+
this.brand = 'AunuCore Creative Network';
|
|
10
|
+
}
|
|
11
|
+
/** Kirim pesan apa pun (delegate ke sock.sendMessage). */
|
|
12
|
+
async send(jid, content, options) {
|
|
13
|
+
return this.sock.sendMessage(jid, content, options);
|
|
14
|
+
}
|
|
15
|
+
/** Balas sebuah pesan (quote). */
|
|
16
|
+
async reply(jid, content, quoted, options = {}) {
|
|
17
|
+
return this.sock.sendMessage(jid, content, { ...options, quoted });
|
|
18
|
+
}
|
|
19
|
+
/** Beri reaksi emoji ke sebuah pesan. */
|
|
20
|
+
async react(jid, emoji, key) {
|
|
21
|
+
return this.sock.sendMessage(jid, { react: { text: emoji, key } });
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Kirim pesan interaktif; otomatis fallback ke teks polos bila
|
|
25
|
+
* perangkat pasangan adalah iOS (tombol tidak tampil andal di sana)
|
|
26
|
+
* atau bila options.forceFallback diaktifkan.
|
|
27
|
+
*/
|
|
28
|
+
async sendInteractive(jid, content, options = {}) {
|
|
29
|
+
const platform = this.sock.authState?.creds?.platform;
|
|
30
|
+
if (options.forceFallback || platform === 'ios') {
|
|
31
|
+
const i = content?.interactive || {};
|
|
32
|
+
const text = options.fallbackText || i.text || i.body?.text || '';
|
|
33
|
+
const rest = { ...options };
|
|
34
|
+
delete rest.forceFallback;
|
|
35
|
+
delete rest.fallbackText;
|
|
36
|
+
return this.sock.sendMessage(jid, { text }, rest);
|
|
37
|
+
}
|
|
38
|
+
return this.sock.sendMessage(jid, content, options);
|
|
39
|
+
}
|
|
40
|
+
/** ID numerik dari JID newsletter. */
|
|
41
|
+
newsletterId(jid) {
|
|
42
|
+
return this.sock.newsletterId(jid);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { AnyMessageContent } from '../Types';
|
|
2
|
+
export declare class Button {
|
|
3
|
+
title(t: string): this; body(t: string): this; footer(t: string): this;
|
|
4
|
+
reply(id: string, text: string): this; url(text: string, url: string): this;
|
|
5
|
+
copy(text: string, code: string): this; call(text: string, phoneNumber: string): this;
|
|
6
|
+
build(): AnyMessageContent;
|
|
7
|
+
}
|
|
8
|
+
export declare class ButtonV2 {
|
|
9
|
+
body(t: string): this; footer(t: string): this;
|
|
10
|
+
list(buttonText: string, sections: unknown[]): this;
|
|
11
|
+
reply(id: string, text: string): this; url(text: string, url: string): this;
|
|
12
|
+
build(): AnyMessageContent;
|
|
13
|
+
}
|
|
14
|
+
export declare class Carousel {
|
|
15
|
+
body(t: string): this; card(card: Record<string, unknown>): this;
|
|
16
|
+
build(): AnyMessageContent;
|
|
17
|
+
}
|
|
18
|
+
export declare class AIRich {
|
|
19
|
+
text(t: string): this; code(code: string, language?: string): this;
|
|
20
|
+
latex(t: string): this; table(table: unknown): this; raw(sub: unknown): this;
|
|
21
|
+
build(): AnyMessageContent;
|
|
22
|
+
}
|
|
23
|
+
export declare class Toolkit {
|
|
24
|
+
static quickReply(id: string, text: string): unknown;
|
|
25
|
+
static urlButton(text: string, url: string): unknown;
|
|
26
|
+
static copyButton(text: string, code: string): unknown;
|
|
27
|
+
static callButton(text: string, phoneNumber: string): unknown;
|
|
28
|
+
static listSection(title: string, rows: { id: string; title: string; description?: string }[]): unknown;
|
|
29
|
+
static listButton(text: string, title: string, rows: { id: string; title: string; description?: string }[]): unknown;
|
|
30
|
+
}
|
|
31
|
+
export { Button as Buttons, Button as ButtonBuilder, ButtonV2 as ListButton, Carousel as CarouselBuilder, AIRich as AiRich, AIRich as RichResponse, Toolkit as Tools };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rich message builders khas AunuBaileys — API chainable untuk tombol,
|
|
3
|
+
* carousel, dan respons kaya ala Meta AI. Hasil build() adalah
|
|
4
|
+
* AnyMessageContent yang siap dikirim via sock.sendMessage().
|
|
5
|
+
*/
|
|
6
|
+
const nf = (name, params) => ({ name, buttonParamsJson: JSON.stringify(params) });
|
|
7
|
+
|
|
8
|
+
/** Builder tombol interaktif (native flow). Alias: Buttons, ButtonBuilder. */
|
|
9
|
+
export class Button {
|
|
10
|
+
constructor() {
|
|
11
|
+
this._text = '';
|
|
12
|
+
this._footer = '';
|
|
13
|
+
this._title = '';
|
|
14
|
+
this._buttons = [];
|
|
15
|
+
}
|
|
16
|
+
title(t) { this._title = t; return this; }
|
|
17
|
+
body(t) { this._text = t; return this; }
|
|
18
|
+
footer(t) { this._footer = t; return this; }
|
|
19
|
+
/** Tombol balasan cepat. */
|
|
20
|
+
reply(id, text) { this._buttons.push(nf('quick_reply', { display_text: text, id: String(id) })); return this; }
|
|
21
|
+
/** Tombol buka tautan. */
|
|
22
|
+
url(text, url) { this._buttons.push(nf('cta_url', { display_text: text, url, merchant_url: url })); return this; }
|
|
23
|
+
/** Tombol salin teks/kode. */
|
|
24
|
+
copy(text, code) { this._buttons.push(nf('cta_copy', { display_text: text, copy_code: code, id: 'copy' })); return this; }
|
|
25
|
+
/** Tombol telepon. */
|
|
26
|
+
call(text, phoneNumber) { this._buttons.push(nf('cta_call', { display_text: text, phone_number: phoneNumber })); return this; }
|
|
27
|
+
build() {
|
|
28
|
+
return {
|
|
29
|
+
interactive: {
|
|
30
|
+
text: this._text,
|
|
31
|
+
footerText: this._footer || undefined,
|
|
32
|
+
header: this._title ? { title: this._title, hasMediaAttachment: false } : undefined,
|
|
33
|
+
buttons: this._buttons
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Builder daftar pilihan (single_select) + tombol lanjutan. Alias: ListButton. */
|
|
40
|
+
export class ButtonV2 {
|
|
41
|
+
constructor() {
|
|
42
|
+
this._text = '';
|
|
43
|
+
this._footer = '';
|
|
44
|
+
this._buttons = [];
|
|
45
|
+
this._sections = [];
|
|
46
|
+
}
|
|
47
|
+
body(t) { this._text = t; return this; }
|
|
48
|
+
footer(t) { this._footer = t; return this; }
|
|
49
|
+
/** Satu tombol daftar berisi sections/rows. */
|
|
50
|
+
list(buttonText, sections) {
|
|
51
|
+
this._buttons.push(nf('single_select', { title: buttonText, sections }));
|
|
52
|
+
return this;
|
|
53
|
+
}
|
|
54
|
+
reply(id, text) { this._buttons.push(nf('quick_reply', { display_text: text, id: String(id) })); return this; }
|
|
55
|
+
url(text, url) { this._buttons.push(nf('cta_url', { display_text: text, url, merchant_url: url })); return this; }
|
|
56
|
+
build() {
|
|
57
|
+
return { interactive: { text: this._text, footerText: this._footer || undefined, buttons: this._buttons } };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Builder carousel kartu geser. Alias: CarouselBuilder. */
|
|
62
|
+
export class Carousel {
|
|
63
|
+
constructor() {
|
|
64
|
+
this._text = '';
|
|
65
|
+
this._cards = [];
|
|
66
|
+
}
|
|
67
|
+
body(t) { this._text = t; return this; }
|
|
68
|
+
/** card: { title, body, footer?, imageMessage?, buttons?: Button instance | array nativeFlow } */
|
|
69
|
+
card(card) {
|
|
70
|
+
let buttons = card.buttons || [];
|
|
71
|
+
if (buttons instanceof Button) {
|
|
72
|
+
buttons = buttons.build().interactive.buttons;
|
|
73
|
+
}
|
|
74
|
+
this._cards.push({ title: card.title, body: card.body || '', footer: card.footer, imageMessage: card.imageMessage, buttons });
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
build() {
|
|
78
|
+
return { interactive: { text: this._text, carousel: this._cards } };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Builder respons kaya ala Meta AI (AIRichResponseMessage). Alias: AiRich, RichResponse. */
|
|
83
|
+
export class AIRich {
|
|
84
|
+
constructor() {
|
|
85
|
+
this._subs = [];
|
|
86
|
+
}
|
|
87
|
+
text(t) { this._subs.push({ type: 'text', text: t }); return this; }
|
|
88
|
+
code(code, language = 'text') { this._subs.push({ type: 'code', code, language }); return this; }
|
|
89
|
+
latex(t) { this._subs.push({ type: 'latex', text: t }); return this; }
|
|
90
|
+
/** table: proto tableMetadata mentah { title, rows: [...] } */
|
|
91
|
+
table(table) { this._subs.push({ type: 'table', table }); return this; }
|
|
92
|
+
raw(sub) { this._subs.push({ raw: sub }); return this; }
|
|
93
|
+
build() {
|
|
94
|
+
return { aiRich: { submessages: this._subs } };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Helper statis campuran. Alias: Tools. */
|
|
99
|
+
export class Toolkit {
|
|
100
|
+
static quickReply(id, text) { return nf('quick_reply', { display_text: text, id: String(id) }); }
|
|
101
|
+
static urlButton(text, url) { return nf('cta_url', { display_text: text, url, merchant_url: url }); }
|
|
102
|
+
static copyButton(text, code) { return nf('cta_copy', { display_text: text, copy_code: code, id: 'copy' }); }
|
|
103
|
+
static callButton(text, phoneNumber) { return nf('cta_call', { display_text: text, phone_number: phoneNumber }); }
|
|
104
|
+
static listSection(title, rows) {
|
|
105
|
+
return { title, rows: rows.map(r => ({ id: String(r.id), title: r.title, description: r.description || '' })) };
|
|
106
|
+
}
|
|
107
|
+
/** Ubah daftar {id, title, description} menjadi satu tombol single_select. */
|
|
108
|
+
static listButton(text, title, rows) {
|
|
109
|
+
return nf('single_select', { title: text, sections: [Toolkit.listSection(title, rows)] });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export const Buttons = Button;
|
|
113
|
+
export const ButtonBuilder = Button;
|
|
114
|
+
export const ListButton = ButtonV2;
|
|
115
|
+
export const CarouselBuilder = Carousel;
|
|
116
|
+
export const AiRich = AIRich;
|
|
117
|
+
export const RichResponse = AIRich;
|
|
118
|
+
export const Tools = Toolkit;
|
package/lib/Utils/chat-utils.js
CHANGED
|
@@ -286,6 +286,7 @@ export const decodePatches = async (name, syncds, initial, getAppStateSyncKey, o
|
|
|
286
286
|
};
|
|
287
287
|
const mutationMap = {};
|
|
288
288
|
for (const syncd of syncds) {
|
|
289
|
+
try {
|
|
289
290
|
const { version, keyId, snapshotMac } = syncd;
|
|
290
291
|
if (syncd.externalMutations) {
|
|
291
292
|
logger?.trace({ name, version }, 'downloading external patch');
|
|
@@ -318,6 +319,12 @@ export const decodePatches = async (name, syncds, initial, getAppStateSyncKey, o
|
|
|
318
319
|
}
|
|
319
320
|
// clear memory used up by the mutations
|
|
320
321
|
syncd.mutations = [];
|
|
322
|
+
}
|
|
323
|
+
catch (err) {
|
|
324
|
+
// AunuBaileys: satu patch rusak tidak membunuh seluruh sinkronisasi
|
|
325
|
+
logger?.warn({ err, name, version: syncd.version }, 'skipping undecodable app state patch');
|
|
326
|
+
syncd.mutations = [];
|
|
327
|
+
}
|
|
321
328
|
}
|
|
322
329
|
return { state: newState, mutationMap };
|
|
323
330
|
};
|
|
@@ -100,7 +100,21 @@ export const makeEventBuffer = (logger) => {
|
|
|
100
100
|
},
|
|
101
101
|
on: (...args) => ev.on(...args),
|
|
102
102
|
off: (...args) => ev.off(...args),
|
|
103
|
-
removeAllListeners: (...args) => ev.removeAllListeners(...args)
|
|
103
|
+
removeAllListeners: (...args) => ev.removeAllListeners(...args),
|
|
104
|
+
// AunuBaileys: destroy sungguhan — bersihkan buffer, cache & listener
|
|
105
|
+
destroy() {
|
|
106
|
+
try {
|
|
107
|
+
const fresh = makeBufferData();
|
|
108
|
+
for (const key of Object.keys(data)) {
|
|
109
|
+
delete data[key];
|
|
110
|
+
}
|
|
111
|
+
Object.assign(data, fresh);
|
|
112
|
+
}
|
|
113
|
+
catch { }
|
|
114
|
+
historyCache.clear();
|
|
115
|
+
isBuffering = false;
|
|
116
|
+
ev.removeAllListeners();
|
|
117
|
+
}
|
|
104
118
|
};
|
|
105
119
|
};
|
|
106
120
|
const makeBufferData = () => {
|
package/lib/Utils/generics.js
CHANGED
|
@@ -25,6 +25,8 @@ export const Browsers = {
|
|
|
25
25
|
macOS: browser => ['Mac OS', browser, '14.4.1'],
|
|
26
26
|
baileys: browser => ['Baileys', browser, '6.5.0'],
|
|
27
27
|
windows: browser => ['Windows', browser, '10.0.22631'],
|
|
28
|
+
/** AunuBaileys: sesi yang tampil sebagai Android */
|
|
29
|
+
android: browser => ['Android', browser, '14'],
|
|
28
30
|
/** The appropriate browser based on your OS & release */
|
|
29
31
|
appropriate: browser => [PLATFORM_MAP[platform()] || 'Ubuntu', browser, release()]
|
|
30
32
|
};
|
|
@@ -114,7 +114,8 @@ export const extractImageThumb = async (bufferOrFilePath, width = 32) => {
|
|
|
114
114
|
}
|
|
115
115
|
};
|
|
116
116
|
}
|
|
117
|
-
else if ('jimp' in lib && typeof lib.jimp?.Jimp === 'object') {
|
|
117
|
+
else if ('jimp' in lib && (typeof lib.jimp?.Jimp === 'object' || typeof lib.jimp?.Jimp === 'function')) {
|
|
118
|
+
// AunuBaileys: jimp v1 mengekspos Jimp sebagai class (function), bukan object
|
|
118
119
|
const jimp = await lib.jimp.Jimp.read(bufferOrFilePath);
|
|
119
120
|
const dimensions = {
|
|
120
121
|
width: jimp.width,
|
|
@@ -156,10 +157,12 @@ export const generateProfilePicture = async (mediaUpload, dimensions) => {
|
|
|
156
157
|
})
|
|
157
158
|
.toBuffer();
|
|
158
159
|
}
|
|
159
|
-
else if ('jimp' in lib && typeof lib.jimp?.Jimp === 'object') {
|
|
160
|
+
else if ('jimp' in lib && (typeof lib.jimp?.Jimp === 'object' || typeof lib.jimp?.Jimp === 'function')) {
|
|
161
|
+
// AunuBaileys: jimp v1 mengekspos Jimp sebagai class (function), bukan object
|
|
160
162
|
const jimp = await lib.jimp.Jimp.read(buffer);
|
|
161
163
|
const min = Math.min(jimp.width, jimp.height);
|
|
162
|
-
|
|
164
|
+
// AunuBaileys: crop dari TENGAH (sebelumnya pojok kiri-atas memotong subjek)
|
|
165
|
+
const cropped = jimp.crop({ x: Math.floor((jimp.width - min) / 2), y: Math.floor((jimp.height - min) / 2), w: min, h: min });
|
|
163
166
|
img = cropped.resize({ w, h, mode: lib.jimp.ResizeStrategy.BILINEAR }).getBuffer('image/jpeg', { quality: 50 });
|
|
164
167
|
}
|
|
165
168
|
else {
|
|
@@ -386,8 +389,8 @@ const toSmallestChunkSize = (num) => {
|
|
|
386
389
|
};
|
|
387
390
|
export const getUrlFromDirectPath = (directPath) => `https://${DEF_HOST}${directPath}`;
|
|
388
391
|
export const downloadContentFromMessage = async ({ mediaKey, directPath, url }, type, opts = {}) => {
|
|
389
|
-
|
|
390
|
-
const downloadUrl =
|
|
392
|
+
// AunuBaileys: pakai host dari URL pesan bila ada (bukan selalu mmg.whatsapp.net)
|
|
393
|
+
const downloadUrl = url?.startsWith('https://') ? url : getUrlFromDirectPath(directPath);
|
|
391
394
|
if (!downloadUrl) {
|
|
392
395
|
throw new Boom('No valid media URL or directPath present in message', { statusCode: 400 });
|
|
393
396
|
}
|
package/lib/Utils/messages.js
CHANGED
|
@@ -615,6 +615,23 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
615
615
|
// poll v2 is for community announcement groups (single select and multiple)
|
|
616
616
|
m.pollCreationMessageV2 = pollCreationMessage;
|
|
617
617
|
}
|
|
618
|
+
else if (message.poll.pollType === 1) {
|
|
619
|
+
// AunuBaileys: kuis (dari @itsliaaa/baileys) — untuk newsletter
|
|
620
|
+
if (!message.poll.correctAnswer) {
|
|
621
|
+
throw new Boom('No "correctAnswer" provided for quiz', { statusCode: 400 });
|
|
622
|
+
}
|
|
623
|
+
// proto 6.7.24: V5 adalah FutureProofMessage — konten dibungkus di .message
|
|
624
|
+
m.pollCreationMessageV5 = {
|
|
625
|
+
message: {
|
|
626
|
+
pollCreationMessage: {
|
|
627
|
+
...pollCreationMessage,
|
|
628
|
+
correctAnswer: { optionName: message.poll.correctAnswer.toString() },
|
|
629
|
+
pollType: 1,
|
|
630
|
+
selectableOptionsCount: 1
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
};
|
|
634
|
+
}
|
|
618
635
|
else {
|
|
619
636
|
if (message.poll.selectableCount === 1) {
|
|
620
637
|
//poll v3 is for single select polls
|
|
@@ -626,6 +643,87 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
626
643
|
}
|
|
627
644
|
}
|
|
628
645
|
}
|
|
646
|
+
else if ('aiRich' in message) {
|
|
647
|
+
// AunuBaileys: Meta AI-style rich response (AIRichResponseMessage)
|
|
648
|
+
const subs = (message.aiRich.submessages || []).map((s) => {
|
|
649
|
+
if (s.type === 'text' || (!s.type && s.text)) {
|
|
650
|
+
return { messageText: { text: s.text || '' } };
|
|
651
|
+
}
|
|
652
|
+
if (s.type === 'code') {
|
|
653
|
+
return { codeMetadata: { codeContent: s.code || s.text || '', codeLanguage: s.language || 'text' } };
|
|
654
|
+
}
|
|
655
|
+
if (s.type === 'latex') {
|
|
656
|
+
return { latexMetadata: { text: s.text || '' } };
|
|
657
|
+
}
|
|
658
|
+
if (s.type === 'table') {
|
|
659
|
+
return { tableMetadata: s.table };
|
|
660
|
+
}
|
|
661
|
+
return s.raw || s;
|
|
662
|
+
});
|
|
663
|
+
m.richResponseMessage = {
|
|
664
|
+
messageType: proto.AIRichResponseMessage.AIRichResponseMessageType.AI_RICH_RESPONSE_TYPE_STANDARD,
|
|
665
|
+
submessages: subs
|
|
666
|
+
};
|
|
667
|
+
if ('text' in message) {
|
|
668
|
+
// teks pendamping opsional diabaikan oleh proto bila rich response ada
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
else if ('pollResult' in message) {
|
|
672
|
+
// AunuBaileys: poll result snapshot (dari @itsliaaa/baileys)
|
|
673
|
+
const pollResultSnapshotMessage = {
|
|
674
|
+
name: message.pollResult.name,
|
|
675
|
+
pollVotes: message.pollResult.votes.map(vote => ({
|
|
676
|
+
optionName: vote.name,
|
|
677
|
+
optionVoteCount: parseInt(vote.voteCount)
|
|
678
|
+
}))
|
|
679
|
+
};
|
|
680
|
+
if (message.pollResult.pollType === 1) {
|
|
681
|
+
pollResultSnapshotMessage.pollType = proto.Message.PollCreationMessage.PollType.QUIZ;
|
|
682
|
+
m.pollResultSnapshotMessageV3 = pollResultSnapshotMessage;
|
|
683
|
+
}
|
|
684
|
+
else {
|
|
685
|
+
pollResultSnapshotMessage.pollType = proto.Message.PollCreationMessage.PollType.POLL;
|
|
686
|
+
m.pollResultSnapshotMessage = pollResultSnapshotMessage;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
else if ('pollUpdate' in message) {
|
|
690
|
+
if (!message.pollUpdate.key) {
|
|
691
|
+
throw new Boom('Message key is required', { statusCode: 400 });
|
|
692
|
+
}
|
|
693
|
+
if (!message.pollUpdate.vote) {
|
|
694
|
+
throw new Boom('Encrypted vote payload is required', { statusCode: 400 });
|
|
695
|
+
}
|
|
696
|
+
m.pollUpdateMessage = {
|
|
697
|
+
metadata: message.pollUpdate.metadata,
|
|
698
|
+
pollCreationMessageKey: message.pollUpdate.key,
|
|
699
|
+
senderTimestampMs: Date.now(),
|
|
700
|
+
vote: message.pollUpdate.vote
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
else if ('paymentInviteServiceType' in message) {
|
|
704
|
+
// AunuBaileys: payment invite (dari @itsliaaa/baileys)
|
|
705
|
+
m.paymentInviteMessage = {
|
|
706
|
+
expiryTimestamp: Date.now(),
|
|
707
|
+
serviceType: message.paymentInviteServiceType
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
else if ('orderText' in message) {
|
|
711
|
+
// AunuBaileys: order message (dari @itsliaaa/baileys)
|
|
712
|
+
if (message.thumbnail !== undefined && !Buffer.isBuffer(message.thumbnail)) {
|
|
713
|
+
throw new Boom('Must provide thumbnail buffer in order message', { statusCode: 400 });
|
|
714
|
+
}
|
|
715
|
+
m.orderMessage = {
|
|
716
|
+
itemCount: message.itemCount || 1,
|
|
717
|
+
messageVersion: 1,
|
|
718
|
+
orderTitle: message.orderTitle || 'AunuBaileys',
|
|
719
|
+
status: proto.Message.OrderMessage.OrderStatus.INQUIRY,
|
|
720
|
+
surface: proto.Message.OrderMessage.OrderSurface.CATALOG,
|
|
721
|
+
totalAmount1000: message.totalAmount1000 || 1000,
|
|
722
|
+
totalCurrencyCode: message.currency || 'IDR',
|
|
723
|
+
thumbnail: message.thumbnail,
|
|
724
|
+
message: message.orderText
|
|
725
|
+
};
|
|
726
|
+
}
|
|
629
727
|
else if ('sharePhoneNumber' in message) {
|
|
630
728
|
m.protocolMessage = {
|
|
631
729
|
type: proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
|
|
@@ -634,6 +732,40 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
634
732
|
else if ('requestPhoneNumber' in message) {
|
|
635
733
|
m.requestPhoneNumberMessage = {};
|
|
636
734
|
}
|
|
735
|
+
else if ('interactive' in message) {
|
|
736
|
+
// AunuBaileys: interactive / native-flow (pola @itsliaaa/baileys, disederhanakan)
|
|
737
|
+
const iMsg = message.interactive;
|
|
738
|
+
const interactiveMessage = {
|
|
739
|
+
body: iMsg.body ?? (iMsg.text ? { text: iMsg.text } : undefined),
|
|
740
|
+
footer: iMsg.footer ?? (iMsg.footerText ? { text: iMsg.footerText } : undefined),
|
|
741
|
+
header: iMsg.header,
|
|
742
|
+
contextInfo: iMsg.contextInfo
|
|
743
|
+
};
|
|
744
|
+
if (iMsg.carousel) {
|
|
745
|
+
interactiveMessage.carouselMessage = {
|
|
746
|
+
cards: iMsg.carousel.map(card => ({
|
|
747
|
+
header: card.header ?? (card.title ? { title: card.title, hasMediaAttachment: !!card.imageMessage, imageMessage: card.imageMessage } : undefined),
|
|
748
|
+
body: { text: card.body || '' },
|
|
749
|
+
footer: card.footer ? { text: card.footer } : undefined,
|
|
750
|
+
nativeFlowMessage: { buttons: (card.buttons || []).map(b => ({ name: b.name, buttonParamsJson: typeof b.buttonParamsJson === 'string' ? b.buttonParamsJson : JSON.stringify(b.buttonParamsJson ?? {}) })) }
|
|
751
|
+
})),
|
|
752
|
+
messageVersion: 1
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
else {
|
|
756
|
+
interactiveMessage.nativeFlowMessage = {
|
|
757
|
+
buttons: (iMsg.buttons || []).map(b => ({
|
|
758
|
+
name: b.name,
|
|
759
|
+
buttonParamsJson: typeof b.buttonParamsJson === 'string' ? b.buttonParamsJson : JSON.stringify(b.buttonParamsJson ?? {})
|
|
760
|
+
})),
|
|
761
|
+
messageParamsJson: iMsg.messageParamsJson || ''
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
m = {
|
|
765
|
+
interactiveMessage,
|
|
766
|
+
messageContextInfo: { deviceListMetadata: {}, deviceListMetadataVersion: 2 }
|
|
767
|
+
};
|
|
768
|
+
}
|
|
637
769
|
else if ('album' in message) {
|
|
638
770
|
// AunuBaileys: album message (dari @itsliaaa/baileys)
|
|
639
771
|
if (!Array.isArray(message.album)) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import Boom from '@hapi/boom';
|
|
1
2
|
import { proto } from '../../WAProto/index.js';
|
|
2
3
|
import { WAMessageStubType } from '../Types/index.js';
|
|
3
4
|
import { getContentType, normalizeMessageContent } from '../Utils/messages.js';
|
|
@@ -61,10 +62,12 @@ export const shouldIncrementChatUnread = (message) => !message.key.fromMe && !me
|
|
|
61
62
|
* Typically -- that'll be the remoteJid, but for broadcasts, it'll be the participant
|
|
62
63
|
*/
|
|
63
64
|
export const getChatId = ({ remoteJid, participant, fromMe }) => {
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
// AunuBaileys: gagal dengan jelas, jangan diam-diam memakai undefined
|
|
66
|
+
const id = isJidBroadcast(remoteJid) && !isJidStatusBroadcast(remoteJid) && !fromMe ? participant : remoteJid;
|
|
67
|
+
if (!id) {
|
|
68
|
+
throw new Boom('getChatId: missing remoteJid/participant in message key', { statusCode: 400 });
|
|
66
69
|
}
|
|
67
|
-
return
|
|
70
|
+
return id;
|
|
68
71
|
};
|
|
69
72
|
/**
|
|
70
73
|
* Decrypt a poll vote
|
package/lib/index.d.ts
CHANGED
|
@@ -13,3 +13,5 @@ export default makeWASocket;
|
|
|
13
13
|
export * from './Store/index.js';
|
|
14
14
|
export { processedMessages, initInMemoryKeyStore } from './Utils/fork-compat.js';
|
|
15
15
|
export { PHONENUMBER_MCC } from './Utils/phonenumber-mcc.js';
|
|
16
|
+
export * from './Utils/aunu-handler';
|
|
17
|
+
export * from './Utils/builders';
|
package/lib/index.js
CHANGED
|
@@ -12,3 +12,5 @@ export default makeWASocket;
|
|
|
12
12
|
export * from './Store/index.js';
|
|
13
13
|
export { processedMessages, initInMemoryKeyStore } from './Utils/fork-compat.js';
|
|
14
14
|
export { PHONENUMBER_MCC } from './Utils/phonenumber-mcc.js';
|
|
15
|
+
export * from './Utils/aunu-handler.js';
|
|
16
|
+
export * from './Utils/builders.js';
|
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.4",
|
|
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",
|