@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,147 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true })
|
|
3
|
+
exports.GroupOperationGuard =
|
|
4
|
+
exports.extractPrivacyBlock =
|
|
5
|
+
exports.classifyGroupOpError =
|
|
6
|
+
exports.GROUP_OP_ERRORS =
|
|
7
|
+
void 0
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @file group-operation-guard.js
|
|
11
|
+
* @description Rate limiting for WhatsApp group operations.
|
|
12
|
+
* Prevents account_reachout_restricted and rate-overlimit errors
|
|
13
|
+
* by enforcing per-operation windows on group adds, removes, and creates.
|
|
14
|
+
*
|
|
15
|
+
* WA unofficial limits (observed):
|
|
16
|
+
* - groupParticipantsUpdate (add): ~3 new contacts per 10 min
|
|
17
|
+
* - groupCreate: ~2 per 10 min
|
|
18
|
+
* - Rapid retries after 403: triggers account_reachout_restricted
|
|
19
|
+
*
|
|
20
|
+
* Ported from baileys-antiban to CJS for bilis.
|
|
21
|
+
* @author Denzy ZeroDay (port), original by baileys-antiban contributors
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const DEFAULT_LIMITS = {
|
|
25
|
+
add: { max: 3, windowMs: 10 * 60 * 1000 }, // 3 adds / 10 min
|
|
26
|
+
remove: { max: 5, windowMs: 10 * 60 * 1000 }, // 5 removes / 10 min
|
|
27
|
+
create: { max: 2, windowMs: 10 * 60 * 1000 }, // 2 creates / 10 min
|
|
28
|
+
invite: { max: 10, windowMs: 10 * 60 * 1000 } // 10 invite fetches / 10 min
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Known WA error patterns for group operations */
|
|
32
|
+
const GROUP_OP_ERRORS = {
|
|
33
|
+
REACHOUT_RESTRICTED: 'account_reachout_restricted',
|
|
34
|
+
RATE_OVERLIMIT: 'rate-overlimit',
|
|
35
|
+
PRIVACY_BLOCK: '403',
|
|
36
|
+
INVITE_EXPIRED: 'gone',
|
|
37
|
+
GROUP_LOCKED: 'locked'
|
|
38
|
+
}
|
|
39
|
+
exports.GROUP_OP_ERRORS = GROUP_OP_ERRORS
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Classify a caught error from a group operation.
|
|
43
|
+
* @param {unknown} err
|
|
44
|
+
* @returns {string|null}
|
|
45
|
+
*/
|
|
46
|
+
const classifyGroupOpError = (err) => {
|
|
47
|
+
const msg = err instanceof Error ? err.message : String(err)
|
|
48
|
+
if (msg.includes('account_reachout_restricted') || msg.includes('reachout'))
|
|
49
|
+
return GROUP_OP_ERRORS.REACHOUT_RESTRICTED
|
|
50
|
+
if (msg.includes('rate-overlimit') || msg.includes('429')) return GROUP_OP_ERRORS.RATE_OVERLIMIT
|
|
51
|
+
if (msg.includes('locked')) return GROUP_OP_ERRORS.GROUP_LOCKED
|
|
52
|
+
if (msg.includes('gone')) return GROUP_OP_ERRORS.INVITE_EXPIRED
|
|
53
|
+
return null
|
|
54
|
+
}
|
|
55
|
+
exports.classifyGroupOpError = classifyGroupOpError
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Check whether a groupParticipantsUpdate result contains a privacy-block 403
|
|
59
|
+
* with an invite code that should be used instead of direct add.
|
|
60
|
+
* @param {Array} result - Result from sock.groupParticipantsUpdate()
|
|
61
|
+
* @returns {{ blocked: boolean, inviteCode?: string, inviteLink?: string }}
|
|
62
|
+
*/
|
|
63
|
+
const extractPrivacyBlock = (result) => {
|
|
64
|
+
if (!Array.isArray(result)) return { blocked: false }
|
|
65
|
+
for (const r of result) {
|
|
66
|
+
if (r.status !== '403') continue
|
|
67
|
+
const content = Array.isArray(r.content) ? r.content : []
|
|
68
|
+
for (const c of content) {
|
|
69
|
+
if (c.tag === 'add_request' && typeof c.attrs === 'object' && c.attrs.code) {
|
|
70
|
+
return {
|
|
71
|
+
blocked: true,
|
|
72
|
+
inviteCode: c.attrs.code,
|
|
73
|
+
inviteLink: `https://chat.whatsapp.com/${c.attrs.code}`
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return { blocked: true }
|
|
78
|
+
}
|
|
79
|
+
return { blocked: false }
|
|
80
|
+
}
|
|
81
|
+
exports.extractPrivacyBlock = extractPrivacyBlock
|
|
82
|
+
|
|
83
|
+
class GroupOperationGuard {
|
|
84
|
+
/**
|
|
85
|
+
* @param {object} config
|
|
86
|
+
* @param {object} config.limits - Override per-operation limits
|
|
87
|
+
*/
|
|
88
|
+
constructor(config = {}) {
|
|
89
|
+
this.limits = { ...DEFAULT_LIMITS, ...(config.limits || {}) }
|
|
90
|
+
this.windows = new Map()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Check whether an operation is allowed under the current rate limits.
|
|
95
|
+
* @param {'add'|'remove'|'create'|'invite'} op - Operation type
|
|
96
|
+
* @param {string} key - Unique key scoping the limit (e.g. groupJid)
|
|
97
|
+
* @returns {{ allowed: boolean, reason?: string, retryAfterSec?: number }}
|
|
98
|
+
*/
|
|
99
|
+
check(op, key) {
|
|
100
|
+
const limit = this.limits[op]
|
|
101
|
+
if (!limit) return { allowed: true }
|
|
102
|
+
const windowKey = `${op}:${key}`
|
|
103
|
+
const now = Date.now()
|
|
104
|
+
const entry = this.windows.get(windowKey)
|
|
105
|
+
|
|
106
|
+
if (!entry || now > entry.resetAt) {
|
|
107
|
+
this.windows.set(windowKey, { count: 1, resetAt: now + limit.windowMs })
|
|
108
|
+
return { allowed: true }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (entry.count >= limit.max) {
|
|
112
|
+
const retryAfterSec = Math.ceil((entry.resetAt - now) / 1000)
|
|
113
|
+
return {
|
|
114
|
+
allowed: false,
|
|
115
|
+
reason: `Too many ${op} attempts — wait ${Math.ceil(retryAfterSec / 60)} min before trying again.`,
|
|
116
|
+
retryAfterSec
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
entry.count++
|
|
121
|
+
return { allowed: true }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Reset the counter for a specific operation + key.
|
|
126
|
+
* @param {'add'|'remove'|'create'|'invite'} op
|
|
127
|
+
* @param {string} key
|
|
128
|
+
*/
|
|
129
|
+
reset(op, key) {
|
|
130
|
+
this.windows.delete(`${op}:${key}`)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Snapshot of all active windows.
|
|
135
|
+
* @returns {object}
|
|
136
|
+
*/
|
|
137
|
+
getStats() {
|
|
138
|
+
const stats = {}
|
|
139
|
+
for (const [key, val] of this.windows) {
|
|
140
|
+
stats[key] = { ...val }
|
|
141
|
+
}
|
|
142
|
+
return stats
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
exports.GroupOperationGuard = GroupOperationGuard
|
|
147
|
+
module.exports = { GroupOperationGuard, classifyGroupOpError, extractPrivacyBlock, GROUP_OP_ERRORS }
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true })
|
|
3
|
+
exports.health_monitor = void 0
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file health-monitor.js
|
|
7
|
+
* @description Tracks disconnects, forbidden errors, logouts, and failed
|
|
8
|
+
* messages to compute a ban-risk score with actionable recommendations.
|
|
9
|
+
* @author Denzy ZeroDay
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const default_config = {
|
|
13
|
+
disconnect_warning_threshold: 3,
|
|
14
|
+
disconnect_critical_threshold: 5,
|
|
15
|
+
failed_message_threshold: 5,
|
|
16
|
+
auto_pause_at: 'high'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
class health_monitor {
|
|
20
|
+
constructor(config = {}) {
|
|
21
|
+
this.config = { ...default_config, ...config }
|
|
22
|
+
this.events = []
|
|
23
|
+
this.start_time = Date.now()
|
|
24
|
+
this.paused = false
|
|
25
|
+
this.last_risk = 'low'
|
|
26
|
+
this.last_bad_event_time = Date.now()
|
|
27
|
+
this.last_event_was_severe = false
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Record a disconnection event */
|
|
31
|
+
record_disconnect(reason) {
|
|
32
|
+
const reason_str = String(reason)
|
|
33
|
+
if (reason_str === '403' || reason_str === 'forbidden') {
|
|
34
|
+
this.events.push({ type: 'forbidden', timestamp: Date.now(), detail: reason_str })
|
|
35
|
+
this.last_bad_event_time = Date.now()
|
|
36
|
+
this.last_event_was_severe = true
|
|
37
|
+
} else if (reason_str === '401' || reason_str === 'loggedOut') {
|
|
38
|
+
this.events.push({ type: 'loggedOut', timestamp: Date.now(), detail: reason_str })
|
|
39
|
+
this.last_bad_event_time = Date.now()
|
|
40
|
+
this.last_event_was_severe = true
|
|
41
|
+
} else if (reason_str === '463') {
|
|
42
|
+
this.events.push({
|
|
43
|
+
type: 'reachoutTimelocked',
|
|
44
|
+
timestamp: Date.now(),
|
|
45
|
+
detail: reason_str
|
|
46
|
+
})
|
|
47
|
+
this.last_bad_event_time = Date.now()
|
|
48
|
+
this.last_event_was_severe = false
|
|
49
|
+
} else {
|
|
50
|
+
this.events.push({ type: 'disconnect', timestamp: Date.now(), detail: reason_str })
|
|
51
|
+
this.last_bad_event_time = Date.now()
|
|
52
|
+
this.last_event_was_severe = false
|
|
53
|
+
}
|
|
54
|
+
this._check_and_notify()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Record a successful reconnection */
|
|
58
|
+
record_reconnect() {
|
|
59
|
+
this.events.push({ type: 'reconnect', timestamp: Date.now() })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Record a failed message send */
|
|
63
|
+
record_message_failed(error) {
|
|
64
|
+
this.events.push({ type: 'messageFailed', timestamp: Date.now(), detail: error })
|
|
65
|
+
this.last_bad_event_time = Date.now()
|
|
66
|
+
this.last_event_was_severe = false
|
|
67
|
+
this._check_and_notify()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Record a 463 reachout timelock error */
|
|
71
|
+
record_reachout_timelock(detail) {
|
|
72
|
+
this.events.push({ type: 'reachoutTimelocked', timestamp: Date.now(), detail })
|
|
73
|
+
this.last_bad_event_time = Date.now()
|
|
74
|
+
this.last_event_was_severe = false
|
|
75
|
+
this._check_and_notify()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Get current health status with ban-risk score and recommendation */
|
|
79
|
+
get_status() {
|
|
80
|
+
const now = Date.now()
|
|
81
|
+
this._cleanup(now)
|
|
82
|
+
|
|
83
|
+
const hour_events = this.events.filter((e) => now - e.timestamp < 3600000)
|
|
84
|
+
const disconnects = hour_events.filter((e) => e.type === 'disconnect').length
|
|
85
|
+
const forbidden = hour_events.filter((e) => e.type === 'forbidden').length
|
|
86
|
+
const logged_out = hour_events.filter((e) => e.type === 'loggedOut').length
|
|
87
|
+
const failed_messages = hour_events.filter((e) => e.type === 'messageFailed').length
|
|
88
|
+
const timelocked = hour_events.filter((e) => e.type === 'reachoutTimelocked').length
|
|
89
|
+
|
|
90
|
+
let score = 0
|
|
91
|
+
const reasons = []
|
|
92
|
+
|
|
93
|
+
if (forbidden > 0) {
|
|
94
|
+
score += 40 * forbidden
|
|
95
|
+
reasons.push(
|
|
96
|
+
`${forbidden} forbidden (403) error${forbidden > 1 ? 's' : ''} in last hour`
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
if (logged_out > 0) {
|
|
100
|
+
score += 60
|
|
101
|
+
reasons.push('Logged out by WhatsApp — possible temporary ban')
|
|
102
|
+
}
|
|
103
|
+
if (timelocked > 0) {
|
|
104
|
+
score += 25
|
|
105
|
+
reasons.push(
|
|
106
|
+
`${timelocked} reachout timelock (463) error${timelocked > 1 ? 's' : ''} in last hour`
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
if (disconnects >= this.config.disconnect_critical_threshold) {
|
|
110
|
+
score += 30
|
|
111
|
+
reasons.push(`${disconnects} disconnects in last hour (critical threshold)`)
|
|
112
|
+
} else if (disconnects >= this.config.disconnect_warning_threshold) {
|
|
113
|
+
score += 30
|
|
114
|
+
reasons.push(`${disconnects} disconnects in last hour`)
|
|
115
|
+
}
|
|
116
|
+
if (failed_messages >= this.config.failed_message_threshold) {
|
|
117
|
+
score += 20
|
|
118
|
+
reasons.push(`${failed_messages} failed messages in last hour`)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
score = Math.min(100, score)
|
|
122
|
+
const minutes_since_last_bad = (now - this.last_bad_event_time) / 60000
|
|
123
|
+
const decay_rate = this.last_event_was_severe ? 2 : 5
|
|
124
|
+
score = Math.max(0, score - Math.floor(minutes_since_last_bad * decay_rate))
|
|
125
|
+
|
|
126
|
+
let risk
|
|
127
|
+
if (score >= 80) risk = 'critical'
|
|
128
|
+
else if (score >= 40) risk = 'high'
|
|
129
|
+
else if (score >= 15) risk = 'medium'
|
|
130
|
+
else risk = 'low'
|
|
131
|
+
|
|
132
|
+
let recommendation
|
|
133
|
+
switch (risk) {
|
|
134
|
+
case 'critical':
|
|
135
|
+
recommendation =
|
|
136
|
+
'STOP ALL MESSAGING IMMEDIATELY. Disconnect and wait 24-48 hours before reconnecting.'
|
|
137
|
+
break
|
|
138
|
+
case 'high':
|
|
139
|
+
recommendation = 'Reduce messaging rate by 80%. Consider pausing for 1-2 hours.'
|
|
140
|
+
break
|
|
141
|
+
case 'medium':
|
|
142
|
+
recommendation = 'Reduce messaging rate by 50%. Increase delays between messages.'
|
|
143
|
+
break
|
|
144
|
+
default:
|
|
145
|
+
recommendation = 'Operating normally. Continue monitoring.'
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const last_disconnect = [...this.events]
|
|
149
|
+
.reverse()
|
|
150
|
+
.find(
|
|
151
|
+
(e) => e.type === 'disconnect' || e.type === 'forbidden' || e.type === 'loggedOut'
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
risk,
|
|
156
|
+
score,
|
|
157
|
+
reasons: reasons.length ? reasons : ['No issues detected'],
|
|
158
|
+
recommendation,
|
|
159
|
+
stats: {
|
|
160
|
+
disconnectsLastHour: disconnects,
|
|
161
|
+
failedMessagesLastHour: failed_messages,
|
|
162
|
+
forbiddenErrors: forbidden,
|
|
163
|
+
timelockErrors: timelocked,
|
|
164
|
+
uptimeMs: now - this.start_time,
|
|
165
|
+
lastDisconnectReason: last_disconnect?.detail
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Check if sending should be paused based on current risk */
|
|
171
|
+
is_paused() {
|
|
172
|
+
if (this.paused) return true
|
|
173
|
+
const status = this.get_status()
|
|
174
|
+
const risk_order = ['low', 'medium', 'high', 'critical']
|
|
175
|
+
return risk_order.indexOf(status.risk) >= risk_order.indexOf(this.config.auto_pause_at)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Manually pause/resume sending */
|
|
179
|
+
set_paused(paused) {
|
|
180
|
+
this.paused = paused
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Reset all tracked events */
|
|
184
|
+
reset() {
|
|
185
|
+
this.events = []
|
|
186
|
+
this.start_time = Date.now()
|
|
187
|
+
this.paused = false
|
|
188
|
+
this.last_risk = 'low'
|
|
189
|
+
this.last_bad_event_time = Date.now()
|
|
190
|
+
this.last_event_was_severe = false
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
_cleanup(now) {
|
|
194
|
+
this.events = this.events.filter((e) => now - e.timestamp < 21600000)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
_check_and_notify() {
|
|
198
|
+
const status = this.get_status()
|
|
199
|
+
if (status.risk !== this.last_risk) {
|
|
200
|
+
this.last_risk = status.risk
|
|
201
|
+
this.config.onRiskChange?.(status)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
exports.health_monitor = health_monitor
|
|
207
|
+
module.exports = { health_monitor }
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true })
|
|
3
|
+
exports.getHistoryMsg =
|
|
4
|
+
exports.downloadAndProcessHistorySyncNotification =
|
|
5
|
+
exports.processHistoryMessage =
|
|
6
|
+
exports.downloadHistory =
|
|
7
|
+
void 0
|
|
8
|
+
const util_1 = require('util')
|
|
9
|
+
const zlib_1 = require('zlib')
|
|
10
|
+
const WAProto_1 = require('../../WAProto')
|
|
11
|
+
const Types_1 = require('../Types')
|
|
12
|
+
const WABinary_1 = require('../WABinary')
|
|
13
|
+
const generics_1 = require('./generics')
|
|
14
|
+
const messages_1 = require('./messages')
|
|
15
|
+
const messages_media_1 = require('./messages-media')
|
|
16
|
+
const inflatePromise = (0, util_1.promisify)(zlib_1.inflate)
|
|
17
|
+
const downloadHistory = async (msg, options) => {
|
|
18
|
+
const stream = await (0, messages_media_1.downloadContentFromMessage)(msg, 'md-msg-hist', {
|
|
19
|
+
options
|
|
20
|
+
})
|
|
21
|
+
const bufferArray = []
|
|
22
|
+
for await (const chunk of stream) {
|
|
23
|
+
bufferArray.push(chunk)
|
|
24
|
+
}
|
|
25
|
+
let buffer = Buffer.concat(bufferArray)
|
|
26
|
+
// decompress buffer
|
|
27
|
+
buffer = await inflatePromise(buffer)
|
|
28
|
+
const syncData = WAProto_1.proto.HistorySync.decode(buffer)
|
|
29
|
+
return syncData
|
|
30
|
+
}
|
|
31
|
+
exports.downloadHistory = downloadHistory
|
|
32
|
+
const processHistoryMessage = (item) => {
|
|
33
|
+
var _a, _b, _c
|
|
34
|
+
const messages = []
|
|
35
|
+
const contacts = []
|
|
36
|
+
const chats = []
|
|
37
|
+
switch (item.syncType) {
|
|
38
|
+
case WAProto_1.proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP:
|
|
39
|
+
case WAProto_1.proto.HistorySync.HistorySyncType.RECENT:
|
|
40
|
+
case WAProto_1.proto.HistorySync.HistorySyncType.FULL:
|
|
41
|
+
case WAProto_1.proto.HistorySync.HistorySyncType.ON_DEMAND:
|
|
42
|
+
for (const chat of item.conversations) {
|
|
43
|
+
contacts.push({
|
|
44
|
+
id: chat.id,
|
|
45
|
+
name: chat.name || undefined,
|
|
46
|
+
lid: chat.lidJid || undefined,
|
|
47
|
+
jid: (0, WABinary_1.isJidUser)(chat.id) ? chat.id : undefined
|
|
48
|
+
})
|
|
49
|
+
const msgs = chat.messages || []
|
|
50
|
+
delete chat.messages
|
|
51
|
+
delete chat.archived
|
|
52
|
+
delete chat.muteEndTime
|
|
53
|
+
delete chat.pinned
|
|
54
|
+
for (const item of msgs) {
|
|
55
|
+
const message = item.message
|
|
56
|
+
messages.push(message)
|
|
57
|
+
if (!((_a = chat.messages) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
58
|
+
// keep only the most recent message in the chat array
|
|
59
|
+
chat.messages = [{ message }]
|
|
60
|
+
}
|
|
61
|
+
if (!message.key.fromMe && !chat.lastMessageRecvTimestamp) {
|
|
62
|
+
chat.lastMessageRecvTimestamp = (0, generics_1.toNumber)(
|
|
63
|
+
message.messageTimestamp
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
if (
|
|
67
|
+
(message.messageStubType ===
|
|
68
|
+
Types_1.WAMessageStubType.BIZ_PRIVACY_MODE_TO_BSP ||
|
|
69
|
+
message.messageStubType ===
|
|
70
|
+
Types_1.WAMessageStubType.BIZ_PRIVACY_MODE_TO_FB) &&
|
|
71
|
+
((_b = message.messageStubParameters) === null || _b === void 0
|
|
72
|
+
? void 0
|
|
73
|
+
: _b[0])
|
|
74
|
+
) {
|
|
75
|
+
contacts.push({
|
|
76
|
+
id: message.key.participant || message.key.remoteJid,
|
|
77
|
+
verifiedName:
|
|
78
|
+
(_c = message.messageStubParameters) === null || _c === void 0
|
|
79
|
+
? void 0
|
|
80
|
+
: _c[0]
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if ((0, WABinary_1.isJidUser)(chat.id) && chat.readOnly && chat.archived) {
|
|
85
|
+
delete chat.readOnly
|
|
86
|
+
}
|
|
87
|
+
chats.push({ ...chat })
|
|
88
|
+
}
|
|
89
|
+
break
|
|
90
|
+
case WAProto_1.proto.HistorySync.HistorySyncType.PUSH_NAME:
|
|
91
|
+
for (const c of item.pushnames) {
|
|
92
|
+
contacts.push({ id: c.id, notify: c.pushname })
|
|
93
|
+
}
|
|
94
|
+
break
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
chats,
|
|
98
|
+
contacts,
|
|
99
|
+
messages
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
exports.processHistoryMessage = processHistoryMessage
|
|
103
|
+
const downloadAndProcessHistorySyncNotification = async (msg, options) => {
|
|
104
|
+
const historyMsg = await (0, exports.downloadHistory)(msg, options)
|
|
105
|
+
return (0, exports.processHistoryMessage)(historyMsg)
|
|
106
|
+
}
|
|
107
|
+
exports.downloadAndProcessHistorySyncNotification = downloadAndProcessHistorySyncNotification
|
|
108
|
+
const getHistoryMsg = (message) => {
|
|
109
|
+
var _a
|
|
110
|
+
const normalizedContent = !!message
|
|
111
|
+
? (0, messages_1.normalizeMessageContent)(message)
|
|
112
|
+
: undefined
|
|
113
|
+
const anyHistoryMsg =
|
|
114
|
+
(_a =
|
|
115
|
+
normalizedContent === null || normalizedContent === void 0
|
|
116
|
+
? void 0
|
|
117
|
+
: normalizedContent.protocolMessage) === null || _a === void 0
|
|
118
|
+
? void 0
|
|
119
|
+
: _a.historySyncNotification
|
|
120
|
+
return anyHistoryMsg
|
|
121
|
+
}
|
|
122
|
+
exports.getHistoryMsg = getHistoryMsg
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true })
|
|
3
|
+
exports.HumanEntropyService = exports.createHumanEntropyService = void 0
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file human-entropy.js
|
|
7
|
+
* @description Background noise for WA sessions — fake human activity
|
|
8
|
+
* to prevent bot detection. Performs random typing presence, delayed
|
|
9
|
+
* read receipts, and availability status toggles periodically.
|
|
10
|
+
* Ported from baileys-antiban to CJS for bilis.
|
|
11
|
+
* @author Denzy ZeroDay (port), original by baileys-antiban contributors
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const DEFAULT_CONFIG = {
|
|
15
|
+
enabled: true,
|
|
16
|
+
minIntervalMs: 2 * 60 * 60 * 1000, // 2 hours
|
|
17
|
+
maxIntervalMs: 6 * 60 * 60 * 1000, // 6 hours
|
|
18
|
+
maxRecentContacts: 30,
|
|
19
|
+
typingProbability: 0.3,
|
|
20
|
+
typingMinMs: 3000,
|
|
21
|
+
typingMaxMs: 8000,
|
|
22
|
+
readReceiptProbability: 0.2,
|
|
23
|
+
readReceiptMinDelayMs: 10 * 60 * 1000, // 10 min
|
|
24
|
+
readReceiptMaxDelayMs: 60 * 60 * 1000, // 60 min
|
|
25
|
+
presenceToggleProbability: 0.15,
|
|
26
|
+
presenceToggleMinMs: 30 * 1000, // 30 sec
|
|
27
|
+
presenceToggleMaxMs: 2 * 60 * 1000 // 2 min
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
class HumanEntropyService {
|
|
31
|
+
/**
|
|
32
|
+
* @param {object} sock - Baileys WASocket instance
|
|
33
|
+
* @param {object} config - Optional config overrides
|
|
34
|
+
*/
|
|
35
|
+
constructor(sock, config = {}) {
|
|
36
|
+
this.sock = sock
|
|
37
|
+
this.config = { ...DEFAULT_CONFIG, ...config }
|
|
38
|
+
this.recentContacts = []
|
|
39
|
+
this.unreadMessages = []
|
|
40
|
+
this.cycleTimer = null
|
|
41
|
+
this.isRunning = false
|
|
42
|
+
this.stats = {
|
|
43
|
+
cyclesExecuted: 0,
|
|
44
|
+
typingActionsPerformed: 0,
|
|
45
|
+
readReceiptsMarked: 0,
|
|
46
|
+
presenceToggles: 0,
|
|
47
|
+
errors: 0,
|
|
48
|
+
lastCycleAt: null,
|
|
49
|
+
nextCycleAt: null
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Start the entropy service.
|
|
55
|
+
* Call this after sock is connected.
|
|
56
|
+
*/
|
|
57
|
+
start() {
|
|
58
|
+
if (!this.config.enabled) return
|
|
59
|
+
if (this.isRunning) return
|
|
60
|
+
this.isRunning = true
|
|
61
|
+
this._scheduleNextCycle()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Stop the entropy service.
|
|
66
|
+
*/
|
|
67
|
+
stop() {
|
|
68
|
+
this.isRunning = false
|
|
69
|
+
if (this.cycleTimer) {
|
|
70
|
+
clearTimeout(this.cycleTimer)
|
|
71
|
+
this.cycleTimer = null
|
|
72
|
+
}
|
|
73
|
+
this.stats.nextCycleAt = null
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Track an incoming message to build recent contacts list.
|
|
78
|
+
* Call this from messages.upsert event handler.
|
|
79
|
+
* @param {object} msg - Raw Baileys message
|
|
80
|
+
*/
|
|
81
|
+
trackMessage(msg) {
|
|
82
|
+
try {
|
|
83
|
+
if (msg.key?.fromMe) return
|
|
84
|
+
const jid = msg.key?.remoteJid
|
|
85
|
+
if (!jid) return
|
|
86
|
+
|
|
87
|
+
const isGroup = jid.endsWith('@g.us')
|
|
88
|
+
const existingIdx = this.recentContacts.findIndex((c) => c.jid === jid)
|
|
89
|
+
|
|
90
|
+
if (existingIdx >= 0) {
|
|
91
|
+
this.recentContacts[existingIdx].lastMessageAt = new Date()
|
|
92
|
+
} else {
|
|
93
|
+
this.recentContacts.push({ jid, lastMessageAt: new Date(), isGroup })
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (this.recentContacts.length > this.config.maxRecentContacts) {
|
|
97
|
+
this.recentContacts.sort((a, b) => b.lastMessageAt - a.lastMessageAt)
|
|
98
|
+
this.recentContacts = this.recentContacts.slice(0, this.config.maxRecentContacts)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (msg.key?.id && msg.message) {
|
|
102
|
+
this.unreadMessages.push({ jid, messageKey: msg.key, receivedAt: new Date() })
|
|
103
|
+
if (this.unreadMessages.length > 50) {
|
|
104
|
+
this.unreadMessages = this.unreadMessages.slice(-50)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
} catch (err) {
|
|
108
|
+
this.stats.errors++
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Get current stats
|
|
114
|
+
*/
|
|
115
|
+
getStats() {
|
|
116
|
+
return { ...this.stats }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
_scheduleNextCycle() {
|
|
120
|
+
if (!this.isRunning) return
|
|
121
|
+
const delay = this._rand(this.config.minIntervalMs, this.config.maxIntervalMs)
|
|
122
|
+
this.stats.nextCycleAt = new Date(Date.now() + delay)
|
|
123
|
+
this.cycleTimer = setTimeout(() => {
|
|
124
|
+
this._executeCycle()
|
|
125
|
+
.catch(() => this.stats.errors++)
|
|
126
|
+
.finally(() => this._scheduleNextCycle())
|
|
127
|
+
}, delay)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async _executeCycle() {
|
|
131
|
+
if (!this.isRunning || this.recentContacts.length === 0) return
|
|
132
|
+
|
|
133
|
+
this.stats.cyclesExecuted++
|
|
134
|
+
this.stats.lastCycleAt = new Date()
|
|
135
|
+
|
|
136
|
+
const actions = []
|
|
137
|
+
|
|
138
|
+
if (Math.random() < this.config.typingProbability) {
|
|
139
|
+
actions.push(this._performTyping())
|
|
140
|
+
}
|
|
141
|
+
if (Math.random() < this.config.readReceiptProbability && this.unreadMessages.length > 0) {
|
|
142
|
+
actions.push(this._performReadReceipt())
|
|
143
|
+
}
|
|
144
|
+
if (Math.random() < this.config.presenceToggleProbability) {
|
|
145
|
+
actions.push(this._performPresenceToggle())
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
await Promise.allSettled(actions)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async _performTyping() {
|
|
152
|
+
try {
|
|
153
|
+
const eligible = this.recentContacts.filter((c) => !c.isGroup)
|
|
154
|
+
if (eligible.length === 0) return
|
|
155
|
+
const contact = eligible[Math.floor(Math.random() * eligible.length)]
|
|
156
|
+
const duration = this._rand(this.config.typingMinMs, this.config.typingMaxMs)
|
|
157
|
+
|
|
158
|
+
await this.sock.sendPresenceUpdate('composing', contact.jid)
|
|
159
|
+
await this._sleep(duration)
|
|
160
|
+
await this.sock.sendPresenceUpdate('paused', contact.jid)
|
|
161
|
+
|
|
162
|
+
this.stats.typingActionsPerformed++
|
|
163
|
+
} catch (err) {
|
|
164
|
+
this.stats.errors++
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async _performReadReceipt() {
|
|
169
|
+
try {
|
|
170
|
+
const msg = this.unreadMessages[Math.floor(Math.random() * this.unreadMessages.length)]
|
|
171
|
+
if (!msg) return
|
|
172
|
+
const delay = this._rand(
|
|
173
|
+
this.config.readReceiptMinDelayMs,
|
|
174
|
+
this.config.readReceiptMaxDelayMs
|
|
175
|
+
)
|
|
176
|
+
await this._sleep(delay)
|
|
177
|
+
await this.sock.readMessages([msg.messageKey])
|
|
178
|
+
this.unreadMessages = this.unreadMessages.filter((m) => m !== msg)
|
|
179
|
+
this.stats.readReceiptsMarked++
|
|
180
|
+
} catch (err) {
|
|
181
|
+
this.stats.errors++
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async _performPresenceToggle() {
|
|
186
|
+
try {
|
|
187
|
+
const duration = this._rand(
|
|
188
|
+
this.config.presenceToggleMinMs,
|
|
189
|
+
this.config.presenceToggleMaxMs
|
|
190
|
+
)
|
|
191
|
+
await this.sock.sendPresenceUpdate('available')
|
|
192
|
+
await this._sleep(duration)
|
|
193
|
+
await this.sock.sendPresenceUpdate('unavailable')
|
|
194
|
+
this.stats.presenceToggles++
|
|
195
|
+
} catch (err) {
|
|
196
|
+
this.stats.errors++
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
_rand(min, max) {
|
|
201
|
+
return Math.floor(Math.random() * (max - min + 1)) + min
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
_sleep(ms) {
|
|
205
|
+
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Factory function
|
|
211
|
+
* @param {object} sock - Baileys WASocket
|
|
212
|
+
* @param {object} config - Optional config
|
|
213
|
+
*/
|
|
214
|
+
const createHumanEntropyService = (sock, config = {}) => {
|
|
215
|
+
const service = new HumanEntropyService(sock, config)
|
|
216
|
+
return {
|
|
217
|
+
start: () => service.start(),
|
|
218
|
+
stop: () => service.stop(),
|
|
219
|
+
trackMessage: (msg) => service.trackMessage(msg),
|
|
220
|
+
getStats: () => service.getStats()
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
exports.HumanEntropyService = HumanEntropyService
|
|
225
|
+
exports.createHumanEntropyService = createHumanEntropyService
|
|
226
|
+
module.exports = { HumanEntropyService, createHumanEntropyService }
|