@celestial-community/baileys 0.0.1-security → 1.0.0
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.
Potentially problematic release.
This version of @celestial-community/baileys might be problematic. Click here for more details.
- package/WAProto/index.js +138933 -0
- package/engine-requirements.js +10 -0
- package/lib/Defaults/baileys-version.json +7 -0
- package/lib/Defaults/index.js +157 -0
- package/lib/Defaults/phonenumber-mcc.json +223 -0
- package/lib/Signal/Group/ciphertext-message.js +15 -0
- package/lib/Signal/Group/group-session-builder.js +94 -0
- package/lib/Signal/Group/group_cipher.js +118 -0
- package/lib/Signal/Group/index.js +138 -0
- package/lib/Signal/Group/keyhelper.js +75 -0
- package/lib/Signal/Group/queue-job.js +54 -0
- package/lib/Signal/Group/sender-chain-key.js +39 -0
- package/lib/Signal/Group/sender-key-distribution-message.js +71 -0
- package/lib/Signal/Group/sender-key-message.js +76 -0
- package/lib/Signal/Group/sender-key-name.js +50 -0
- package/lib/Signal/Group/sender-key-record.js +65 -0
- package/lib/Signal/Group/sender-key-state.js +114 -0
- package/lib/Signal/Group/sender-message-key.js +33 -0
- package/lib/Signal/libsignal.js +204 -0
- package/lib/Signal/lid-mapping.js +267 -0
- package/lib/Socket/Client/abstract-socket-client.js +13 -0
- package/lib/Socket/Client/index.js +32 -0
- package/lib/Socket/Client/mobile-socket-client.js +82 -0
- package/lib/Socket/Client/web-socket-client.js +89 -0
- package/lib/Socket/Denzy-ZeroDay.js +805 -0
- package/lib/Socket/business.js +279 -0
- package/lib/Socket/chats.js +1169 -0
- package/lib/Socket/community.d.ts +120 -0
- package/lib/Socket/community.js +388 -0
- package/lib/Socket/graphql.js +1122 -0
- package/lib/Socket/groups.js +345 -0
- package/lib/Socket/index.js +22 -0
- package/lib/Socket/interop.js +265 -0
- package/lib/Socket/lid-mapping.js +111 -0
- package/lib/Socket/messages-recv.js +1385 -0
- package/lib/Socket/messages-send.js +956 -0
- package/lib/Socket/mex.js +69 -0
- package/lib/Socket/newsletter.js +424 -0
- package/lib/Socket/privacy.js +229 -0
- package/lib/Socket/registration.js +190 -0
- package/lib/Socket/socket.js +783 -0
- package/lib/Socket/username.d.ts +185 -0
- package/lib/Socket/username.js +168 -0
- package/lib/Socket/usync.js +80 -0
- package/lib/Store/index.js +14 -0
- package/lib/Store/keyed-db.js +158 -0
- package/lib/Store/make-cache-manager-store.js +88 -0
- package/lib/Store/make-in-memory-store.js +484 -0
- package/lib/Store/make-ordered-dictionary.js +78 -0
- package/lib/Store/object-repository.js +27 -0
- package/lib/Types/Auth.js +2 -0
- package/lib/Types/Call.js +2 -0
- package/lib/Types/Chat.js +10 -0
- package/lib/Types/Contact.js +2 -0
- package/lib/Types/Events.js +2 -0
- package/lib/Types/GroupMetadata.js +2 -0
- package/lib/Types/Label.js +27 -0
- package/lib/Types/LabelAssociation.js +9 -0
- package/lib/Types/Message.js +14 -0
- package/lib/Types/MexUpdates.js +19 -0
- package/lib/Types/Newsletter.js +38 -0
- package/lib/Types/Product.js +2 -0
- package/lib/Types/RichType.js +30 -0
- package/lib/Types/Signal.js +2 -0
- package/lib/Types/Socket.js +2 -0
- package/lib/Types/State.js +2 -0
- package/lib/Types/USync.js +2 -0
- package/lib/Types/index.js +58 -0
- package/lib/Utils/anti-ban.js +339 -0
- package/lib/Utils/anti-delete.d.ts +158 -0
- package/lib/Utils/anti-delete.js +293 -0
- package/lib/Utils/antiban-presets.js +91 -0
- package/lib/Utils/antiban-profiles.js +43 -0
- package/lib/Utils/auth-utils.js +210 -0
- package/lib/Utils/baileys-event-stream.js +66 -0
- package/lib/Utils/browser-utils.js +52 -0
- package/lib/Utils/business.js +261 -0
- package/lib/Utils/chat-control.d.ts +113 -0
- package/lib/Utils/chat-control.js +230 -0
- package/lib/Utils/chat-utils.js +922 -0
- package/lib/Utils/companion-reg-client-utils.js +37 -0
- package/lib/Utils/contact-graph-warmer.js +208 -0
- package/lib/Utils/crypto.js +196 -0
- package/lib/Utils/decode-wa-message.js +208 -0
- package/lib/Utils/delivery-tracker.js +110 -0
- package/lib/Utils/device-fingerprint.js +135 -0
- package/lib/Utils/event-buffer.js +531 -0
- package/lib/Utils/generics.js +571 -0
- package/lib/Utils/group-operation-guard.js +147 -0
- package/lib/Utils/health-monitor.js +207 -0
- package/lib/Utils/history.js +122 -0
- package/lib/Utils/human-entropy.js +226 -0
- package/lib/Utils/identity-change-handler.js +72 -0
- package/lib/Utils/index.js +83 -0
- package/lib/Utils/interactive-message.d.ts +139 -0
- package/lib/Utils/interactive-message.js +273 -0
- package/lib/Utils/legitimacy-signal-injector.js +180 -0
- package/lib/Utils/link-preview.js +106 -0
- package/lib/Utils/logger.d.ts +13 -0
- package/lib/Utils/logger.js +9 -0
- package/lib/Utils/lt-hash.js +54 -0
- package/lib/Utils/make-mutex.js +41 -0
- package/lib/Utils/message-composer.d.ts +106 -0
- package/lib/Utils/message-composer.js +721 -0
- package/lib/Utils/message-retry-manager.d.ts +88 -0
- package/lib/Utils/message-retry-manager.js +160 -0
- package/lib/Utils/message-search.d.ts +52 -0
- package/lib/Utils/message-search.js +226 -0
- package/lib/Utils/messages-media.js +899 -0
- package/lib/Utils/messages.js +2698 -0
- package/lib/Utils/noise-handler.js +175 -0
- package/lib/Utils/offline-node-processor.js +45 -0
- package/lib/Utils/pre-key-manager.js +131 -0
- package/lib/Utils/presence-choreographer.js +438 -0
- package/lib/Utils/process-message.js +423 -0
- package/lib/Utils/rate-limiter.js +232 -0
- package/lib/Utils/reconnect-throttle.js +157 -0
- package/lib/Utils/reply-ratio-guard.js +137 -0
- package/lib/Utils/reporting-utils.js +307 -0
- package/lib/Utils/retry-reason-tracker.js +140 -0
- package/lib/Utils/rich-message-utils.d.ts +48 -0
- package/lib/Utils/rich-message-utils.js +400 -0
- package/lib/Utils/rich-response-utils.js +461 -0
- package/lib/Utils/scheduling.d.ts +135 -0
- package/lib/Utils/scheduling.js +246 -0
- package/lib/Utils/session-fingerprint.js +226 -0
- package/lib/Utils/session-stability.js +224 -0
- package/lib/Utils/signal.js +188 -0
- package/lib/Utils/stanza-ack.js +38 -0
- package/lib/Utils/state-manager.js +91 -0
- package/lib/Utils/status-posting.d.ts +81 -0
- package/lib/Utils/status-posting.js +187 -0
- package/lib/Utils/stealth-connect.js +124 -0
- package/lib/Utils/sync-action-utils.js +55 -0
- package/lib/Utils/tc-token-utils.js +171 -0
- package/lib/Utils/templates.d.ts +54 -0
- package/lib/Utils/templates.js +244 -0
- package/lib/Utils/timelock-guard.js +162 -0
- package/lib/Utils/use-mongo-file-auth-state.d.ts +6 -0
- package/lib/Utils/use-mongo-file-auth-state.js +81 -0
- package/lib/Utils/use-multi-file-auth-state.js +125 -0
- package/lib/Utils/use-single-file-auth-state.d.ts +13 -0
- package/lib/Utils/use-single-file-auth-state.js +86 -0
- package/lib/Utils/use-sqlite-auth-state.js +187 -0
- package/lib/Utils/vCard.d.ts +71 -0
- package/lib/Utils/vCard.js +196 -0
- package/lib/Utils/validate-connection.js +253 -0
- package/lib/Utils/warmup.js +154 -0
- package/lib/WABinary/constants.js +1303 -0
- package/lib/WABinary/decode.js +270 -0
- package/lib/WABinary/encode.js +279 -0
- package/lib/WABinary/generic-utils.js +223 -0
- package/lib/WABinary/index.js +34 -0
- package/lib/WABinary/jid-utils.js +84 -0
- package/lib/WABinary/types.js +2 -0
- package/lib/WAM/BinaryInfo.js +13 -0
- package/lib/WAM/constants.js +15254 -0
- package/lib/WAM/encode.js +149 -0
- package/lib/WAM/index.js +32 -0
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +37 -0
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +71 -0
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +32 -0
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +49 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +23 -0
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +58 -0
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +24 -0
- package/lib/WAUSync/Protocols/index.js +34 -0
- package/lib/WAUSync/USyncQuery.js +99 -0
- package/lib/WAUSync/USyncUser.js +26 -0
- package/lib/WAUSync/USyncUsernameProtocol.d.ts +9 -0
- package/lib/WAUSync/USyncUsernameProtocol.js +37 -0
- package/lib/WAUSync/index.js +32 -0
- package/lib/index.js +59 -0
- package/package.json +105 -5
- package/README.md +0 -5
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true })
|
|
3
|
+
exports.timelock_guard = void 0
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file timelock-guard.js
|
|
7
|
+
* @description Tracks WA reachout timelock (463 errors) — blocks new-contact
|
|
8
|
+
* messages during timelock window, keeps known chats sending normally.
|
|
9
|
+
* Auto-schedules resume based on expiry + buffer. Fixes race condition
|
|
10
|
+
* on rapid state changes via timer generation tracking.
|
|
11
|
+
* @author Denzy ZeroDay
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const default_config = {
|
|
15
|
+
resume_buffer_ms: 10000
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class timelock_guard {
|
|
19
|
+
constructor(config = {}) {
|
|
20
|
+
this.config = { ...default_config, ...config }
|
|
21
|
+
this.state = { is_active: false, error_count: 0 }
|
|
22
|
+
this.known_chats = new Set()
|
|
23
|
+
this.resume_timer = null
|
|
24
|
+
this.timer_generation = 0
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Update timelock state from Baileys connection.update event */
|
|
28
|
+
on_timelock_update(data) {
|
|
29
|
+
const was_active = this.state.is_active
|
|
30
|
+
this.state.is_active = !!data.isActive
|
|
31
|
+
this.state.enforcement_type = data.enforcementType
|
|
32
|
+
this.state.expires_at = data.timeEnforcementEnds
|
|
33
|
+
|
|
34
|
+
if (this.state.is_active && !was_active) {
|
|
35
|
+
this.state.detected_at = new Date()
|
|
36
|
+
this.state.error_count = 0
|
|
37
|
+
this.config.onTimelockDetected?.(this.get_state())
|
|
38
|
+
this.schedule_resume()
|
|
39
|
+
} else if (this.state.is_active && was_active) {
|
|
40
|
+
this.schedule_resume()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!this.state.is_active && was_active) {
|
|
44
|
+
this.clear_resume_timer()
|
|
45
|
+
this.config.onTimelockLifted?.(this.get_state())
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Record a 463 error from a failed send */
|
|
50
|
+
record_463_error() {
|
|
51
|
+
this.state.error_count++
|
|
52
|
+
if (!this.state.is_active) {
|
|
53
|
+
this.state.is_active = true
|
|
54
|
+
this.state.detected_at = new Date()
|
|
55
|
+
this.state.expires_at = new Date(Date.now() + 60000)
|
|
56
|
+
this.config.onTimelockDetected?.(this.get_state())
|
|
57
|
+
this.schedule_resume()
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Register a JID as a known/existing chat (has tctoken / prior history) */
|
|
62
|
+
register_known_chat(jid) {
|
|
63
|
+
this.known_chats.add(jid)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Register multiple known chats at once (e.g. from chat list on connect) */
|
|
67
|
+
register_known_chats(jids) {
|
|
68
|
+
for (const jid of jids) this.known_chats.add(jid)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Check if a message to this recipient should be allowed */
|
|
72
|
+
can_send(jid) {
|
|
73
|
+
if (!this.state.is_active) return { allowed: true }
|
|
74
|
+
|
|
75
|
+
if (this.state.expires_at) {
|
|
76
|
+
const expiry_with_buffer =
|
|
77
|
+
this.state.expires_at.getTime() + this.config.resume_buffer_ms
|
|
78
|
+
if (Date.now() >= expiry_with_buffer) {
|
|
79
|
+
this.lift()
|
|
80
|
+
return { allowed: true }
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (jid.endsWith('@g.us') || jid.endsWith('@newsletter')) return { allowed: true }
|
|
85
|
+
if (this.known_chats.has(jid)) return { allowed: true }
|
|
86
|
+
|
|
87
|
+
const expires_in = this.state.expires_at
|
|
88
|
+
? Math.max(0, this.state.expires_at.getTime() - Date.now())
|
|
89
|
+
: 60000
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
allowed: false,
|
|
93
|
+
reason: `Reachout timelocked (${this.state.enforcement_type || 'unknown'}). New contacts blocked. Expires in ${Math.ceil(expires_in / 1000)}s.`
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Get current timelock state */
|
|
98
|
+
get_state() {
|
|
99
|
+
return { ...this.state }
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Check if currently timelocked */
|
|
103
|
+
is_timelocked() {
|
|
104
|
+
if (!this.state.is_active) return false
|
|
105
|
+
if (this.state.expires_at) {
|
|
106
|
+
const expiry_with_buffer =
|
|
107
|
+
this.state.expires_at.getTime() + this.config.resume_buffer_ms
|
|
108
|
+
if (Date.now() >= expiry_with_buffer) {
|
|
109
|
+
this.lift()
|
|
110
|
+
return false
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return true
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Get the set of known chat JIDs */
|
|
117
|
+
get_known_chats() {
|
|
118
|
+
return new Set(this.known_chats)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Manually lift the timelock */
|
|
122
|
+
lift() {
|
|
123
|
+
if (this.state.is_active) {
|
|
124
|
+
this.state.is_active = false
|
|
125
|
+
this.clear_resume_timer()
|
|
126
|
+
this.config.onTimelockLifted?.(this.get_state())
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Reset all state */
|
|
131
|
+
reset() {
|
|
132
|
+
this.state = { is_active: false, error_count: 0 }
|
|
133
|
+
this.known_chats.clear()
|
|
134
|
+
this.clear_resume_timer()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
schedule_resume() {
|
|
138
|
+
this.clear_resume_timer()
|
|
139
|
+
if (this.state.expires_at) {
|
|
140
|
+
const delay =
|
|
141
|
+
this.state.expires_at.getTime() - Date.now() + this.config.resume_buffer_ms
|
|
142
|
+
if (delay > 0) {
|
|
143
|
+
this.timer_generation++
|
|
144
|
+
const current_generation = this.timer_generation
|
|
145
|
+
this.resume_timer = setTimeout(() => {
|
|
146
|
+
if (current_generation === this.timer_generation) this.lift()
|
|
147
|
+
}, delay)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
clear_resume_timer() {
|
|
153
|
+
if (this.resume_timer) {
|
|
154
|
+
clearTimeout(this.resume_timer)
|
|
155
|
+
this.resume_timer = null
|
|
156
|
+
this.timer_generation++
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
exports.timelock_guard = timelock_guard
|
|
162
|
+
module.exports = { timelock_guard }
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true })
|
|
4
|
+
|
|
5
|
+
const WAProto_1 = require('../../WAProto')
|
|
6
|
+
const auth_utils_1 = require('./auth-utils')
|
|
7
|
+
const generics_1 = require('./generics')
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
code from amiruldev readjusted by @irull2nd, don't delete WM!
|
|
11
|
+
*/
|
|
12
|
+
const useMongoFileAuthState = async (collection) => {
|
|
13
|
+
const writeData = (data, id) => {
|
|
14
|
+
const informationToStore = JSON.parse(JSON.stringify(data, generics_1.BufferJSON.replacer))
|
|
15
|
+
const update = {
|
|
16
|
+
$set: {
|
|
17
|
+
...informationToStore
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return collection.updateOne({ _id: id }, update, { upsert: true })
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const readData = async (id) => {
|
|
25
|
+
try {
|
|
26
|
+
const data = JSON.stringify(await collection.findOne({ _id: id }))
|
|
27
|
+
return JSON.parse(data, generics_1.BufferJSON.reviver)
|
|
28
|
+
} catch (err) {
|
|
29
|
+
console.log(err)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const removeData = async (id) => {
|
|
34
|
+
try {
|
|
35
|
+
await collection.deleteOne({ _id: id })
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.log('error', err)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const creds = (await readData('creds')) || auth_utils_1.initAuthCreds()
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
state: {
|
|
45
|
+
creds,
|
|
46
|
+
keys: {
|
|
47
|
+
get: async (type, ids) => {
|
|
48
|
+
const data = {}
|
|
49
|
+
await Promise.all(
|
|
50
|
+
ids.map(async (id) => {
|
|
51
|
+
let value = await readData(`${type}-${id}`)
|
|
52
|
+
if (type === 'app-state-sync-key') {
|
|
53
|
+
value = WAProto_1.proto.Message.AppStateSyncKeyData.fromObject(data)
|
|
54
|
+
}
|
|
55
|
+
data[id] = value
|
|
56
|
+
})
|
|
57
|
+
)
|
|
58
|
+
return data
|
|
59
|
+
},
|
|
60
|
+
set: async (data) => {
|
|
61
|
+
const tasks = []
|
|
62
|
+
for (const category of Object.keys(data)) {
|
|
63
|
+
for (const id of Object.keys(data[category])) {
|
|
64
|
+
const value = data[category][id]
|
|
65
|
+
const key = `${category}-${id}`
|
|
66
|
+
tasks.push(value ? writeData(value, key) : removeData(key))
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
await Promise.all(tasks)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
saveCreds: () => {
|
|
74
|
+
return writeData(creds, 'creds')
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = {
|
|
80
|
+
useMongoFileAuthState
|
|
81
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true })
|
|
3
|
+
exports.useMultiFileAuthState = void 0
|
|
4
|
+
const async_mutex_1 = require('async-mutex')
|
|
5
|
+
const promises_1 = require('fs/promises')
|
|
6
|
+
const path_1 = require('path')
|
|
7
|
+
const WAProto_1 = require('../../WAProto')
|
|
8
|
+
const auth_utils_1 = require('./auth-utils')
|
|
9
|
+
const generics_1 = require('./generics')
|
|
10
|
+
const fileLocks = new Map()
|
|
11
|
+
const getFileLock = (path) => {
|
|
12
|
+
let mutex = fileLocks.get(path)
|
|
13
|
+
if (!mutex) {
|
|
14
|
+
mutex = new async_mutex_1.Mutex()
|
|
15
|
+
fileLocks.set(path, mutex)
|
|
16
|
+
}
|
|
17
|
+
return mutex
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* stores the full authentication state in a single folder.
|
|
21
|
+
* Far more efficient than singlefileauthstate
|
|
22
|
+
*
|
|
23
|
+
* Again, I wouldn't endorse this for any production level use other than perhaps a bot.
|
|
24
|
+
* Would recommend writing an auth state for use with a proper SQL or No-SQL DB
|
|
25
|
+
* */
|
|
26
|
+
const useMultiFileAuthState = async (folder) => {
|
|
27
|
+
const writeData = async (data, file) => {
|
|
28
|
+
const filePath = (0, path_1.join)(folder, fixFileName(file))
|
|
29
|
+
const mutex = getFileLock(filePath)
|
|
30
|
+
return mutex.acquire().then(async (release) => {
|
|
31
|
+
try {
|
|
32
|
+
await (0, promises_1.writeFile)(
|
|
33
|
+
filePath,
|
|
34
|
+
JSON.stringify(data, generics_1.BufferJSON.replacer)
|
|
35
|
+
)
|
|
36
|
+
} finally {
|
|
37
|
+
release()
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
const readData = async (file) => {
|
|
42
|
+
try {
|
|
43
|
+
const filePath = (0, path_1.join)(folder, fixFileName(file))
|
|
44
|
+
const mutex = getFileLock(filePath)
|
|
45
|
+
return await mutex.acquire().then(async (release) => {
|
|
46
|
+
try {
|
|
47
|
+
const data = await (0, promises_1.readFile)(filePath, { encoding: 'utf-8' })
|
|
48
|
+
return JSON.parse(data, generics_1.BufferJSON.reviver)
|
|
49
|
+
} finally {
|
|
50
|
+
release()
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
} catch (error) {
|
|
54
|
+
return null
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const removeData = async (file) => {
|
|
58
|
+
try {
|
|
59
|
+
const filePath = (0, path_1.join)(folder, fixFileName(file))
|
|
60
|
+
const mutex = getFileLock(filePath)
|
|
61
|
+
return mutex.acquire().then(async (release) => {
|
|
62
|
+
try {
|
|
63
|
+
await (0, promises_1.unlink)(filePath)
|
|
64
|
+
} catch (_a) {
|
|
65
|
+
} finally {
|
|
66
|
+
release()
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
} catch (_a) {}
|
|
70
|
+
}
|
|
71
|
+
const folderInfo = await (0, promises_1.stat)(folder).catch(() => {})
|
|
72
|
+
if (folderInfo) {
|
|
73
|
+
if (!folderInfo.isDirectory()) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`found something that is not a directory at ${folder}, either delete it or specify a different location`
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
await (0, promises_1.mkdir)(folder, { recursive: true })
|
|
80
|
+
}
|
|
81
|
+
const fixFileName = (file) => {
|
|
82
|
+
var _a
|
|
83
|
+
return (_a = file === null || file === void 0 ? void 0 : file.replace(/\//g, '__')) ===
|
|
84
|
+
null || _a === void 0
|
|
85
|
+
? void 0
|
|
86
|
+
: _a.replace(/:/g, '-')
|
|
87
|
+
}
|
|
88
|
+
const creds = (await readData('creds.json')) || (0, auth_utils_1.initAuthCreds)()
|
|
89
|
+
return {
|
|
90
|
+
state: {
|
|
91
|
+
creds,
|
|
92
|
+
keys: {
|
|
93
|
+
get: async (type, ids) => {
|
|
94
|
+
const data = {}
|
|
95
|
+
await Promise.all(
|
|
96
|
+
ids.map(async (id) => {
|
|
97
|
+
let value = await readData(`${type}-${id}.json`)
|
|
98
|
+
if (type === 'app-state-sync-key' && value) {
|
|
99
|
+
value =
|
|
100
|
+
WAProto_1.proto.Message.AppStateSyncKeyData.fromObject(value)
|
|
101
|
+
}
|
|
102
|
+
data[id] = value
|
|
103
|
+
})
|
|
104
|
+
)
|
|
105
|
+
return data
|
|
106
|
+
},
|
|
107
|
+
set: async (data) => {
|
|
108
|
+
const tasks = []
|
|
109
|
+
for (const category in data) {
|
|
110
|
+
for (const id in data[category]) {
|
|
111
|
+
const value = data[category][id]
|
|
112
|
+
const file = `${category}-${id}.json`
|
|
113
|
+
tasks.push(value ? writeData(value, file) : removeData(file))
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
await Promise.all(tasks)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
saveCreds: async () => {
|
|
121
|
+
return writeData(creds, 'creds.json')
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
exports.useMultiFileAuthState = useMultiFileAuthState
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ILogger } from './logger'
|
|
2
|
+
import type { AuthenticationState } from '../Types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @deprecated use multi file auth state instead please
|
|
6
|
+
* stores the full authentication state in a single JSON file
|
|
7
|
+
*
|
|
8
|
+
* DO NOT USE IN A PROD ENVIRONMENT, only meant to serve as an example
|
|
9
|
+
* */
|
|
10
|
+
export declare const useSingleFileAuthState: (filename: string, logger?: ILogger) => {
|
|
11
|
+
state: AuthenticationState
|
|
12
|
+
saveState: () => void
|
|
13
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true })
|
|
4
|
+
|
|
5
|
+
const WAProto_1 = require('../../WAProto')
|
|
6
|
+
const auth_utils_1 = require('./auth-utils')
|
|
7
|
+
const generics_1 = require('./generics')
|
|
8
|
+
|
|
9
|
+
// useless key map only there to maintain backwards compatibility
|
|
10
|
+
// do not use in your own systems please
|
|
11
|
+
const KEY_MAP = {
|
|
12
|
+
'pre-key': 'preKeys',
|
|
13
|
+
session: 'sessions',
|
|
14
|
+
'sender-key': 'senderKeys',
|
|
15
|
+
'app-state-sync-key': 'appStateSyncKeys',
|
|
16
|
+
'app-state-sync-version': 'appStateVersions',
|
|
17
|
+
'sender-key-memory': 'senderKeyMemory',
|
|
18
|
+
'lid-mapping': 'lidMappings'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @deprecated use multi file auth state instead please
|
|
23
|
+
* stores the full authentication state in a single JSON file
|
|
24
|
+
*
|
|
25
|
+
* DO NOT USE IN A PROD ENVIRONMENT, only meant to serve as an example
|
|
26
|
+
* */
|
|
27
|
+
const useSingleFileAuthState = (filename, logger) => {
|
|
28
|
+
// require fs here so that in case "fs" is not available -- the app does not crash
|
|
29
|
+
const { readFileSync, writeFileSync, existsSync } = require('fs')
|
|
30
|
+
let creds
|
|
31
|
+
let keys = {}
|
|
32
|
+
// save the authentication state to a file
|
|
33
|
+
const saveState = () => {
|
|
34
|
+
logger && logger.trace('saving auth state')
|
|
35
|
+
writeFileSync(
|
|
36
|
+
filename,
|
|
37
|
+
// BufferJSON replacer utility saves buffers nicely
|
|
38
|
+
JSON.stringify({ creds, keys }, generics_1.BufferJSON.replacer, 2)
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
if (existsSync(filename)) {
|
|
42
|
+
const result = JSON.parse(
|
|
43
|
+
readFileSync(filename, { encoding: 'utf-8' }),
|
|
44
|
+
generics_1.BufferJSON.reviver
|
|
45
|
+
)
|
|
46
|
+
creds = result.creds
|
|
47
|
+
keys = result.keys
|
|
48
|
+
} else {
|
|
49
|
+
creds = auth_utils_1.initAuthCreds()
|
|
50
|
+
keys = {}
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
state: {
|
|
54
|
+
creds,
|
|
55
|
+
keys: {
|
|
56
|
+
get: (type, ids) => {
|
|
57
|
+
const key = KEY_MAP[type]
|
|
58
|
+
return ids.reduce((dict, id) => {
|
|
59
|
+
let value = keys[key]?.[id]
|
|
60
|
+
if (value) {
|
|
61
|
+
if (type === 'app-state-sync-key') {
|
|
62
|
+
value =
|
|
63
|
+
WAProto_1.proto.Message.AppStateSyncKeyData.fromObject(value)
|
|
64
|
+
}
|
|
65
|
+
dict[id] = value
|
|
66
|
+
}
|
|
67
|
+
return dict
|
|
68
|
+
}, {})
|
|
69
|
+
},
|
|
70
|
+
set: (data) => {
|
|
71
|
+
for (const _key in data) {
|
|
72
|
+
const key = KEY_MAP[_key]
|
|
73
|
+
keys[key] = keys[key] || {}
|
|
74
|
+
Object.assign(keys[key], data[_key])
|
|
75
|
+
}
|
|
76
|
+
saveState()
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
saveState
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = {
|
|
85
|
+
useSingleFileAuthState
|
|
86
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true })
|
|
3
|
+
exports.use_sqlite_auth_state = void 0
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file use-sqlite-auth-state.js
|
|
7
|
+
* @description Auth state persisted in a local SQLite database (using
|
|
8
|
+
* Node's built-in `node:sqlite` — no external DB driver needed).
|
|
9
|
+
* Much faster and more robust than one-file-per-key JSON storage for
|
|
10
|
+
* bots with heavy session key churn. Requires Node.js 22.5+.
|
|
11
|
+
* Can auto-migrate an existing folder-based (useMultiFileAuthState)
|
|
12
|
+
* session into the new SQLite db on first run.
|
|
13
|
+
* @author Denzy ZeroDay
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const { mkdir, readdir, readFile } = require('fs/promises')
|
|
17
|
+
const { dirname, join } = require('path')
|
|
18
|
+
const { proto } = require('../../WAProto')
|
|
19
|
+
const { initAuthCreds } = require('./auth-utils')
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {string} path_or_folder - full .db/.sqlite path, or a folder (fileName is appended)
|
|
23
|
+
* @param {object} options
|
|
24
|
+
* @param {string} options.fileName - db file name when path_or_folder is a folder (default 'auth.db')
|
|
25
|
+
* @param {string} options.migrateFromFolder - optional legacy useMultiFileAuthState folder to import from on first run
|
|
26
|
+
* @param {object} options.logger - optional logger with .warn/.info
|
|
27
|
+
* @returns {Promise<{ state, saveCreds, db, close }>}
|
|
28
|
+
*/
|
|
29
|
+
const use_sqlite_auth_state = async (path_or_folder, options = {}) => {
|
|
30
|
+
const { fileName = 'auth.db', migrateFromFolder: migrate_from_folder, logger } = options
|
|
31
|
+
const db_path = /\.(db|sqlite|sqlite3)$/i.test(path_or_folder) ? path_or_folder : join(path_or_folder, fileName)
|
|
32
|
+
|
|
33
|
+
const buf_replacer = (_, value) => {
|
|
34
|
+
if (Buffer.isBuffer(value) || value instanceof Uint8Array || value?.type === 'Buffer') {
|
|
35
|
+
return { type: 'Buffer', data: Buffer.from(value?.data || value).toString('base64') }
|
|
36
|
+
}
|
|
37
|
+
return value
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const buf_reviver = (_, value) => {
|
|
41
|
+
if (value && typeof value === 'object' && value.type === 'Buffer' && typeof value.data === 'string') {
|
|
42
|
+
return Buffer.from(value.data, 'base64')
|
|
43
|
+
}
|
|
44
|
+
return value
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const encode = (value) => {
|
|
48
|
+
if (Buffer.isBuffer(value) || value instanceof Uint8Array) {
|
|
49
|
+
return Buffer.concat([Buffer.from([1]), Buffer.from(value)])
|
|
50
|
+
}
|
|
51
|
+
return Buffer.concat([Buffer.from([0]), Buffer.from(JSON.stringify(value, buf_replacer), 'utf8')])
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const decode = (blob) => {
|
|
55
|
+
if (!blob || blob.length === 0) return null
|
|
56
|
+
if (blob[0] === 1) return Buffer.from(blob.subarray(1))
|
|
57
|
+
return JSON.parse(Buffer.from(blob.subarray(1)).toString('utf8'), buf_reviver)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const fix_name = (s) => s?.replace(/\//g, '__')?.replace(/:/g, '-')
|
|
61
|
+
const key_of = (category, id) => fix_name(`${category}-${id}`)
|
|
62
|
+
|
|
63
|
+
let DatabaseSync
|
|
64
|
+
try {
|
|
65
|
+
;({ DatabaseSync } = await import('node:sqlite'))
|
|
66
|
+
} catch (err) {
|
|
67
|
+
throw new Error("use_sqlite_auth_state needs the built-in 'node:sqlite' module (Node 22.5+). Upgrade Node, or use useMultiFileAuthState instead.")
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
await mkdir(dirname(db_path), { recursive: true }).catch(() => {})
|
|
71
|
+
|
|
72
|
+
const db = new DatabaseSync(db_path)
|
|
73
|
+
db.exec('PRAGMA journal_mode = TRUNCATE')
|
|
74
|
+
db.exec('PRAGMA synchronous = NORMAL')
|
|
75
|
+
db.exec('PRAGMA busy_timeout = 5000')
|
|
76
|
+
db.exec('CREATE TABLE IF NOT EXISTS auth_state (k TEXT PRIMARY KEY, v BLOB NOT NULL) WITHOUT ROWID')
|
|
77
|
+
|
|
78
|
+
const q_get = db.prepare('SELECT v FROM auth_state WHERE k = ?')
|
|
79
|
+
const q_upsert = db.prepare('INSERT INTO auth_state(k,v) VALUES(?,?) ON CONFLICT(k) DO UPDATE SET v=excluded.v')
|
|
80
|
+
const q_delete = db.prepare('DELETE FROM auth_state WHERE k = ?')
|
|
81
|
+
|
|
82
|
+
const run_tx = (fn) => {
|
|
83
|
+
db.exec('BEGIN')
|
|
84
|
+
try {
|
|
85
|
+
const r = fn()
|
|
86
|
+
db.exec('COMMIT')
|
|
87
|
+
return r
|
|
88
|
+
} catch (err) {
|
|
89
|
+
db.exec('ROLLBACK')
|
|
90
|
+
throw err
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const read_raw = (k) => {
|
|
95
|
+
const row = q_get.get(k)
|
|
96
|
+
if (!row) return null
|
|
97
|
+
try {
|
|
98
|
+
return decode(row.v)
|
|
99
|
+
} catch (err) {
|
|
100
|
+
logger?.warn?.({ k, err: err?.message }, 'sqlite-auth: failed to decode row, treating as missing')
|
|
101
|
+
return null
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const run_migration = async (folder) => {
|
|
106
|
+
let files
|
|
107
|
+
try {
|
|
108
|
+
files = await readdir(folder)
|
|
109
|
+
} catch {
|
|
110
|
+
return 0
|
|
111
|
+
}
|
|
112
|
+
const rows = []
|
|
113
|
+
for (const file of files) {
|
|
114
|
+
if (!file.endsWith('.json')) continue
|
|
115
|
+
let value
|
|
116
|
+
try {
|
|
117
|
+
value = JSON.parse(await readFile(join(folder, file), 'utf8'), buf_reviver)
|
|
118
|
+
} catch {
|
|
119
|
+
continue
|
|
120
|
+
}
|
|
121
|
+
if (value === null || value === undefined) continue
|
|
122
|
+
rows.push([file.slice(0, -'.json'.length), encode(value)])
|
|
123
|
+
}
|
|
124
|
+
run_tx(() => {
|
|
125
|
+
for (const [k, v] of rows) q_upsert.run(k, v)
|
|
126
|
+
})
|
|
127
|
+
return rows.length
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const has_creds = () => !!q_get.get('creds')
|
|
131
|
+
|
|
132
|
+
if (migrate_from_folder && !has_creds()) {
|
|
133
|
+
const n = await run_migration(migrate_from_folder)
|
|
134
|
+
if (n > 0) logger?.info?.({ count: n, from: migrate_from_folder }, 'sqlite-auth: migrated legacy auth state')
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let creds = read_raw('creds')
|
|
138
|
+
if (!creds) {
|
|
139
|
+
creds = initAuthCreds()
|
|
140
|
+
q_upsert.run('creds', encode(creds))
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const keys = {
|
|
144
|
+
get: async (type, ids) => {
|
|
145
|
+
const data = {}
|
|
146
|
+
for (const id of ids) {
|
|
147
|
+
let value = read_raw(key_of(type, id))
|
|
148
|
+
if (type === 'app-state-sync-key' && value) {
|
|
149
|
+
value = proto.Message.AppStateSyncKeyData.fromObject(value)
|
|
150
|
+
}
|
|
151
|
+
if (value !== null && value !== undefined) {
|
|
152
|
+
data[id] = value
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return data
|
|
156
|
+
},
|
|
157
|
+
set: async (data) => {
|
|
158
|
+
const ops = []
|
|
159
|
+
for (const category in data) {
|
|
160
|
+
for (const id in data[category]) {
|
|
161
|
+
ops.push([key_of(category, id), data[category][id]])
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
run_tx(() => {
|
|
165
|
+
for (const [k, value] of ops) {
|
|
166
|
+
if (value === null || value === undefined) q_delete.run(k)
|
|
167
|
+
else q_upsert.run(k, encode(value))
|
|
168
|
+
}
|
|
169
|
+
})
|
|
170
|
+
},
|
|
171
|
+
clear: async () => {
|
|
172
|
+
db.prepare("DELETE FROM auth_state WHERE k <> 'creds'").run()
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
state: { creds, keys },
|
|
178
|
+
saveCreds: async () => {
|
|
179
|
+
q_upsert.run('creds', encode(creds))
|
|
180
|
+
},
|
|
181
|
+
db,
|
|
182
|
+
close: () => db.close()
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
exports.use_sqlite_auth_state = use_sqlite_auth_state
|
|
187
|
+
module.exports = { use_sqlite_auth_state }
|