@badzz88/baileys 8.5.4 → 8.5.5
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 +1 -1
- package/WAProto/WAProto.proto +75 -1256
- package/WAProto/index.d.ts +19729 -36122
- package/WAProto/index.js +132193 -178538
- package/engine-requirements.js +15 -7
- package/lib/Defaults/index.js +14 -22
- package/lib/Signal/Group/group_cipher.js +5 -5
- package/lib/Signal/Group/keyhelper.js +4 -4
- package/lib/Signal/Group/sender-chain-key.js +2 -2
- package/lib/Signal/Group/sender-key-distribution-message.js +1 -1
- package/lib/Signal/Group/sender-key-message.js +3 -3
- package/lib/Signal/Group/sender-key-record.js +1 -1
- package/lib/Signal/Group/sender-message-key.js +5 -5
- package/lib/Signal/libsignal.js +28 -39
- package/lib/Signal/lid-mapping.js +8 -28
- package/lib/Socket/aigroups.js +5 -5
- package/lib/Socket/business.js +11 -3
- package/lib/Socket/chats.js +319 -668
- package/lib/Socket/communities.js +89 -85
- package/lib/Socket/graphql.js +228 -435
- package/lib/Socket/groups.js +211 -132
- package/lib/Socket/index.js +7 -9
- package/lib/Socket/interactive-handler.js +60 -117
- package/lib/Socket/interop.js +175 -56
- package/lib/Socket/luxu.js +387 -0
- package/lib/Socket/managed-account.js +48 -79
- package/lib/Socket/messages-recv.js +340 -898
- package/lib/Socket/messages-send.js +160 -462
- package/lib/Socket/mex.js +53 -40
- package/lib/Socket/newsletter.js +295 -291
- package/lib/Socket/privacy.js +265 -77
- package/lib/Socket/registration.js +157 -164
- package/lib/Socket/socket.js +199 -181
- package/lib/Socket/text-router.js +83 -0
- package/lib/Socket/username.js +227 -144
- package/lib/Store/keyed-db.js +108 -0
- package/lib/Store/make-cache-manager-store.js +2 -2
- package/lib/Store/make-in-memory-store.js +141 -25
- package/lib/Types/Auth.js +3 -3
- package/lib/Types/Label.js +1 -1
- package/lib/Types/LabelAssociation.js +1 -1
- package/lib/Types/Mex.js +112 -0
- package/lib/Types/Newsletter.js +65 -96
- package/lib/Types/State.js +4 -4
- package/lib/Types/index.js +1 -1
- package/lib/Utils/auth-utils.js +57 -52
- package/lib/Utils/browser-utils.js +1 -1
- package/lib/Utils/business.js +6 -2
- package/lib/Utils/chat-utils.js +38 -102
- package/lib/Utils/command-loader.d.ts +31 -0
- package/lib/Utils/command-loader.js +100 -0
- package/lib/Utils/companion-reg-client-utils.js +42 -0
- package/lib/Utils/crypto.js +84 -54
- package/lib/Utils/curve25519-js.js +275 -0
- package/lib/Utils/decode-wa-message.js +83 -316
- package/lib/Utils/event-buffer.js +61 -37
- package/lib/Utils/generics.js +103 -157
- package/lib/Utils/group-history.js +12 -3
- package/lib/Utils/history.js +15 -15
- package/lib/Utils/index.js +5 -1
- package/lib/Utils/link-preview.js +8 -3
- package/lib/Utils/lt-hash.js +35 -2
- package/lib/Utils/make-mutex.js +1 -1
- package/lib/Utils/message-retry-manager.js +92 -27
- package/lib/Utils/messages-media.js +130 -159
- package/lib/Utils/messages.js +566 -144
- package/lib/Utils/meta-ai-msmsg.js +202 -62
- package/lib/Utils/native-bridge.js +82 -0
- package/lib/Utils/noise-handler.js +21 -19
- package/lib/Utils/offline-node-processor.js +8 -3
- package/lib/Utils/pre-key-manager.js +23 -13
- package/lib/Utils/process-message.js +106 -197
- package/lib/Utils/reporting-utils.js +2 -2
- package/lib/Utils/session-pool.d.ts +16 -0
- package/lib/Utils/session-pool.js +94 -0
- package/lib/Utils/signal.js +63 -28
- package/lib/Utils/stanza-ack.js +12 -39
- package/lib/Utils/sticker.d.ts +17 -0
- package/lib/Utils/sticker.js +145 -0
- package/lib/Utils/sync-action-utils.js +7 -4
- package/lib/Utils/tc-token-utils.js +25 -20
- package/lib/Utils/use-multi-file-auth-state.js +14 -30
- package/lib/Utils/validate-connection.js +19 -59
- package/lib/Utils/view-once-cache.d.ts +9 -0
- package/lib/Utils/view-once-cache.js +79 -0
- package/lib/WABinary/decode.js +16 -50
- package/lib/WABinary/encode.js +224 -54
- package/lib/WABinary/generic-utils.js +97 -7
- package/lib/WABinary/jid-utils.js +14 -27
- package/lib/WAM/encode.js +7 -7
- package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +1 -58
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +1 -7
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +5 -13
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +4 -1
- package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +7 -20
- package/lib/WAUSync/Protocols/USyncPictureProtocol.js +1 -1
- package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +2 -2
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +2 -7
- package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +1 -1
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +1 -2
- package/lib/WAUSync/Protocols/index.js +0 -1
- package/lib/WAUSync/USyncQuery.js +6 -77
- package/lib/WAUSync/USyncUser.js +8 -16
- package/lib/WAUSync/index.js +0 -1
- package/lib/antiban.js +854 -608
- package/lib/index.js +1 -1
- package/package.json +20 -16
package/lib/Utils/chat-utils.js
CHANGED
|
@@ -13,7 +13,6 @@ exports.processSyncAction =
|
|
|
13
13
|
exports.newLTHashState =
|
|
14
14
|
void 0
|
|
15
15
|
const boom_1 = require('@hapi/boom')
|
|
16
|
-
const whatsapp_rust_bridge_1 = require('whatsapp-rust-bridge')
|
|
17
16
|
const index_js_1 = require('../../WAProto/index.js')
|
|
18
17
|
const LabelAssociation_1 = require('../Types/LabelAssociation')
|
|
19
18
|
const WABinary_1 = require('../WABinary')
|
|
@@ -23,13 +22,16 @@ const lt_hash_1 = require('./lt-hash')
|
|
|
23
22
|
const messages_media_1 = require('./messages-media')
|
|
24
23
|
const sync_action_utils_1 = require('./sync-action-utils')
|
|
25
24
|
const mutationKeys = keydata => {
|
|
26
|
-
|
|
25
|
+
// app-state mutation keys are just HKDF-expanded sub-keys of a single 32-byte
|
|
26
|
+
// key, no elliptic-curve math involved -- this is the standard derivation
|
|
27
|
+
// used across the Baileys ecosystem.
|
|
28
|
+
const expanded = (0, crypto_1.hkdf)(keydata, 160, { info: 'WhatsApp Mutation Keys' })
|
|
27
29
|
return {
|
|
28
|
-
indexKey:
|
|
29
|
-
valueEncryptionKey:
|
|
30
|
-
valueMacKey:
|
|
31
|
-
snapshotMacKey:
|
|
32
|
-
patchMacKey:
|
|
30
|
+
indexKey: expanded.subarray(0, 32),
|
|
31
|
+
valueEncryptionKey: expanded.subarray(32, 64),
|
|
32
|
+
valueMacKey: expanded.subarray(64, 96),
|
|
33
|
+
snapshotMacKey: expanded.subarray(96, 128),
|
|
34
|
+
patchMacKey: expanded.subarray(128, 160)
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
const generateMac = (operation, data, keyId, key) => {
|
|
@@ -64,11 +66,11 @@ const makeLtHashGenerator = ({ indexValueMap, hash }) => {
|
|
|
64
66
|
if (!prevOp) {
|
|
65
67
|
throw new boom_1.Boom('tried remove, but no previous op', { data: { indexMac, valueMac } })
|
|
66
68
|
}
|
|
67
|
-
|
|
69
|
+
// remove from index value mac, since this mutation is erased
|
|
68
70
|
delete indexValueMap[indexMacBase64]
|
|
69
71
|
} else {
|
|
70
72
|
addBuffs.push(valueMac)
|
|
71
|
-
|
|
73
|
+
// add this index into the history map
|
|
72
74
|
indexValueMap[indexMacBase64] = { valueMac }
|
|
73
75
|
}
|
|
74
76
|
if (prevOp) {
|
|
@@ -135,7 +137,7 @@ const encodeSyncdPatch = async (
|
|
|
135
137
|
const encValue = (0, crypto_1.aesEncrypt)(encoded, keyValue.valueEncryptionKey)
|
|
136
138
|
const valueMac = generateMac(operation, encValue, encKeyId, keyValue.valueMacKey)
|
|
137
139
|
const indexMac = (0, crypto_1.hmacSign)(indexBuffer, keyValue.indexKey)
|
|
138
|
-
|
|
140
|
+
// update LT hash
|
|
139
141
|
const generator = makeLtHashGenerator(state)
|
|
140
142
|
generator.mix({ indexMac, valueMac, operation })
|
|
141
143
|
Object.assign(state, generator.finish())
|
|
@@ -168,12 +170,12 @@ exports.encodeSyncdPatch = encodeSyncdPatch
|
|
|
168
170
|
const decodeSyncdMutations = async (msgMutations, initialState, getAppStateSyncKey, onMutation, validateMacs) => {
|
|
169
171
|
const ltGenerator = makeLtHashGenerator(initialState)
|
|
170
172
|
const derivedKeyCache = new Map()
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
173
|
+
// indexKey used to HMAC sign record.index.blob
|
|
174
|
+
// valueEncryptionKey used to AES-256-CBC encrypt record.value.blob[0:-32]
|
|
175
|
+
// the remaining record.value.blob[0:-32] is the mac, it the HMAC sign of key.keyId + decoded proto data + length of bytes in keyId
|
|
174
176
|
for (const msgMutation of msgMutations) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
+
// if it's a syncdmutation, get the operation property
|
|
178
|
+
// otherwise, if it's only a record -- it'll be a SET mutation
|
|
177
179
|
const operation =
|
|
178
180
|
'operation' in msgMutation ? msgMutation.operation : index_js_1.proto.SyncdMutation.SyncdOperation.SET
|
|
179
181
|
const record = 'record' in msgMutation && !!msgMutation.record ? msgMutation.record : msgMutation
|
|
@@ -397,7 +399,7 @@ const decodePatches = async (
|
|
|
397
399
|
throw new boom_1.Boom(`failed to verify LTHash at ${newState.version} of ${name}`)
|
|
398
400
|
}
|
|
399
401
|
}
|
|
400
|
-
|
|
402
|
+
// clear memory used up by the mutations
|
|
401
403
|
syncd.mutations = []
|
|
402
404
|
}
|
|
403
405
|
return { state: newState, mutationMap }
|
|
@@ -495,7 +497,7 @@ const chatModificationToAppPatch = (mod, jid) => {
|
|
|
495
497
|
messageRange: getMessageRange(mod.lastMessages)
|
|
496
498
|
}
|
|
497
499
|
},
|
|
498
|
-
index: ['clearChat', jid, '1'
|
|
500
|
+
index: ['clearChat', jid, '1' /*the option here is 0 when keep starred messages is enabled*/, '0'],
|
|
499
501
|
type: 'regular_high',
|
|
500
502
|
apiVersion: 6,
|
|
501
503
|
operation: OP.SET
|
|
@@ -841,45 +843,6 @@ const chatModificationToAppPatch = (mod, jid) => {
|
|
|
841
843
|
apiVersion: 1,
|
|
842
844
|
operation: OP.SET
|
|
843
845
|
}
|
|
844
|
-
} else if ('externalWebBeta' in mod) {
|
|
845
|
-
patch = {
|
|
846
|
-
syncAction: {
|
|
847
|
-
externalWebBetaAction: { isOptIn: !!mod.externalWebBeta }
|
|
848
|
-
},
|
|
849
|
-
index: ['setting_external_web_beta'],
|
|
850
|
-
type: 'regular',
|
|
851
|
-
apiVersion: 1,
|
|
852
|
-
operation: OP.SET
|
|
853
|
-
}
|
|
854
|
-
} else if ('maibaAIFeatures' in mod) {
|
|
855
|
-
const statusEnum = index_js_1.proto.SyncActionValue.MaibaAIFeaturesControlAction.MaibaAIFeatureStatus
|
|
856
|
-
const replyModeEnum = index_js_1.proto.SyncActionValue.MaibaAIFeaturesControlAction.MaibaAIReplyMode
|
|
857
|
-
const statusMap = {
|
|
858
|
-
enabled: statusEnum.ENABLED,
|
|
859
|
-
enabled_has_learning: statusEnum.ENABLED_HAS_LEARNING,
|
|
860
|
-
disabled: statusEnum.DISABLED
|
|
861
|
-
}
|
|
862
|
-
const replyModeMap = {
|
|
863
|
-
muted: replyModeEnum.MUTED,
|
|
864
|
-
ai_agent: replyModeEnum.AI_AGENT,
|
|
865
|
-
suggestions: replyModeEnum.SUGGESTIONS
|
|
866
|
-
}
|
|
867
|
-
const aiFeatures = mod.maibaAIFeatures
|
|
868
|
-
const status =
|
|
869
|
-
typeof aiFeatures.status === 'string' ? (statusMap[aiFeatures.status] ?? statusEnum.ENABLED) : aiFeatures.status
|
|
870
|
-
const replyMode =
|
|
871
|
-
typeof aiFeatures.replyMode === 'string'
|
|
872
|
-
? (replyModeMap[aiFeatures.replyMode] ?? replyModeEnum.SUGGESTIONS)
|
|
873
|
-
: aiFeatures.replyMode
|
|
874
|
-
patch = {
|
|
875
|
-
syncAction: {
|
|
876
|
-
maibaAiFeaturesControlAction: { aiFeatureStatus: status, aiReplyMode: replyMode }
|
|
877
|
-
},
|
|
878
|
-
index: ['setting_maiba_ai'],
|
|
879
|
-
type: 'regular',
|
|
880
|
-
apiVersion: 1,
|
|
881
|
-
operation: OP.SET
|
|
882
|
-
}
|
|
883
846
|
} else {
|
|
884
847
|
throw new boom_1.Boom('not supported')
|
|
885
848
|
}
|
|
@@ -904,25 +867,25 @@ const processSyncAction = (syncAction, ev, me, initialSyncOpts, logger) => {
|
|
|
904
867
|
}
|
|
905
868
|
])
|
|
906
869
|
} else if (action?.archiveChatAction || type === 'archive' || type === 'unarchive') {
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
870
|
+
// okay so we've to do some annoying computation here
|
|
871
|
+
// when we're initially syncing the app state
|
|
872
|
+
// there are a few cases we need to handle
|
|
873
|
+
// 1. if the account unarchiveChats setting is true
|
|
874
|
+
// a. if the chat is archived, and no further messages have been received -- simple, keep archived
|
|
875
|
+
// b. if the chat was archived, and the user received messages from the other person afterwards
|
|
876
|
+
// then the chat should be marked unarchved --
|
|
877
|
+
// we compare the timestamp of latest message from the other person to determine this
|
|
878
|
+
// 2. if the account unarchiveChats setting is false -- then it doesn't matter,
|
|
879
|
+
// it'll always take an app state action to mark in unarchived -- which we'll get anyway
|
|
917
880
|
const archiveAction = action?.archiveChatAction
|
|
918
881
|
const isArchived = archiveAction ? archiveAction.archived : type === 'archive'
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
882
|
+
// // basically we don't need to fire an "archive" update if the chat is being marked unarchvied
|
|
883
|
+
// // this only applies for the initial sync
|
|
884
|
+
// if(isInitialSync && !isArchived) {
|
|
885
|
+
// isArchived = false
|
|
886
|
+
// }
|
|
924
887
|
const msgRange = !accountSettings?.unarchiveChats ? undefined : archiveAction?.messageRange
|
|
925
|
-
|
|
888
|
+
// logger?.debug({ chat: id, syncAction }, 'message range archive')
|
|
926
889
|
ev.emit('chats.update', [
|
|
927
890
|
{
|
|
928
891
|
id,
|
|
@@ -932,9 +895,9 @@ const processSyncAction = (syncAction, ev, me, initialSyncOpts, logger) => {
|
|
|
932
895
|
])
|
|
933
896
|
} else if (action?.markChatAsReadAction) {
|
|
934
897
|
const markReadAction = action.markChatAsReadAction
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
898
|
+
// basically we don't need to fire an "read" update if the chat is being marked as read
|
|
899
|
+
// because the chat is read by default
|
|
900
|
+
// this only applies for the initial sync
|
|
938
901
|
const isNullUpdate = isInitialSync && markReadAction.read
|
|
939
902
|
ev.emit('chats.update', [
|
|
940
903
|
{
|
|
@@ -1223,33 +1186,6 @@ const processSyncAction = (syncAction, ev, me, initialSyncOpts, logger) => {
|
|
|
1223
1186
|
ev.emit('settings.update', { setting: 'businessBroadcastInsights', value: action.businessBroadcastInsightsAction })
|
|
1224
1187
|
} else if (action?.bizAiSettingsNudgeAction) {
|
|
1225
1188
|
ev.emit('settings.update', { setting: 'bizAiSettingsNudge', value: action.bizAiSettingsNudgeAction })
|
|
1226
|
-
} else if (action?.clearChatAction) {
|
|
1227
|
-
ev.emit('chats.update', [
|
|
1228
|
-
{
|
|
1229
|
-
id,
|
|
1230
|
-
lastMessageRecvTimestamp: 0,
|
|
1231
|
-
conditional: getChatUpdateConditional(id, action.clearChatAction?.messageRange)
|
|
1232
|
-
}
|
|
1233
|
-
])
|
|
1234
|
-
} else if (action?.deviceCapabilities) {
|
|
1235
|
-
ev.emit('settings.update', { setting: 'deviceCapabilities', value: action.deviceCapabilities })
|
|
1236
|
-
} else if (action?.ugcBot) {
|
|
1237
|
-
ev.emit('settings.update', { setting: 'ugcBot', value: action.ugcBot })
|
|
1238
|
-
} else if (action?.coexV2VersionAction) {
|
|
1239
|
-
ev.emit('settings.update', { setting: 'coexV2Version', value: action.coexV2VersionAction })
|
|
1240
|
-
} else if (action?.wasaRootSecretAction) {
|
|
1241
|
-
ev.emit('settings.update', { setting: 'wasaRootSecret', value: action.wasaRootSecretAction })
|
|
1242
|
-
} else if (action?.bubbleLockMessageAction) {
|
|
1243
|
-
ev.emit('messages.update', [
|
|
1244
|
-
{
|
|
1245
|
-
key: { remoteJid: id, id: msgId, fromMe: fromMe === '1' },
|
|
1246
|
-
update: { bubbleLocked: action.bubbleLockMessageAction?.locked }
|
|
1247
|
-
}
|
|
1248
|
-
])
|
|
1249
|
-
} else if (action?.labelSublistAction) {
|
|
1250
|
-
ev.emit('chats.update', [
|
|
1251
|
-
{ id, labelSublistId: action.labelSublistAction?.subListId, conditional: getChatUpdateConditional(id, undefined) }
|
|
1252
|
-
])
|
|
1253
1189
|
} else {
|
|
1254
1190
|
logger?.debug({ syncAction, id }, 'unprocessable update')
|
|
1255
1191
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface CommandContext {
|
|
2
|
+
sock: any
|
|
3
|
+
msg: any
|
|
4
|
+
args: string[]
|
|
5
|
+
text: string
|
|
6
|
+
jid: string
|
|
7
|
+
sender: string
|
|
8
|
+
isGroup: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface Command {
|
|
12
|
+
name: string
|
|
13
|
+
aliases?: string[]
|
|
14
|
+
description?: string
|
|
15
|
+
execute(ctx: CommandContext): Promise<void> | void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface CommandHandlerOptions {
|
|
19
|
+
commandsDir: string
|
|
20
|
+
prefix?: string
|
|
21
|
+
logger?: any
|
|
22
|
+
onError?: (error: any, msg: any) => void
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface CommandHandler {
|
|
26
|
+
commands: Map<string, Command>
|
|
27
|
+
reload: () => void
|
|
28
|
+
stop: () => void
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function createCommandHandler(sock: any, options: CommandHandlerOptions): CommandHandler
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true })
|
|
3
|
+
exports.createCommandHandler = void 0
|
|
4
|
+
const fs = require('fs')
|
|
5
|
+
const path = require('path')
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Loads command modules from a folder and wires them up to a socket's `messages.upsert`
|
|
9
|
+
* event, so you don't need to write a prefix/dispatch loop by hand for every project.
|
|
10
|
+
*
|
|
11
|
+
* Each file in `commandsDir` should export an object (or a default export) shaped like:
|
|
12
|
+
* { name: string, aliases?: string[], description?: string, execute(ctx): Promise<void> | void }
|
|
13
|
+
*
|
|
14
|
+
* `ctx` passed to `execute` is: { sock, msg, args, text, jid, sender, isGroup }
|
|
15
|
+
*
|
|
16
|
+
* @param {ReturnType<typeof import('../Socket').default>} sock - an active Baileys socket
|
|
17
|
+
* @param {{ commandsDir: string, prefix?: string, logger?: any, onError?: (error: any, msg: any) => void }} options
|
|
18
|
+
* @returns {{ commands: Map<string, any>, reload: () => void, stop: () => void }}
|
|
19
|
+
*/
|
|
20
|
+
const createCommandHandler = (sock, options) => {
|
|
21
|
+
const prefix = options.prefix ?? '!'
|
|
22
|
+
const logger = options.logger || sock.logger
|
|
23
|
+
const commands = new Map()
|
|
24
|
+
|
|
25
|
+
const load = () => {
|
|
26
|
+
commands.clear()
|
|
27
|
+
const dir = options.commandsDir
|
|
28
|
+
if (!fs.existsSync(dir)) {
|
|
29
|
+
logger?.warn?.({ dir }, 'commands directory does not exist')
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
const files = fs.readdirSync(dir).filter(f => f.endsWith('.js'))
|
|
33
|
+
for (const file of files) {
|
|
34
|
+
const fullPath = path.join(dir, file)
|
|
35
|
+
try {
|
|
36
|
+
delete require.cache[require.resolve(fullPath)]
|
|
37
|
+
const mod = require(fullPath)
|
|
38
|
+
const cmd = mod?.default || mod
|
|
39
|
+
if (!cmd?.name || typeof cmd.execute !== 'function') {
|
|
40
|
+
logger?.warn?.({ file }, 'skipping invalid command module (missing name/execute)')
|
|
41
|
+
continue
|
|
42
|
+
}
|
|
43
|
+
commands.set(cmd.name.toLowerCase(), cmd)
|
|
44
|
+
for (const alias of cmd.aliases || []) {
|
|
45
|
+
commands.set(alias.toLowerCase(), cmd)
|
|
46
|
+
}
|
|
47
|
+
} catch (error) {
|
|
48
|
+
logger?.error?.({ error, file }, 'failed to load command')
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
logger?.debug?.({ count: commands.size }, 'commands loaded')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
load()
|
|
55
|
+
|
|
56
|
+
const handler = async ({ messages, type }) => {
|
|
57
|
+
if (type !== 'notify') return
|
|
58
|
+
for (const msg of messages) {
|
|
59
|
+
try {
|
|
60
|
+
if (!msg.message || msg.key.fromMe) continue
|
|
61
|
+
const body =
|
|
62
|
+
msg.message.conversation ||
|
|
63
|
+
msg.message.extendedTextMessage?.text ||
|
|
64
|
+
msg.message.imageMessage?.caption ||
|
|
65
|
+
msg.message.videoMessage?.caption ||
|
|
66
|
+
''
|
|
67
|
+
if (!body.startsWith(prefix)) continue
|
|
68
|
+
const [cmdName, ...args] = body.slice(prefix.length).trim().split(/\s+/)
|
|
69
|
+
if (!cmdName) continue
|
|
70
|
+
const cmd = commands.get(cmdName.toLowerCase())
|
|
71
|
+
if (!cmd) continue
|
|
72
|
+
const jid = msg.key.remoteJid
|
|
73
|
+
const ctx = {
|
|
74
|
+
sock,
|
|
75
|
+
msg,
|
|
76
|
+
args,
|
|
77
|
+
text: args.join(' '),
|
|
78
|
+
jid,
|
|
79
|
+
sender: msg.key.participant || msg.key.remoteJid,
|
|
80
|
+
isGroup: !!jid && jid.endsWith('@g.us')
|
|
81
|
+
}
|
|
82
|
+
await cmd.execute(ctx)
|
|
83
|
+
} catch (error) {
|
|
84
|
+
if (options.onError) {
|
|
85
|
+
options.onError(error, msg)
|
|
86
|
+
} else {
|
|
87
|
+
logger?.error?.({ error }, 'command execution failed')
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
sock.ev.on('messages.upsert', handler)
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
commands,
|
|
96
|
+
reload: load,
|
|
97
|
+
stop: () => sock.ev.off('messages.upsert', handler)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
exports.createCommandHandler = createCommandHandler
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true })
|
|
3
|
+
exports.buildPairingQRData = exports.getCompanionPlatformId = exports.getCompanionWebClientType = exports.CompanionWebClientType = void 0
|
|
4
|
+
var CompanionWebClientType
|
|
5
|
+
;(function (CompanionWebClientType) {
|
|
6
|
+
CompanionWebClientType[(CompanionWebClientType['UNKNOWN'] = 0)] = 'UNKNOWN'
|
|
7
|
+
CompanionWebClientType[(CompanionWebClientType['CHROME'] = 1)] = 'CHROME'
|
|
8
|
+
CompanionWebClientType[(CompanionWebClientType['EDGE'] = 2)] = 'EDGE'
|
|
9
|
+
CompanionWebClientType[(CompanionWebClientType['FIREFOX'] = 3)] = 'FIREFOX'
|
|
10
|
+
CompanionWebClientType[(CompanionWebClientType['IE'] = 4)] = 'IE'
|
|
11
|
+
CompanionWebClientType[(CompanionWebClientType['OPERA'] = 5)] = 'OPERA'
|
|
12
|
+
CompanionWebClientType[(CompanionWebClientType['SAFARI'] = 6)] = 'SAFARI'
|
|
13
|
+
CompanionWebClientType[(CompanionWebClientType['ELECTRON'] = 7)] = 'ELECTRON'
|
|
14
|
+
CompanionWebClientType[(CompanionWebClientType['UWP'] = 8)] = 'UWP'
|
|
15
|
+
CompanionWebClientType[(CompanionWebClientType['OTHER_WEB_CLIENT'] = 9)] = 'OTHER_WEB_CLIENT'
|
|
16
|
+
})((CompanionWebClientType = exports.CompanionWebClientType || (exports.CompanionWebClientType = {})))
|
|
17
|
+
const BROWSER_TO_COMPANION_WEB_CLIENT = {
|
|
18
|
+
Chrome: CompanionWebClientType.CHROME,
|
|
19
|
+
Edge: CompanionWebClientType.EDGE,
|
|
20
|
+
Firefox: CompanionWebClientType.FIREFOX,
|
|
21
|
+
IE: CompanionWebClientType.IE,
|
|
22
|
+
Opera: CompanionWebClientType.OPERA,
|
|
23
|
+
Safari: CompanionWebClientType.SAFARI
|
|
24
|
+
}
|
|
25
|
+
const getCompanionWebClientType = ([os, browserName]) => {
|
|
26
|
+
if (browserName === 'Desktop') {
|
|
27
|
+
return os === 'Windows' ? CompanionWebClientType.UWP : CompanionWebClientType.ELECTRON
|
|
28
|
+
}
|
|
29
|
+
return BROWSER_TO_COMPANION_WEB_CLIENT[browserName] || CompanionWebClientType.OTHER_WEB_CLIENT
|
|
30
|
+
}
|
|
31
|
+
exports.getCompanionWebClientType = getCompanionWebClientType
|
|
32
|
+
const getCompanionPlatformId = (browser) => {
|
|
33
|
+
return getCompanionWebClientType(browser).toString()
|
|
34
|
+
}
|
|
35
|
+
exports.getCompanionPlatformId = getCompanionPlatformId
|
|
36
|
+
const buildPairingQRData = (ref, noiseKeyB64, identityKeyB64, advB64, browser) => {
|
|
37
|
+
return (
|
|
38
|
+
'https://wa.me/settings/linked_devices#' +
|
|
39
|
+
[ref, noiseKeyB64, identityKeyB64, advB64, getCompanionPlatformId(browser)].join(',')
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
exports.buildPairingQRData = buildPairingQRData
|
package/lib/Utils/crypto.js
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
Object.defineProperty(exports, '__esModule', { value: true })
|
|
3
|
-
exports.signedKeyPair =
|
|
4
|
-
exports.Curve =
|
|
5
|
-
exports.generateSignalPubKey =
|
|
6
|
-
exports.getEnabledFeatures =
|
|
7
|
-
exports.hkdf =
|
|
8
|
-
exports.md5 =
|
|
9
|
-
void 0
|
|
3
|
+
exports.signedKeyPair = exports.Curve = exports.generateSignalPubKey = exports.hkdf = exports.md5 = void 0
|
|
10
4
|
exports.aesEncryptGCM = aesEncryptGCM
|
|
11
5
|
exports.aesDecryptGCM = aesDecryptGCM
|
|
12
6
|
exports.aesEncryptCTR = aesEncryptCTR
|
|
@@ -20,36 +14,64 @@ exports.sha256 = sha256
|
|
|
20
14
|
exports.derivePairingCodeKey = derivePairingCodeKey
|
|
21
15
|
const { subtle } = globalThis.crypto
|
|
22
16
|
const Defaults_1 = require('../Defaults')
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
17
|
+
const nodeCrypto = require('crypto')
|
|
18
|
+
|
|
19
|
+
// Pure Node.js `crypto` / hand-written JS only -- no native/WASM module
|
|
20
|
+
// dependency anywhere in this file (including `Curve`, X25519 key exchange +
|
|
21
|
+
// XEdDSA signing). XEdDSA specifically is validated against the official
|
|
22
|
+
// RFC 8032 Ed25519 test vectors, RFC 7748-style X25519 DH vectors, and
|
|
23
|
+
// hundreds of randomized Montgomery<->Edwards interop + self-consistency
|
|
24
|
+
// checks -- see ./curve25519-js.js for details and references.
|
|
25
|
+
|
|
26
|
+
// ── MD5 ────────────────────────────────────────────────────────────────────────
|
|
27
|
+
const md5 = buffer => nodeCrypto.createHash('md5').update(buffer).digest()
|
|
28
|
+
Object.defineProperty(exports, 'md5', { enumerable: true, get: () => md5 })
|
|
29
|
+
|
|
30
|
+
// ── HKDF (RFC 5869) ─────────────────────────────────────────────────────────────
|
|
31
|
+
const hkdfExtractJS = (salt, ikm) => nodeCrypto.createHmac('sha256', salt).update(ikm).digest()
|
|
32
|
+
const hkdfExpandJS = (prk, length, info) => {
|
|
33
|
+
const hashLen = 32
|
|
34
|
+
const n = Math.ceil(length / hashLen)
|
|
35
|
+
let t = Buffer.alloc(0)
|
|
36
|
+
let okm = Buffer.alloc(0)
|
|
37
|
+
for (let i = 1; i <= n; i++) {
|
|
38
|
+
t = nodeCrypto
|
|
39
|
+
.createHmac('sha256', prk)
|
|
40
|
+
.update(Buffer.concat([t, info, Buffer.from([i])]))
|
|
41
|
+
.digest()
|
|
42
|
+
okm = Buffer.concat([okm, t])
|
|
43
|
+
}
|
|
44
|
+
return okm.subarray(0, length)
|
|
45
|
+
}
|
|
46
|
+
const hkdf = (ikm, length, options = {}) => {
|
|
47
|
+
const ikmBuf = Buffer.isBuffer(ikm) ? ikm : Buffer.from(ikm)
|
|
48
|
+
const saltBuf = options.salt
|
|
49
|
+
? Buffer.isBuffer(options.salt)
|
|
50
|
+
? options.salt
|
|
51
|
+
: Buffer.from(options.salt)
|
|
52
|
+
: Buffer.alloc(32) // RFC 5869: default salt is HashLen zero bytes
|
|
53
|
+
const infoBuf = options.info
|
|
54
|
+
? Buffer.isBuffer(options.info)
|
|
55
|
+
? options.info
|
|
56
|
+
: Buffer.from(options.info)
|
|
57
|
+
: Buffer.alloc(0)
|
|
58
|
+
const prk = hkdfExtractJS(saltBuf, ikmBuf)
|
|
59
|
+
return hkdfExpandJS(prk, length, infoBuf)
|
|
60
|
+
}
|
|
61
|
+
Object.defineProperty(exports, 'hkdf', { enumerable: true, get: () => hkdf })
|
|
30
62
|
|
|
63
|
+
// ── Signal pub key helper ─────────────────────────────────────────────────────
|
|
31
64
|
const generateSignalPubKey = pubKey =>
|
|
32
65
|
pubKey.length === 33 ? pubKey : Buffer.concat([Defaults_1.KEY_BUNDLE_TYPE, pubKey])
|
|
33
66
|
exports.generateSignalPubKey = generateSignalPubKey
|
|
34
67
|
|
|
35
|
-
|
|
68
|
+
// ── Curve — X25519 key exchange + XEdDSA signing ──────────────────────────────
|
|
69
|
+
const curve25519JS = require('./curve25519-js')
|
|
36
70
|
exports.Curve = {
|
|
37
|
-
generateKeyPair: () =>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
public: Buffer.from(pair.pubKey).subarray(1)
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
sharedKey: (privateKey, publicKey) => Buffer.from(rb.calculateAgreement(generateSignalPubKey(publicKey), privateKey)),
|
|
45
|
-
sign: (privateKey, buf) => rb.calculateSignature(privateKey, buf),
|
|
46
|
-
verify: (pubKey, message, signature) => {
|
|
47
|
-
try {
|
|
48
|
-
return rb.verifySignature(generateSignalPubKey(pubKey), message, signature)
|
|
49
|
-
} catch {
|
|
50
|
-
return false
|
|
51
|
-
}
|
|
52
|
-
}
|
|
71
|
+
generateKeyPair: () => curve25519JS.generateKeyPairJS(),
|
|
72
|
+
sharedKey: (privateKey, publicKey) => curve25519JS.sharedKeyJS(privateKey, publicKey),
|
|
73
|
+
sign: (privateKey, buf) => curve25519JS.signJS(privateKey, buf),
|
|
74
|
+
verify: (pubKey, message, signature) => curve25519JS.verifyJS(pubKey, message, signature)
|
|
53
75
|
}
|
|
54
76
|
|
|
55
77
|
const signedKeyPair = (identityKeyPair, keyId) => {
|
|
@@ -60,57 +82,65 @@ const signedKeyPair = (identityKeyPair, keyId) => {
|
|
|
60
82
|
}
|
|
61
83
|
exports.signedKeyPair = signedKeyPair
|
|
62
84
|
|
|
63
|
-
|
|
85
|
+
// ── AES-256-GCM ───────────────────────────────────────────────────────────────
|
|
64
86
|
function aesEncryptGCM(plaintext, key, iv, additionalData) {
|
|
65
|
-
|
|
87
|
+
const cipher = nodeCrypto.createCipheriv('aes-256-gcm', key, iv)
|
|
88
|
+
if (additionalData) cipher.setAAD(additionalData)
|
|
89
|
+
const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()])
|
|
90
|
+
return Buffer.concat([encrypted, cipher.getAuthTag()])
|
|
66
91
|
}
|
|
67
92
|
|
|
68
93
|
function aesDecryptGCM(ciphertext, key, iv, additionalData) {
|
|
69
|
-
|
|
94
|
+
const authTag = ciphertext.subarray(ciphertext.length - 16)
|
|
95
|
+
const encrypted = ciphertext.subarray(0, ciphertext.length - 16)
|
|
96
|
+
const decipher = nodeCrypto.createDecipheriv('aes-256-gcm', key, iv)
|
|
97
|
+
if (additionalData) decipher.setAAD(additionalData)
|
|
98
|
+
decipher.setAuthTag(authTag)
|
|
99
|
+
return Buffer.concat([decipher.update(encrypted), decipher.final()])
|
|
70
100
|
}
|
|
71
101
|
|
|
72
|
-
|
|
102
|
+
// ── AES-256-CTR ───────────────────────────────────────────────────────────────
|
|
73
103
|
function aesEncryptCTR(plaintext, key, iv) {
|
|
74
|
-
|
|
104
|
+
const cipher = nodeCrypto.createCipheriv('aes-256-ctr', key, iv)
|
|
105
|
+
return Buffer.concat([cipher.update(plaintext), cipher.final()])
|
|
75
106
|
}
|
|
76
107
|
|
|
77
108
|
function aesDecryptCTR(ciphertext, key, iv) {
|
|
78
|
-
|
|
109
|
+
const decipher = nodeCrypto.createDecipheriv('aes-256-ctr', key, iv)
|
|
110
|
+
return Buffer.concat([decipher.update(ciphertext), decipher.final()])
|
|
79
111
|
}
|
|
80
112
|
|
|
81
|
-
|
|
113
|
+
// ── AES-256-CBC (no explicit IV -- zero IV, legacy/unused elsewhere in this lib) ─
|
|
82
114
|
function aesDecrypt(buffer, key) {
|
|
83
|
-
return
|
|
115
|
+
return aesDecryptWithIV(buffer, key, Buffer.alloc(16))
|
|
84
116
|
}
|
|
85
117
|
|
|
86
|
-
function
|
|
87
|
-
return
|
|
118
|
+
function aesEncrypt(buffer, key) {
|
|
119
|
+
return aesEncrypWithIV(buffer, key, Buffer.alloc(16))
|
|
88
120
|
}
|
|
89
121
|
|
|
90
|
-
|
|
91
|
-
|
|
122
|
+
// ── AES-256-CBC (explicit IV) ─────────────────────────────────────────────────
|
|
123
|
+
function aesDecryptWithIV(buffer, key, IV) {
|
|
124
|
+
const decipher = nodeCrypto.createDecipheriv('aes-256-cbc', key, IV)
|
|
125
|
+
return Buffer.concat([decipher.update(buffer), decipher.final()])
|
|
92
126
|
}
|
|
93
127
|
|
|
94
128
|
function aesEncrypWithIV(buffer, key, IV) {
|
|
95
|
-
|
|
129
|
+
const cipher = nodeCrypto.createCipheriv('aes-256-cbc', key, IV)
|
|
130
|
+
return Buffer.concat([cipher.update(buffer), cipher.final()])
|
|
96
131
|
}
|
|
97
132
|
|
|
98
|
-
|
|
133
|
+
// ── HMAC-SHA256 ───────────────────────────────────────────────────────────────
|
|
99
134
|
function hmacSign(buffer, key, variant = 'sha256') {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const { createHmac } = require('crypto')
|
|
103
|
-
return createHmac(variant, key).update(buffer).digest()
|
|
104
|
-
}
|
|
105
|
-
return Buffer.from(rb.hmacSign(buffer, key))
|
|
135
|
+
return nodeCrypto.createHmac(variant, key).update(buffer).digest()
|
|
106
136
|
}
|
|
107
137
|
|
|
108
|
-
|
|
138
|
+
// ── SHA-256 ───────────────────────────────────────────────────────────────────
|
|
109
139
|
function sha256(buffer) {
|
|
110
|
-
return
|
|
140
|
+
return nodeCrypto.createHash('sha256').update(buffer).digest()
|
|
111
141
|
}
|
|
112
142
|
|
|
113
|
-
|
|
143
|
+
// ── PBKDF2 pairing code key ───────────────────────────────────────────────────
|
|
114
144
|
async function derivePairingCodeKey(pairingCode, salt) {
|
|
115
145
|
const encoder = new TextEncoder()
|
|
116
146
|
const pairingCodeBuffer = encoder.encode(pairingCode)
|