@pontasockets/baileys 0.2.0 โ†’ 0.2.3

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 CHANGED
@@ -62,6 +62,7 @@ This fork introduces targeted custom patches on top of the original Baileys libr
62
62
  | # | Patch | Description |
63
63
  |---|-------|-------------|
64
64
  | `01` | ๐Ÿค– **Code Block Message** | Send syntax-highlighted code via `{ code, language }` โ€” renders as AI bot message in WhatsApp |
65
+ | `02` | ๐Ÿ“Š **Table Message** | Send structured tables via `{ table: { title?, headers, rows } }` โ€” renders as rich table in WhatsApp |
65
66
  | `02` | ๐Ÿชช **Custom Message ID** | All outgoing message IDs prefixed with `PONTASOCKETS` |
66
67
  | `03` | ๐Ÿ› **LID Bug Fixes** | Full fix for participant, mentionedJid, sender, and group admin LID resolution |
67
68
  | `04` | ๐Ÿ“ฆ **makeInMemoryStore Fix** | Corrected store behavior for stable session handling |
@@ -240,6 +241,8 @@ await sock.sendMessage(jid, {
240
241
 
241
242
  > Renders as a syntax-highlighted code block in WhatsApp (AI bot style message)
242
243
 
244
+ **Single Code Block**
245
+
243
246
  ```javascript
244
247
  // JavaScript
245
248
  await sock.sendMessage(jid, {
@@ -260,6 +263,39 @@ await sock.sendMessage(jid, {
260
263
  }, { quoted: message })
261
264
  ```
262
265
 
266
+ **Multiple Code Blocks in One Message**
267
+
268
+ > Gunakan key `codes` (array) untuk kirim lebih dari satu code block sekaligus dalam satu pesan
269
+
270
+ ```javascript
271
+ // 2 bahasa berbeda dalam 1 pesan
272
+ await sock.sendMessage(jid, {
273
+ codes: [
274
+ { code: 'console.log("Hello World")', language: 'javascript' },
275
+ { code: 'print("Hello World")', language: 'python' }
276
+ ]
277
+ }, { quoted: message })
278
+
279
+ // Input vs Output
280
+ await sock.sendMessage(jid, {
281
+ codes: [
282
+ { code: 'SELECT * FROM users WHERE id = 1;', language: 'sql' },
283
+ { code: '{ "id": 1, "name": "Ponta", "role": "admin" }', language: 'json' }
284
+ ]
285
+ }, { quoted: message })
286
+
287
+ // Banyak snippet sekaligus
288
+ await sock.sendMessage(jid, {
289
+ codes: [
290
+ { code: 'npm install @pontasockets/baileys', language: 'bash' },
291
+ { code: 'import makeWASocket from "@pontasockets/baileys"', language: 'javascript' },
292
+ { code: 'const sock = makeWASocket({ printQRInTerminal: true })', language: 'javascript' }
293
+ ]
294
+ }, { quoted: message })
295
+ ```
296
+
297
+ > **Note:** Key `code` (single) dan `codes` (array) keduanya tetap support โ€” tidak breaking change.
298
+
263
299
  **Supported Languages:**
264
300
 
265
301
  | Language | Keys |
@@ -277,6 +313,85 @@ await sock.sendMessage(jid, {
277
313
  | HTML | `html` |
278
314
  | PowerShell | `powershell` ยท `ps1` |
279
315
  | CMD | `cmd` ยท `bat` |
316
+ | SQL | `sql` |
317
+ | JSON | `json` |
318
+
319
+ </details>
320
+
321
+ <details>
322
+ <summary><b>๐Ÿ“Š Table Message <kbd>NEW</kbd></b></summary>
323
+ <br>
324
+
325
+ > Renders as a structured table in WhatsApp (AI bot style rich message)
326
+
327
+ **Simple Table**
328
+
329
+ ```javascript
330
+ await sock.sendMessage(jid, {
331
+ table: {
332
+ title: 'Daftar User',
333
+ headers: ['Name', 'Role', 'Status'],
334
+ rows: [
335
+ ['Ponta', 'Admin', 'Active'],
336
+ ['Yue', 'Member', 'Idle'],
337
+ ['Frieren', 'Guest', 'Offline']
338
+ ]
339
+ }
340
+ }, { quoted: message })
341
+ ```
342
+
343
+ **Table tanpa title**
344
+
345
+ ```javascript
346
+ await sock.sendMessage(jid, {
347
+ table: {
348
+ headers: ['Command', 'Description'],
349
+ rows: [
350
+ ['.ping', 'Cek latency bot'],
351
+ ['.info', 'Info bot'],
352
+ ['.help', 'List semua command']
353
+ ]
354
+ }
355
+ }, { quoted: message })
356
+ ```
357
+
358
+ **Table tanpa header (data only)**
359
+
360
+ ```javascript
361
+ await sock.sendMessage(jid, {
362
+ table: {
363
+ rows: [
364
+ ['RAM', '512 MB'],
365
+ ['CPU', '4 Core'],
366
+ ['Uptime', '99.9%']
367
+ ]
368
+ }
369
+ }, { quoted: message })
370
+ ```
371
+
372
+ **Mixed: Table + Code Block dalam 1 pesan**
373
+
374
+ > Gunakan key `items` untuk gabungkan berbagai tipe dalam urutan bebas
375
+
376
+ ```javascript
377
+ await sock.sendMessage(jid, {
378
+ items: [
379
+ {
380
+ table: {
381
+ title: 'Query Result',
382
+ headers: ['id', 'name', 'score'],
383
+ rows: [
384
+ ['1', 'Ponta', '98'],
385
+ ['2', 'Yue', '87']
386
+ ]
387
+ }
388
+ },
389
+ { code: 'SELECT * FROM users ORDER BY score DESC', language: 'sql' }
390
+ ]
391
+ }, { quoted: message })
392
+ ```
393
+
394
+ > **Note:** `items` array juga bisa diisi full code blocks saja (pengganti `codes`), atau full tables saja, atau campuran keduanya โ€” bebas urutannya.
280
395
 
281
396
  </details>
282
397
 
@@ -1348,8 +1348,26 @@ const makeMessagesRecvSocket = (config) => {
1348
1348
  })
1349
1349
  } else {
1350
1350
  let type = undefined
1351
+ // FIX: Jangan fallback ke me.id saat LID tidak bisa di-resolve.
1352
+ // Fallback ke me.id menyebabkan semua pesan LID private chat
1353
+ // punya participant = JID bot sendiri, sehingga owner check lolos
1354
+ // untuk semua user yang menggunakan LID addressing.
1351
1355
  if (msg.key.participant && msg.key.participant.endsWith('@lid')) {
1352
- msg.key.participant = node.attrs.participant_pn || authState.creds.me.id;
1356
+ if (node.attrs.participant_pn) {
1357
+ // Gunakan nomor telepon asli jika tersedia
1358
+ msg.key.participant = WABinary_1.jidNormalizedUser(node.attrs.participant_pn);
1359
+ } else {
1360
+ // Coba resolve dari lidMapping, jika gagal pakai remoteJid
1361
+ // (lebih aman daripada me.id yang menyebabkan false owner match)
1362
+ try {
1363
+ const resolvedPn = await signalRepository.lidMapping.getPNForLID(msg.key.participant);
1364
+ msg.key.participant = resolvedPn
1365
+ ? WABinary_1.jidNormalizedUser(resolvedPn)
1366
+ : WABinary_1.jidNormalizedUser(msg.key.remoteJid);
1367
+ } catch (_) {
1368
+ msg.key.participant = WABinary_1.jidNormalizedUser(msg.key.remoteJid);
1369
+ }
1370
+ }
1353
1371
  }
1354
1372
  if (WABinary_1.isJidGroup(msg.key.remoteJid)) {
1355
1373
  const mtype = getTypeMessage(msg.message)
@@ -1418,7 +1436,33 @@ const makeMessagesRecvSocket = (config) => {
1418
1436
  }
1419
1437
  }
1420
1438
  if (!WABinary_1.isJidGroup(msg.key.remoteJid) && WABinary_1.isLidUser(msg.key.remoteJid)) {
1421
- msg.key.remoteJid = node.attrs.sender_pn || node.attrs.peer_recipient_pn;
1439
+ // FIX: private chat - resolve remoteJid dari LID ke JID
1440
+ const resolvedRemote = node.attrs.sender_pn || node.attrs.peer_recipient_pn;
1441
+ if (resolvedRemote) {
1442
+ msg.key.remoteJid = WABinary_1.jidNormalizedUser(resolvedRemote);
1443
+ } else {
1444
+ // Fallback: coba resolve dari lidMapping
1445
+ try {
1446
+ const resolvedPn = await signalRepository.lidMapping.getPNForLID(msg.key.remoteJid);
1447
+ if (resolvedPn) {
1448
+ msg.key.remoteJid = WABinary_1.jidNormalizedUser(resolvedPn);
1449
+ }
1450
+ } catch (_) {}
1451
+ }
1452
+ }
1453
+ // FIX: private chat - jika author masih LID, resolve ke JID
1454
+ if (!WABinary_1.isJidGroup(msg.key.remoteJid) && author && WABinary_1.isLidUser(author)) {
1455
+ const resolvedAuthor = node.attrs.sender_pn
1456
+ || node.attrs.participant_pn
1457
+ || node.attrs.peer_recipient_pn;
1458
+ if (resolvedAuthor) {
1459
+ author = WABinary_1.jidNormalizedUser(resolvedAuthor);
1460
+ } else {
1461
+ try {
1462
+ const resolvedPn = await signalRepository.lidMapping.getPNForLID(author);
1463
+ if (resolvedPn) author = WABinary_1.jidNormalizedUser(resolvedPn);
1464
+ } catch (_) {}
1465
+ }
1422
1466
  }
1423
1467
  let participant = msg.key.participant
1424
1468
  if (category === 'peer') {
@@ -68,13 +68,26 @@ function decodeMessageNode(stanza, meId, meLid) {
68
68
  if (!isMeLid(from)) {
69
69
  throw new boom_1.Boom('receipient present, but msg not from me', { data: stanza })
70
70
  }
71
- chat_id = recipient
71
+ // FIX: recipient mungkin LID juga, gunakan peer_recipient_pn jika ada
72
+ chat_id = stanza.attrs.peer_recipient_pn
73
+ ? WABinary_1.jidNormalizedUser(stanza.attrs.peer_recipient_pn)
74
+ : recipient
72
75
  }
73
76
  else {
74
- chat_id = from
77
+ // FIX: chat_id dari LID โ†’ resolve ke JID via sender_pn
78
+ chat_id = stanza.attrs.sender_pn
79
+ ? WABinary_1.jidNormalizedUser(stanza.attrs.sender_pn)
80
+ : stanza.attrs.peer_recipient_pn
81
+ ? WABinary_1.jidNormalizedUser(stanza.attrs.peer_recipient_pn)
82
+ : from
75
83
  }
76
84
  msg_type = 'chat'
77
- author = from
85
+ // FIX: untuk private LID chat, gunakan sender_pn/participant_pn sebagai author
86
+ // agar key.remoteJid dan author tidak berupa LID mentah
87
+ author = stanza.attrs.sender_pn
88
+ || stanza.attrs.participant_pn
89
+ || stanza.attrs.peer_recipient_pn
90
+ || from
78
91
  } else if (WABinary_1.isJidGroup(from)) {
79
92
  if (!participant) {
80
93
  throw new boom_1.Boom('No participant in group message')
@@ -102,7 +115,17 @@ function decodeMessageNode(stanza, meId, meLid) {
102
115
  } else {
103
116
  throw new boom_1.Boom('Unknown message type', { data: stanza })
104
117
  }
105
- const fromMe = WABinary_1.isJidNewsletter(from) ? !!stanza.attrs?.is_sender : WABinary_1.isLidUser(from) ? isMeLid(participant || stanza.attrs.from) : isMe(participant || stanza.attrs.from)
118
+ // FIX: untuk private chat (isJidUser), participant === from (bukan undefined),
119
+ // sehingga fallback ke stanza.attrs.from tidak diperlukan dan bisa menyebabkan
120
+ // fromMe=true saat pesan dari orang lain di kondisi edge case LID.
121
+ // Kita batasi fallback hanya untuk group (di mana participant memang bisa undefined).
122
+ const fromMe = WABinary_1.isJidNewsletter(from)
123
+ ? !!stanza.attrs?.is_sender
124
+ : WABinary_1.isLidUser(from)
125
+ ? isMeLid(participant != null ? participant : stanza.attrs.from)
126
+ : (WABinary_1.isJidGroup(from) || WABinary_1.isJidBroadcast(from))
127
+ ? isMe(participant != null ? participant : stanza.attrs.from)
128
+ : isMe(from)
106
129
  const pushname = stanza?.attrs?.notify
107
130
  const key = {
108
131
  remoteJid: chat_id,
@@ -409,7 +409,7 @@ const generateForwardMessageContent = (message, forceForward) => {
409
409
 
410
410
  const generateWAMessageContent = async (message, options) => {
411
411
  let m = {}
412
- if ('code' in message) {
412
+ if ('code' in message || 'codes' in message || 'table' in message || 'items' in message) {
413
413
  return rich_message_utils_1.prepareRichResponseMessage(message)
414
414
  } else if ('text' in message) {
415
415
  const extContent = {
@@ -24,7 +24,7 @@ const CodeHighlightType = {
24
24
  }
25
25
 
26
26
  const RichSubMessageType = {
27
- UNKNOWN: 0, TEXT: 2, CODE: 5
27
+ UNKNOWN: 0, TEXT: 2, TABLE: 4, CODE: 5
28
28
  }
29
29
 
30
30
  // โ”€โ”€โ”€ Keyword Sets โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
@@ -167,6 +167,22 @@ exports.tokenizeCode = tokenizeCode
167
167
  const toUnified = (submessages, uuid) => ({
168
168
  response_id: uuid,
169
169
  sections: submessages.map((submessage) => {
170
+ if (submessage.messageType === RichSubMessageType.TABLE) {
171
+ const tm = submessage.tableMetadata
172
+ return {
173
+ view_model: {
174
+ primitive: {
175
+ title: tm.title || null,
176
+ rows: tm.rows.map(row => ({
177
+ cells: row.items,
178
+ is_heading: row.isHeading || false
179
+ })),
180
+ __typename: 'GenAITableUXPrimitive'
181
+ },
182
+ __typename: 'GenAISingleLayoutViewModel'
183
+ }
184
+ }
185
+ }
170
186
  if (submessage.messageType === RichSubMessageType.CODE) {
171
187
  const cm = submessage.codeMetadata
172
188
  return {
@@ -218,35 +234,97 @@ const botMetadataCertificate = (length = 685) => {
218
234
  // โ”€โ”€โ”€ prepareRichResponseMessage โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
219
235
 
220
236
  const prepareRichResponseMessage = (content) => {
221
- const { code, language } = content
222
- const lang = language || 'javascript'
223
237
  const proto = WAProto_1.proto
224
238
 
225
- const codeBlocks = tokenizeCode(code, lang)
239
+ // Normalize ke array of items
240
+ // Support:
241
+ // { code, language } โ€” single code block
242
+ // { codes: [{ code, language }, ...] } โ€” multi code block
243
+ // { table: { title?, headers?, rows } } โ€” single table
244
+ // { items: [{ code, language } | { table }] } โ€” mixed, bebas urutan
245
+ let items = []
246
+
247
+ if (content.items) {
248
+ items = content.items
249
+ } else if (content.codes) {
250
+ items = content.codes.map(c => ({ code: c.code, language: c.language }))
251
+ } else if (content.table) {
252
+ items = [{ table: content.table }]
253
+ } else {
254
+ items = [{ code: content.code, language: content.language }]
255
+ }
256
+
257
+ const built = items.map(item => {
258
+ if (item.table) {
259
+ const { title, headers, rows } = item.table
226
260
 
227
- // Build submessage using AICommon proto classes
228
- const submessage = proto.AIRichResponseSubMessage.create({
229
- messageType: RichSubMessageType.CODE, // 5
230
- codeMetadata: proto.AIRichResponseCodeMetadata.create({
231
- codeLanguage: lang,
232
- codeBlocks: codeBlocks.map(b =>
233
- proto.AIRichResponseCodeMetadata.AIRichResponseCodeBlock.create({
234
- highlightType: b.highlightType,
235
- codeContent: b.codeContent
261
+ // Build rows: header row dulu (kalau ada), baru data rows
262
+ const allRows = []
263
+ if (headers && headers.length) {
264
+ allRows.push(proto.AIRichResponseTableMetadata.AIRichResponseTableRow.create({
265
+ items: headers,
266
+ isHeading: true
267
+ }))
268
+ }
269
+ for (const row of (rows || [])) {
270
+ allRows.push(proto.AIRichResponseTableMetadata.AIRichResponseTableRow.create({
271
+ items: row,
272
+ isHeading: false
273
+ }))
274
+ }
275
+
276
+ const tableMetadata = proto.AIRichResponseTableMetadata.create({
277
+ title: title || null,
278
+ rows: allRows
279
+ })
280
+
281
+ return {
282
+ submessage: proto.AIRichResponseSubMessage.create({
283
+ messageType: RichSubMessageType.TABLE, // 4
284
+ tableMetadata
285
+ }),
286
+ unified: {
287
+ messageType: RichSubMessageType.TABLE,
288
+ tableMetadata: {
289
+ title: title || null,
290
+ rows: [
291
+ ...(headers ? [{ items: headers, isHeading: true }] : []),
292
+ ...(rows || []).map(r => ({ items: r, isHeading: false }))
293
+ ]
294
+ }
295
+ }
296
+ }
297
+ }
298
+
299
+ // Default: code block
300
+ const lang = item.language || 'javascript'
301
+ const codeBlocks = tokenizeCode(item.code, lang)
302
+ return {
303
+ submessage: proto.AIRichResponseSubMessage.create({
304
+ messageType: RichSubMessageType.CODE, // 5
305
+ codeMetadata: proto.AIRichResponseCodeMetadata.create({
306
+ codeLanguage: lang,
307
+ codeBlocks: codeBlocks.map(b =>
308
+ proto.AIRichResponseCodeMetadata.AIRichResponseCodeBlock.create({
309
+ highlightType: b.highlightType,
310
+ codeContent: b.codeContent
311
+ })
312
+ )
236
313
  })
237
- )
238
- })
314
+ }),
315
+ unified: {
316
+ messageType: RichSubMessageType.CODE,
317
+ codeMetadata: { codeLanguage: lang, codeBlocks }
318
+ }
319
+ }
239
320
  })
240
321
 
241
322
  const uuid = crypto_1.randomUUID()
242
- const unified = toUnified([{
243
- messageType: RichSubMessageType.CODE,
244
- codeMetadata: { codeLanguage: lang, codeBlocks }
245
- }], uuid)
323
+ const unified = toUnified(built.map(b => b.unified), uuid)
246
324
 
247
325
  // Build AIRichResponseMessage using E2E proto class
248
326
  const richResponseMessage = proto.AIRichResponseMessage.create({
249
- submessages: [submessage],
327
+ submessages: built.map(b => b.submessage),
250
328
  messageType: 1, // AI_RICH_RESPONSE_TYPE_STANDARD
251
329
  unifiedResponse: proto.AIRichResponseUnifiedResponse
252
330
  ? proto.AIRichResponseUnifiedResponse.create({
@@ -101,4 +101,33 @@ module.exports = {
101
101
  isJidBot,
102
102
  transferDevice,
103
103
  jidNormalizedUser
104
- }
104
+ }
105
+ // ============================================================
106
+ // FIX: getSenderJid - helper aman untuk identifikasi pengirim
107
+ // ============================================================
108
+ /**
109
+ * Mendapatkan JID pengirim sebenarnya dari sebuah pesan.
110
+ * Aman digunakan untuk private chat maupun group.
111
+ *
112
+ * Cara pakai di bot:
113
+ * const { getSenderJid, areJidsSameUser } = require('@whiskeysockets/baileys')
114
+ * const sender = getSenderJid(msg)
115
+ * if (areJidsSameUser(sender, ownerNumber)) { ... }
116
+ *
117
+ * @param {object} msg - objek WebMessageInfo dari event messages.upsert
118
+ * @returns {string} JID pengirim yang sudah dinormalisasi
119
+ */
120
+ const getSenderJid = (msg) => {
121
+ const key = msg?.key
122
+ if (!key) return ''
123
+ // Pesan dari bot sendiri: remoteJid adalah lawan bicara
124
+ if (key.fromMe) return jidNormalizedUser(key.remoteJid)
125
+ // Group / broadcast: gunakan participant
126
+ if (isJidGroup(key.remoteJid) || isJidBroadcast(key.remoteJid)) {
127
+ return jidNormalizedUser(key.participant || key.remoteJid)
128
+ }
129
+ // Private chat: pengirim adalah remoteJid itu sendiri
130
+ return jidNormalizedUser(key.remoteJid)
131
+ }
132
+
133
+ exports.getSenderJid = getSenderJid
package/lib/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  console.log([
3
3
  '',
4
4
  ' \x1b[36mยป\x1b[0m \x1b[1m\x1b[97m@pontasockets/baileys\x1b[0m \x1b[36mยซ\x1b[0m',
5
- ' \x1b[35m๐ŸŒธ\x1b[0m \x1b[33m Thank you for using this Baileys\x1b[0m',
5
+ ' \x1b[35m๐Ÿ”ฅ\x1b[0m \x1b[33mTerimakasih sudah memakai baileys ini >.<\x1b[0m',
6
6
  '',
7
7
  ].join('\n'))
8
8
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pontasockets/baileys",
3
- "version": "0.2.0",
3
+ "version": "0.2.3",
4
4
  "description": "Baileys is a lightweight JavaScript library for interacting with the WhatsApp Web API using WebSocket.",
5
5
  "keywords": [
6
6
  "facebook",
@@ -17,7 +17,7 @@
17
17
  "url": "git+ssh://git@github.com/pontasockets/baileys.git"
18
18
  },
19
19
  "license": "MIT",
20
- "author": "Ponta CT",
20
+ "author": "Pontasockets",
21
21
  "main": "lib/index.js",
22
22
  "types": "lib/index.d.ts",
23
23
  "files": [