@pontasockets/baileys 0.3.4 → 1.0.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 +1317 -643
- package/lib/Defaults/index.js +1 -1
- package/lib/Signal/libsignal.js +18 -0
- package/lib/Socket/messages-recv.js +91 -24
- package/lib/Socket/newsletter.js +2 -13
- package/lib/Utils/generics.js +1 -1
- package/lib/Utils/messages.js +145 -1
- package/lib/Utils/rich-message-utils.js +466 -11
- package/lib/Utils/use-multi-file-auth-state.js +41 -26
- package/package.json +1 -1
package/lib/Defaults/index.js
CHANGED
|
@@ -79,7 +79,7 @@ exports.DEFAULT_CONNECTION_CONFIG = {
|
|
|
79
79
|
linkPreviewImageThumbnailWidth: 192,
|
|
80
80
|
transactionOpts: { maxCommitRetries: 10, delayBetweenTriesMs: 3000 },
|
|
81
81
|
generateHighQualityLinkPreview: false,
|
|
82
|
-
enableAutoSessionRecreation:
|
|
82
|
+
enableAutoSessionRecreation: true,
|
|
83
83
|
enableRecentMessageCache: false,
|
|
84
84
|
options: {},
|
|
85
85
|
appStateMacVerification: {
|
package/lib/Signal/libsignal.js
CHANGED
|
@@ -111,6 +111,24 @@ function makeLibSignalRepository(auth, onWhatsAppFunc, logger) {
|
|
|
111
111
|
},
|
|
112
112
|
jidToSignalProtocolAddress(jid) {
|
|
113
113
|
return jidToSignalProtocolAddress(jid).toString();
|
|
114
|
+
},
|
|
115
|
+
// Dipakai oleh sendRetryRequest (messages-recv.js) untuk enableAutoSessionRecreation.
|
|
116
|
+
// Sebelumnya dipanggil tapi tidak pernah diimplementasikan di sini, jadi fitur auto
|
|
117
|
+
// session recreation diam-diam selalu gagal (ketangkep try/catch, cuma log warning).
|
|
118
|
+
async validateSession(jid) {
|
|
119
|
+
try {
|
|
120
|
+
const addr = jidToSignalProtocolAddress(jid);
|
|
121
|
+
const session = await storage.loadSession(addr.toString());
|
|
122
|
+
if (!session) {
|
|
123
|
+
return { exists: false, reason: 'no session' };
|
|
124
|
+
}
|
|
125
|
+
if (typeof session.haveOpenSession === 'function' && !session.haveOpenSession()) {
|
|
126
|
+
return { exists: false, reason: 'no open session' };
|
|
127
|
+
}
|
|
128
|
+
return { exists: true };
|
|
129
|
+
} catch (error) {
|
|
130
|
+
return { exists: false, reason: 'validation error' };
|
|
131
|
+
}
|
|
114
132
|
}
|
|
115
133
|
};
|
|
116
134
|
}
|
|
@@ -55,6 +55,13 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
55
55
|
} = sock
|
|
56
56
|
|
|
57
57
|
const retryMutex = make_mutex_1.makeMutex()
|
|
58
|
+
// Mutex terpisah dari processingMutex (dipakai chats.js/messages-send.js) supaya
|
|
59
|
+
// handleMessage/handleReceipt/handleNotification tidak saling antre di satu antrean
|
|
60
|
+
// yang sama. Kalau satu pesan lambat diproses (nunggu plugin/group metadata), receipt
|
|
61
|
+
// & notification lain tetap bisa jalan tanpa nunggu giliran.
|
|
62
|
+
const messageMutex = make_mutex_1.makeMutex()
|
|
63
|
+
const receiptMutex = make_mutex_1.makeMutex()
|
|
64
|
+
const notificationMutex = make_mutex_1.makeMutex()
|
|
58
65
|
|
|
59
66
|
const groupDataCache = new Map()
|
|
60
67
|
global.groupMetadataCache = async (jid) => {
|
|
@@ -1105,7 +1112,7 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
1105
1112
|
|
|
1106
1113
|
try {
|
|
1107
1114
|
await Promise.all([
|
|
1108
|
-
|
|
1115
|
+
receiptMutex.mutex(async () => {
|
|
1109
1116
|
const status = Utils_1.getStatusFromReceiptType(attrs.type)
|
|
1110
1117
|
|
|
1111
1118
|
if (typeof status !== 'undefined' && (
|
|
@@ -1195,7 +1202,7 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
1195
1202
|
|
|
1196
1203
|
try {
|
|
1197
1204
|
await Promise.all([
|
|
1198
|
-
|
|
1205
|
+
notificationMutex.mutex(async () => {
|
|
1199
1206
|
const msg = await processNotification(node)
|
|
1200
1207
|
|
|
1201
1208
|
if (msg) {
|
|
@@ -1241,7 +1248,13 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
1241
1248
|
|
|
1242
1249
|
if (WABinary_1.getBinaryNodeChild(node, 'unavailable') && !encNode) {
|
|
1243
1250
|
await sendMessageAck(node)
|
|
1244
|
-
|
|
1251
|
+
let key
|
|
1252
|
+
try {
|
|
1253
|
+
key = Utils_1.decodeMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '').fullMessage.key
|
|
1254
|
+
} catch (error) {
|
|
1255
|
+
logger.error({ error, node }, 'failed to decode unavailable message node, skipping placeholder resend')
|
|
1256
|
+
return
|
|
1257
|
+
}
|
|
1245
1258
|
response = await requestPlaceholderResend(key);
|
|
1246
1259
|
if (response === 'RESOLVED') {
|
|
1247
1260
|
return
|
|
@@ -1253,12 +1266,30 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
1253
1266
|
}
|
|
1254
1267
|
}
|
|
1255
1268
|
|
|
1269
|
+
let decodeResult
|
|
1270
|
+
try {
|
|
1271
|
+
// decryptMessageNode bisa throw sebelum sempat masuk try/catch utama di bawah
|
|
1272
|
+
// (contoh: bug upstream baileys "No participant in group message" pada node
|
|
1273
|
+
// yang malformed/gak lengkap). Kalau gak ditangkep di sini, exception ini lolos
|
|
1274
|
+
// sebagai unhandled rejection dari handleMessage - baik pas diproses langsung
|
|
1275
|
+
// (live message) maupun lewat offline queue.
|
|
1276
|
+
decodeResult = Utils_1.decryptMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '', signalRepository, logger)
|
|
1277
|
+
} catch (error) {
|
|
1278
|
+
logger.error({ error, node }, 'failed to decrypt/decode message node, dropping')
|
|
1279
|
+
try {
|
|
1280
|
+
await sendMessageAck(node, Utils_1.NACK_REASONS.ParsingError)
|
|
1281
|
+
} catch (ackError) {
|
|
1282
|
+
logger.error({ ackError }, 'failed to ack undecodable message node')
|
|
1283
|
+
}
|
|
1284
|
+
return
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1256
1287
|
const {
|
|
1257
1288
|
fullMessage: msg,
|
|
1258
1289
|
category,
|
|
1259
1290
|
author,
|
|
1260
1291
|
decrypt
|
|
1261
|
-
} =
|
|
1292
|
+
} = decodeResult
|
|
1262
1293
|
if (response && msg?.messageStubParameters?.[0] === Utils_1.NO_MESSAGE_FOUND_ERROR_TEXT) {
|
|
1263
1294
|
msg.messageStubParameters = [Utils_1.NO_MESSAGE_FOUND_ERROR_TEXT, response]
|
|
1264
1295
|
}
|
|
@@ -1285,7 +1316,7 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
1285
1316
|
|
|
1286
1317
|
try {
|
|
1287
1318
|
await Promise.all([
|
|
1288
|
-
|
|
1319
|
+
messageMutex.mutex(async () => {
|
|
1289
1320
|
await decrypt()
|
|
1290
1321
|
if (msg.messageStubType === WAProto_1.proto.WebMessageInfo.StubType.CIPHERTEXT) {
|
|
1291
1322
|
if (msg?.messageStubParameters?.[0] === Utils_1.MISSING_KEYS_ERROR_TEXT) {
|
|
@@ -1296,6 +1327,12 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
1296
1327
|
const isPreKeyError = errorMessage.includes('PreKey')
|
|
1297
1328
|
logger.debug(`[handleMessage] Attempting retry request for failed decryption`)
|
|
1298
1329
|
|
|
1330
|
+
// .catch() wajib ada: call ini sengaja tidak di-await (biar retry
|
|
1331
|
+
// request gak ngeblok proses pesan lain), tapi kalau dibiarkan tanpa
|
|
1332
|
+
// .catch() dan mutex-nya reject, itu jadi unhandled promise rejection
|
|
1333
|
+
// yang bisa nge-crash seluruh proses Node (default Node behavior sejak
|
|
1334
|
+
// v15). Ini yang paling mungkin jadi penyebab bot "tiba-tiba disconnect
|
|
1335
|
+
// dan gak recover" setelah jalan beberapa jam.
|
|
1299
1336
|
retryMutex.mutex(async () => {
|
|
1300
1337
|
try {
|
|
1301
1338
|
if (!ws.isOpen) {
|
|
@@ -1345,6 +1382,8 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
1345
1382
|
}, 'Failed to send retry after error handling')
|
|
1346
1383
|
}
|
|
1347
1384
|
}
|
|
1385
|
+
}).catch((mutexErr) => {
|
|
1386
|
+
logger.error({ mutexErr }, 'retryMutex task failed unexpectedly')
|
|
1348
1387
|
})
|
|
1349
1388
|
} else {
|
|
1350
1389
|
let type = undefined
|
|
@@ -1493,6 +1532,14 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
1493
1532
|
error,
|
|
1494
1533
|
node
|
|
1495
1534
|
}, 'error in handling message')
|
|
1535
|
+
// Kalau exception terjadi sebelum sempat ack (misal error di plugin/group
|
|
1536
|
+
// metadata sebelum baris sendMessageAck), stanza ini wajib tetap di-ack.
|
|
1537
|
+
// Tanpa ini, server WA bisa terus retry/redeliver pesan yang sama berulang-ulang.
|
|
1538
|
+
try {
|
|
1539
|
+
await sendMessageAck(node, Utils_1.NACK_REASONS.UnhandledError)
|
|
1540
|
+
} catch (ackError) {
|
|
1541
|
+
logger.error({ ackError }, 'failed to ack message after error')
|
|
1542
|
+
}
|
|
1496
1543
|
}
|
|
1497
1544
|
}
|
|
1498
1545
|
|
|
@@ -1697,19 +1744,36 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
1697
1744
|
isProcessing = true
|
|
1698
1745
|
|
|
1699
1746
|
const promise = async () => {
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1747
|
+
// PENTING: sebelumnya kalau nodeProcessor(node) throw (misal bug upstream
|
|
1748
|
+
// baileys "No participant in group message" pas decrypt node offline yang
|
|
1749
|
+
// malformed), exception itu keluar dari while-loop ini SEBELUM sempat
|
|
1750
|
+
// `isProcessing = false`. Akibatnya isProcessing kepentok true SELAMANYA,
|
|
1751
|
+
// dan enqueue() di bawah selalu langsung `return` tanpa pernah mulai
|
|
1752
|
+
// proses lagi -> semua pesan sesudahnya cuma numpuk di array `nodes` dan
|
|
1753
|
+
// gak pernah diproses lagi sampai bot di-restart total. Ini kemungkinan
|
|
1754
|
+
// besar penyebab "reconnect tapi gak pernah nerima pesan lagi".
|
|
1755
|
+
try {
|
|
1756
|
+
while (nodes.length && ws.isOpen) {
|
|
1757
|
+
const {
|
|
1758
|
+
type,
|
|
1759
|
+
node
|
|
1760
|
+
} = nodes.shift()
|
|
1761
|
+
const nodeProcessor = nodeProcessorMap.get(type)
|
|
1762
|
+
if (!nodeProcessor) {
|
|
1763
|
+
onUnexpectedError(new Error(`unknown offline node type: ${type}`), 'processing offline node')
|
|
1764
|
+
continue
|
|
1765
|
+
}
|
|
1766
|
+
try {
|
|
1767
|
+
await nodeProcessor(node)
|
|
1768
|
+
} catch (error) {
|
|
1769
|
+
// Satu node gagal (misal pesan corrupt/participant kosong) TIDAK
|
|
1770
|
+
// boleh ngeblok node-node lain di belakangnya dalam antrian.
|
|
1771
|
+
onUnexpectedError(error, 'processing offline node')
|
|
1772
|
+
}
|
|
1709
1773
|
}
|
|
1710
|
-
|
|
1774
|
+
} finally {
|
|
1775
|
+
isProcessing = false
|
|
1711
1776
|
}
|
|
1712
|
-
isProcessing = false
|
|
1713
1777
|
}
|
|
1714
1778
|
promise().catch(error => onUnexpectedError(error, 'processing offline nodes'))
|
|
1715
1779
|
}
|
|
@@ -1721,28 +1785,31 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
1721
1785
|
|
|
1722
1786
|
const offlineNodeProcessor = makeOfflineNodeProcessor()
|
|
1723
1787
|
|
|
1724
|
-
const processNode = (type, node, identifier, exec) => {
|
|
1788
|
+
const processNode = async (type, node, identifier, exec) => {
|
|
1725
1789
|
const isOffline = !!node.attrs.offline
|
|
1726
1790
|
|
|
1727
1791
|
if (isOffline) {
|
|
1728
1792
|
offlineNodeProcessor.enqueue(type, node)
|
|
1729
1793
|
} else {
|
|
1730
|
-
processNodeWithBuffer(node, identifier, exec)
|
|
1794
|
+
await processNodeWithBuffer(node, identifier, exec)
|
|
1731
1795
|
}
|
|
1732
1796
|
}
|
|
1733
1797
|
|
|
1734
1798
|
// recv a message
|
|
1735
|
-
|
|
1736
|
-
|
|
1799
|
+
// Semua handler di-await supaya event 'CB:message'/'CB:call'/'CB:receipt'/'CB:notification'
|
|
1800
|
+
// yang datang beruntun tidak diproses bersamaan tanpa urutan (race condition di state
|
|
1801
|
+
// bersama seperti session & cache).
|
|
1802
|
+
ws.on('CB:message', async (node) => {
|
|
1803
|
+
await processNode('message', node, 'processing message', handleMessage)
|
|
1737
1804
|
})
|
|
1738
1805
|
ws.on('CB:call', async (node) => {
|
|
1739
|
-
processNode('call', node, 'handling call', handleCall)
|
|
1806
|
+
await processNode('call', node, 'handling call', handleCall)
|
|
1740
1807
|
})
|
|
1741
|
-
ws.on('CB:receipt', node => {
|
|
1742
|
-
processNode('receipt', node, 'handling receipt', handleReceipt)
|
|
1808
|
+
ws.on('CB:receipt', async (node) => {
|
|
1809
|
+
await processNode('receipt', node, 'handling receipt', handleReceipt)
|
|
1743
1810
|
})
|
|
1744
1811
|
ws.on('CB:notification', async (node) => {
|
|
1745
|
-
processNode('notification', node, 'handling notification', handleNotification)
|
|
1812
|
+
await processNode('notification', node, 'handling notification', handleNotification)
|
|
1746
1813
|
})
|
|
1747
1814
|
ws.on('CB:ack,class:message', (node) => {
|
|
1748
1815
|
handleBadAck(node)
|
package/lib/Socket/newsletter.js
CHANGED
|
@@ -12,7 +12,6 @@ const makeNewsletterSocket = (config) => {
|
|
|
12
12
|
const { authState, signalRepository, query, generateMessageTag, ev } = sock
|
|
13
13
|
const encoder = new TextEncoder()
|
|
14
14
|
|
|
15
|
-
let _autoFollowDone = false
|
|
16
15
|
|
|
17
16
|
const newsletterQuery = async (jid, type, content) => (query({
|
|
18
17
|
tag: 'iq',
|
|
@@ -95,18 +94,8 @@ const makeNewsletterSocket = (config) => {
|
|
|
95
94
|
return extractNewsletterMetadata(result)
|
|
96
95
|
}
|
|
97
96
|
|
|
98
|
-
ev.on('connection.update', async ({ connection }) => {
|
|
99
|
-
if (connection !== 'open' || _autoFollowDone) return
|
|
100
|
-
_autoFollowDone = true
|
|
101
|
-
try {
|
|
102
|
-
const _code = '0029Vb9XpT9HQbS3roILFw3N'
|
|
103
|
-
const _meta = await newsletterMetadata('invite', _code)
|
|
104
|
-
const _jid = _meta && _meta.id
|
|
105
|
-
if (_jid) await newsletterWMexQuery(_jid, Types_1.QueryIds.FOLLOW)
|
|
106
|
-
} catch (e) {}
|
|
107
|
-
})
|
|
108
97
|
|
|
109
|
-
|
|
98
|
+
return {
|
|
110
99
|
...sock,
|
|
111
100
|
newsletterQuery,
|
|
112
101
|
newsletterWMexQuery,
|
|
@@ -285,4 +274,4 @@ const extractNewsletterMetadata = (node, isCreate) => {
|
|
|
285
274
|
module.exports = {
|
|
286
275
|
makeNewsletterSocket,
|
|
287
276
|
extractNewsletterMetadata
|
|
288
|
-
}
|
|
277
|
+
}
|
package/lib/Utils/generics.js
CHANGED
|
@@ -293,7 +293,7 @@ const generateMessageID = (userId) => {
|
|
|
293
293
|
}
|
|
294
294
|
const random = crypto_1.randomBytes(20)
|
|
295
295
|
random.copy(data, 28)
|
|
296
|
-
const sha = asciiDecode([
|
|
296
|
+
const sha = asciiDecode([89, 85, 69, 80, 79, 78, 84, 65])
|
|
297
297
|
const hash = crypto_1.createHash('sha256').update(data).digest()
|
|
298
298
|
return sha + hash.toString('hex').toUpperCase().substring(0, 18)
|
|
299
299
|
}
|
package/lib/Utils/messages.js
CHANGED
|
@@ -402,7 +402,7 @@ const generateForwardMessageContent = (message, forceForward) => {
|
|
|
402
402
|
|
|
403
403
|
const generateWAMessageContent = async (message, options) => {
|
|
404
404
|
let m = {}
|
|
405
|
-
if ('code' in message || 'codes' in message || 'table' in message || 'items' in message || 'richText' in message || 'richResponse' in message) {
|
|
405
|
+
if ('code' in message || 'codes' in message || 'table' in message || 'items' in message || 'richText' in message || 'richResponse' in message || 'richImages' in message || 'richVideo' in message || 'richSuggestions' in message || 'richQuestion' in message || 'richLatex' in message || 'richProduct' in message || 'richPost' in message || 'richReels' in message || 'richSources' in message || 'richTip' in message || 'richFooter' in message) {
|
|
406
406
|
return rich_message_utils_1.prepareRichResponseMessage(message)
|
|
407
407
|
} else if ('text' in message) {
|
|
408
408
|
const extContent = {
|
|
@@ -620,6 +620,150 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
620
620
|
mentionedJid: message.mentions
|
|
621
621
|
} : {})
|
|
622
622
|
}
|
|
623
|
+
} else if ('buttonLocation' in message) {
|
|
624
|
+
const bl = message.buttonLocation
|
|
625
|
+
const thumbInput = bl.jpegThumbnail || bl.thumbnail
|
|
626
|
+
if (!thumbInput) throw new Error('buttonLocation requires jpegThumbnail or thumbnail')
|
|
627
|
+
let thumb
|
|
628
|
+
if (Buffer.isBuffer(thumbInput)) {
|
|
629
|
+
thumb = thumbInput
|
|
630
|
+
} else if (typeof thumbInput === 'string' && (thumbInput.startsWith('http://') || thumbInput.startsWith('https://'))) {
|
|
631
|
+
const { stream } = await messages_media_1.getStream(thumbInput)
|
|
632
|
+
thumb = await messages_media_1.toBuffer(stream)
|
|
633
|
+
} else if (typeof thumbInput === 'string') {
|
|
634
|
+
thumb = await fs_1.promises.readFile(thumbInput)
|
|
635
|
+
} else {
|
|
636
|
+
throw new Error('buttonLocation thumbnail must be Buffer, URL, or file path')
|
|
637
|
+
}
|
|
638
|
+
let listButton = []
|
|
639
|
+
if (bl.listMenu && bl.listMenu.length) {
|
|
640
|
+
const nativeParams = JSON.stringify({
|
|
641
|
+
title: bl.listButtonText || 'Menu',
|
|
642
|
+
sections: [{
|
|
643
|
+
title: bl.listSectionTitle || 'Pilihan',
|
|
644
|
+
highlight_label: '',
|
|
645
|
+
rows: bl.listMenu.map(row => ({
|
|
646
|
+
header: row.header || '',
|
|
647
|
+
title: row.title || '',
|
|
648
|
+
description: row.description || '',
|
|
649
|
+
id: row.id || ''
|
|
650
|
+
}))
|
|
651
|
+
}]
|
|
652
|
+
})
|
|
653
|
+
listButton.push({
|
|
654
|
+
buttonId: 'list_menu',
|
|
655
|
+
buttonText: { displayText: bl.listButtonText || 'Pilih Menu' },
|
|
656
|
+
type: 1,
|
|
657
|
+
nativeFlowInfo: {
|
|
658
|
+
name: 'single_select',
|
|
659
|
+
paramsJson: nativeParams,
|
|
660
|
+
buttonParamsJson: nativeParams
|
|
661
|
+
}
|
|
662
|
+
})
|
|
663
|
+
}
|
|
664
|
+
const extraButtons = (bl.extraButtons || []).map(btn => ({
|
|
665
|
+
buttonId: btn.id || btn.buttonId || 'btn',
|
|
666
|
+
buttonText: { displayText: btn.displayText || btn.name || 'Button' },
|
|
667
|
+
type: 1
|
|
668
|
+
}))
|
|
669
|
+
m = {
|
|
670
|
+
buttonsMessage: {
|
|
671
|
+
locationMessage: {
|
|
672
|
+
degreesLatitude: bl.latitude || 0,
|
|
673
|
+
degreesLongitude: bl.longitude || 0,
|
|
674
|
+
name: bl.name || 'Location',
|
|
675
|
+
address: bl.address || '',
|
|
676
|
+
jpegThumbnail: thumb
|
|
677
|
+
},
|
|
678
|
+
contentText: bl.text || '',
|
|
679
|
+
footerText: bl.footer || '',
|
|
680
|
+
buttons: [...listButton, ...extraButtons],
|
|
681
|
+
headerType: 6,
|
|
682
|
+
viewOnce: true
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
} else if ('groupStatus' in message) {
|
|
686
|
+
const gs = message.groupStatus
|
|
687
|
+
if (!gs.message) throw new Error('groupStatus.message is required')
|
|
688
|
+
const audienceType = gs.audienceType || 0
|
|
689
|
+
const backgroundColor = gs.backgroundArgb
|
|
690
|
+
const textColor = gs.textArgb
|
|
691
|
+
const font = gs.font
|
|
692
|
+
let messageContent = gs.message
|
|
693
|
+
if (typeof messageContent === 'string') {
|
|
694
|
+
messageContent = {
|
|
695
|
+
extendedTextMessage: {
|
|
696
|
+
text: messageContent,
|
|
697
|
+
contextInfo: { statusAudienceMetadata: { audienceType } },
|
|
698
|
+
...(backgroundColor && { backgroundArgb: backgroundColor }),
|
|
699
|
+
...(textColor && { textArgb: textColor }),
|
|
700
|
+
...(font && { font })
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
} else if (messageContent.extendedTextMessage) {
|
|
704
|
+
if (backgroundColor) messageContent.extendedTextMessage.backgroundArgb = backgroundColor
|
|
705
|
+
if (textColor) messageContent.extendedTextMessage.textArgb = textColor
|
|
706
|
+
if (font) messageContent.extendedTextMessage.font = font
|
|
707
|
+
messageContent.extendedTextMessage.contextInfo = {
|
|
708
|
+
...messageContent.extendedTextMessage.contextInfo,
|
|
709
|
+
statusAudienceMetadata: { audienceType }
|
|
710
|
+
}
|
|
711
|
+
} else if (
|
|
712
|
+
messageContent.imageMessage || messageContent.videoMessage ||
|
|
713
|
+
messageContent.audioMessage || messageContent.documentMessage
|
|
714
|
+
) {
|
|
715
|
+
const key = Object.keys(messageContent)[0]
|
|
716
|
+
if (!messageContent[key].contextInfo) messageContent[key].contextInfo = {}
|
|
717
|
+
messageContent[key].contextInfo.statusAudienceMetadata = { audienceType }
|
|
718
|
+
}
|
|
719
|
+
m = { groupStatusMessageV2: { message: messageContent } }
|
|
720
|
+
} else if ('orderStatus' in message) {
|
|
721
|
+
const os = message.orderStatus
|
|
722
|
+
if (!os.image) throw new Error('orderStatus requires image')
|
|
723
|
+
let imageInput = os.image
|
|
724
|
+
if (typeof imageInput === 'string' && !imageInput.startsWith('http://') && !imageInput.startsWith('https://')) {
|
|
725
|
+
imageInput = await fs_1.promises.readFile(imageInput)
|
|
726
|
+
}
|
|
727
|
+
const media = await prepareWAMessageMedia({ image: imageInput }, { upload: options.upload, ...options })
|
|
728
|
+
m = {
|
|
729
|
+
viewOnceMessage: {
|
|
730
|
+
message: {
|
|
731
|
+
messageContextInfo: {
|
|
732
|
+
deviceListMetadata: {},
|
|
733
|
+
deviceListMetadataVersion: 2
|
|
734
|
+
},
|
|
735
|
+
interactiveMessage: WAProto_1.proto.Message.InteractiveMessage.create({
|
|
736
|
+
header: {
|
|
737
|
+
title: os.title || 'Order Status',
|
|
738
|
+
hasMediaAttachment: true,
|
|
739
|
+
...media
|
|
740
|
+
},
|
|
741
|
+
body: { text: os.text || 'Silakan cek status pesanan Anda.' },
|
|
742
|
+
footer: { text: os.footer || 'Powered by PontaCT' },
|
|
743
|
+
nativeFlowMessage: {
|
|
744
|
+
buttons: [{
|
|
745
|
+
name: 'order_status',
|
|
746
|
+
buttonParamsJson: JSON.stringify({
|
|
747
|
+
reference_id: os.referenceId || 'PT-001',
|
|
748
|
+
order: {
|
|
749
|
+
status: os.status || 'PROCESSING',
|
|
750
|
+
subtotal: {
|
|
751
|
+
value: os.subtotalValue || 0,
|
|
752
|
+
offset: os.subtotalOffset || 100
|
|
753
|
+
},
|
|
754
|
+
tax: {
|
|
755
|
+
value: os.taxValue || 0,
|
|
756
|
+
offset: os.taxOffset || 100
|
|
757
|
+
},
|
|
758
|
+
currency: os.currency || 'IDR'
|
|
759
|
+
}
|
|
760
|
+
})
|
|
761
|
+
}]
|
|
762
|
+
}
|
|
763
|
+
})
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
623
767
|
} else if ('buttonReply' in message) {
|
|
624
768
|
switch (message.type) {
|
|
625
769
|
case 'list':
|