@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,157 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true })
|
|
3
|
+
exports.PostReconnectThrottle = void 0
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file reconnect-throttle.js
|
|
7
|
+
* @description Throttle outbound messages after WA reconnection.
|
|
8
|
+
* Inspired by whatsapp-rust's client/sessions.rs semaphore pattern.
|
|
9
|
+
* On reconnect, gates outbound messages to a low rate then ramps
|
|
10
|
+
* back to normal over 60s — prevents burst-floods that trigger WA limits.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* const throttle = new PostReconnectThrottle({ enabled: true })
|
|
14
|
+
* // On connection.update where connection === 'open':
|
|
15
|
+
* throttle.onReconnect()
|
|
16
|
+
* // Before sending:
|
|
17
|
+
* const decision = throttle.beforeSend()
|
|
18
|
+
* if (!decision.allowed) await sleep(decision.retryAfterMs)
|
|
19
|
+
*
|
|
20
|
+
* Ported from baileys-antiban to CJS for bilis.
|
|
21
|
+
* @author Denzy ZeroDay (port), Kobus Wentzel (original)
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const DEFAULT_CONFIG = {
|
|
25
|
+
enabled: false,
|
|
26
|
+
rampDurationMs: 60000, // 60s total ramp
|
|
27
|
+
initialRateMultiplier: 0.1, // 10% rate initially
|
|
28
|
+
rampSteps: 6, // 6 steps: 10%→25%→50%→75%→90%→100%
|
|
29
|
+
baselineRatePerMinute: null // Optional getter from RateLimiter
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const WINDOW_DURATION_MS = 60000
|
|
33
|
+
|
|
34
|
+
class PostReconnectThrottle {
|
|
35
|
+
constructor(config = {}) {
|
|
36
|
+
this.config = {
|
|
37
|
+
...DEFAULT_CONFIG,
|
|
38
|
+
...config,
|
|
39
|
+
baselineRatePerMinute: config.baselineRatePerMinute || null
|
|
40
|
+
}
|
|
41
|
+
this.throttledSince = null
|
|
42
|
+
this.throttledSendCount = 0
|
|
43
|
+
this.lifetimeReconnects = 0
|
|
44
|
+
this.rampTimer = null
|
|
45
|
+
this.currentStep = 0
|
|
46
|
+
this.sendsInCurrentWindow = 0
|
|
47
|
+
this.currentWindowStart = 0
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Call when connection is re-established. Starts throttle ramp. */
|
|
51
|
+
onReconnect() {
|
|
52
|
+
if (!this.config.enabled) return
|
|
53
|
+
this.throttledSince = Date.now()
|
|
54
|
+
this.currentStep = 0
|
|
55
|
+
this.throttledSendCount = 0
|
|
56
|
+
this.lifetimeReconnects++
|
|
57
|
+
this.sendsInCurrentWindow = 0
|
|
58
|
+
this.currentWindowStart = Date.now()
|
|
59
|
+
if (this.rampTimer) clearTimeout(this.rampTimer)
|
|
60
|
+
this._scheduleNextRampStep()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Call when connection drops (optional). */
|
|
64
|
+
onDisconnect() {
|
|
65
|
+
// Keep throttle state — prevents rapid reconnect/disconnect from resetting too early
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Get current rate multiplier (1.0 = no throttle). */
|
|
69
|
+
getCurrentMultiplier() {
|
|
70
|
+
if (!this.config.enabled || !this.throttledSince) return 1.0
|
|
71
|
+
const elapsed = Date.now() - this.throttledSince
|
|
72
|
+
if (elapsed >= this.config.rampDurationMs) return 1.0
|
|
73
|
+
const progress = this.currentStep / this.config.rampSteps
|
|
74
|
+
return Math.min(
|
|
75
|
+
1.0,
|
|
76
|
+
this.config.initialRateMultiplier + (1.0 - this.config.initialRateMultiplier) * progress
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Check if a send is allowed. Returns { allowed, reason?, retryAfterMs? } */
|
|
81
|
+
beforeSend() {
|
|
82
|
+
if (!this.config.enabled || !this.throttledSince) return { allowed: true }
|
|
83
|
+
|
|
84
|
+
const now = Date.now()
|
|
85
|
+
const multiplier = this.getCurrentMultiplier()
|
|
86
|
+
|
|
87
|
+
if (multiplier >= 1.0) {
|
|
88
|
+
this.throttledSince = null
|
|
89
|
+
return { allowed: true }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (now - this.currentWindowStart >= WINDOW_DURATION_MS) {
|
|
93
|
+
this.sendsInCurrentWindow = 0
|
|
94
|
+
this.currentWindowStart = now
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const baselineRate = this.config.baselineRatePerMinute
|
|
98
|
+
? this.config.baselineRatePerMinute()
|
|
99
|
+
: 8
|
|
100
|
+
const allowedInWindow = Math.max(1, Math.floor(baselineRate * multiplier))
|
|
101
|
+
|
|
102
|
+
if (this.sendsInCurrentWindow >= allowedInWindow) {
|
|
103
|
+
const windowRemaining = WINDOW_DURATION_MS - (now - this.currentWindowStart)
|
|
104
|
+
return {
|
|
105
|
+
allowed: false,
|
|
106
|
+
reason: `Post-reconnect throttle: ${Math.floor(multiplier * 100)}% rate (${this.sendsInCurrentWindow}/${allowedInWindow} sends in window)`,
|
|
107
|
+
retryAfterMs: windowRemaining
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
this.sendsInCurrentWindow++
|
|
112
|
+
this.throttledSendCount++
|
|
113
|
+
return { allowed: true }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Get current stats */
|
|
117
|
+
getStats() {
|
|
118
|
+
const multiplier = this.getCurrentMultiplier()
|
|
119
|
+
const isThrottled = this.throttledSince !== null && multiplier < 1.0
|
|
120
|
+
return {
|
|
121
|
+
isThrottled,
|
|
122
|
+
currentMultiplier: multiplier,
|
|
123
|
+
throttledSinceMs: this.throttledSince,
|
|
124
|
+
remainingMs:
|
|
125
|
+
isThrottled && this.throttledSince
|
|
126
|
+
? Math.max(0, this.config.rampDurationMs - (Date.now() - this.throttledSince))
|
|
127
|
+
: 0,
|
|
128
|
+
throttledSendCount: this.throttledSendCount,
|
|
129
|
+
lifetimeReconnects: this.lifetimeReconnects
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Cleanup timers */
|
|
134
|
+
destroy() {
|
|
135
|
+
if (this.rampTimer) {
|
|
136
|
+
clearTimeout(this.rampTimer)
|
|
137
|
+
this.rampTimer = null
|
|
138
|
+
}
|
|
139
|
+
this.throttledSince = null
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
_scheduleNextRampStep() {
|
|
143
|
+
if (this.currentStep >= this.config.rampSteps) {
|
|
144
|
+
this.throttledSince = null
|
|
145
|
+
this.rampTimer = null
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
const stepDuration = this.config.rampDurationMs / this.config.rampSteps
|
|
149
|
+
this.rampTimer = setTimeout(() => {
|
|
150
|
+
this.currentStep++
|
|
151
|
+
this._scheduleNextRampStep()
|
|
152
|
+
}, stepDuration)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
exports.PostReconnectThrottle = PostReconnectThrottle
|
|
157
|
+
module.exports = { PostReconnectThrottle }
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true })
|
|
3
|
+
exports.reply_ratio_guard = void 0
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file reply-ratio-guard.js
|
|
7
|
+
* @description Tracks sent vs received ratio per contact. If a bot sends
|
|
8
|
+
* too many messages without getting replies (spam-like pattern), it
|
|
9
|
+
* triggers a cooldown and can suggest human-like auto-replies to keep
|
|
10
|
+
* the ratio healthy. Off by default.
|
|
11
|
+
* @author Denzy ZeroDay
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const default_config = {
|
|
15
|
+
enabled: false,
|
|
16
|
+
min_ratio: 0.1,
|
|
17
|
+
min_messages_before_enforce: 5,
|
|
18
|
+
inbound_auto_reply_probability: 0.25,
|
|
19
|
+
auto_reply_templates: ['👍', '👌', 'ok', 'noted', 'thanks', '🙏', 'got it'],
|
|
20
|
+
cooldown_hours_on_violation: 24,
|
|
21
|
+
scope: 'individual'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class reply_ratio_guard {
|
|
25
|
+
constructor(config = {}) {
|
|
26
|
+
this.config = { ...default_config, ...config }
|
|
27
|
+
this.contacts = new Map()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Check if message can be sent to this contact based on reply ratio. Call before sending. */
|
|
31
|
+
before_send(jid) {
|
|
32
|
+
if (!this.config.enabled) return { allowed: true }
|
|
33
|
+
if (this.is_group(jid) && this.config.scope === 'individual') return { allowed: true }
|
|
34
|
+
|
|
35
|
+
const record = this.contacts.get(jid)
|
|
36
|
+
if (!record) return { allowed: true }
|
|
37
|
+
|
|
38
|
+
if (record.cooledUntil && Date.now() < record.cooledUntil) {
|
|
39
|
+
const hours_left = Math.ceil((record.cooledUntil - Date.now()) / 3600000)
|
|
40
|
+
return {
|
|
41
|
+
allowed: false,
|
|
42
|
+
reason: `Reply ratio cooldown — ${record.sent} sent, ${record.received} received. Retry in ${hours_left}h`
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (record.sent >= this.config.min_messages_before_enforce) {
|
|
47
|
+
const ratio = record.sent === 0 ? 1 : record.received / record.sent
|
|
48
|
+
if (ratio < this.config.min_ratio) {
|
|
49
|
+
record.cooledUntil = Date.now() + this.config.cooldown_hours_on_violation * 3600000
|
|
50
|
+
return {
|
|
51
|
+
allowed: false,
|
|
52
|
+
reason: `Reply ratio too low (${(ratio * 100).toFixed(1)}% < ${(this.config.min_ratio * 100).toFixed(1)}%). Cooldown ${this.config.cooldown_hours_on_violation}h`
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return { allowed: true }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Record an outbound message sent to this contact */
|
|
61
|
+
record_sent(jid) {
|
|
62
|
+
if (!this.config.enabled) return
|
|
63
|
+
const record = this.contacts.get(jid) || { sent: 0, received: 0 }
|
|
64
|
+
record.sent++
|
|
65
|
+
this.contacts.set(jid, record)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Record an inbound message received from this contact */
|
|
69
|
+
record_received(jid) {
|
|
70
|
+
if (!this.config.enabled) return
|
|
71
|
+
const record = this.contacts.get(jid) || { sent: 0, received: 0 }
|
|
72
|
+
record.received++
|
|
73
|
+
delete record.cooledUntil
|
|
74
|
+
this.contacts.set(jid, record)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Suggest whether to send an auto-reply to this incoming message.
|
|
79
|
+
* Returns { should_reply: true, suggested_text: '👍' } if probability check passes.
|
|
80
|
+
* Caller is responsible for actually sending the message.
|
|
81
|
+
*/
|
|
82
|
+
suggest_reply(jid, _msg_text) {
|
|
83
|
+
if (!this.config.enabled) return { should_reply: false }
|
|
84
|
+
if (this.is_group(jid) && this.config.scope === 'individual') return { should_reply: false }
|
|
85
|
+
|
|
86
|
+
if (Math.random() < this.config.inbound_auto_reply_probability) {
|
|
87
|
+
const templates = this.config.auto_reply_templates
|
|
88
|
+
const suggested_text = templates[Math.floor(Math.random() * templates.length)]
|
|
89
|
+
return { should_reply: true, suggested_text }
|
|
90
|
+
}
|
|
91
|
+
return { should_reply: false }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Get statistics for all contacts and global metrics */
|
|
95
|
+
get_stats() {
|
|
96
|
+
const per_contact = Array.from(this.contacts.entries()).map(([jid, record]) => ({
|
|
97
|
+
jid,
|
|
98
|
+
sent: record.sent,
|
|
99
|
+
received: record.received,
|
|
100
|
+
ratio: record.sent === 0 ? 0 : record.received / record.sent,
|
|
101
|
+
cooledUntil: record.cooledUntil
|
|
102
|
+
}))
|
|
103
|
+
const global_sent = per_contact.reduce((sum, c) => sum + c.sent, 0)
|
|
104
|
+
const global_received = per_contact.reduce((sum, c) => sum + c.received, 0)
|
|
105
|
+
const global_ratio = global_sent === 0 ? 0 : global_received / global_sent
|
|
106
|
+
const contacts_on_cooldown = per_contact.filter(
|
|
107
|
+
(c) => c.cooledUntil && Date.now() < c.cooledUntil
|
|
108
|
+
).length
|
|
109
|
+
|
|
110
|
+
return { per_contact, global_sent, global_received, global_ratio, contacts_on_cooldown }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Reset all counters */
|
|
114
|
+
reset() {
|
|
115
|
+
this.contacts.clear()
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Export state for persistence */
|
|
119
|
+
export_state() {
|
|
120
|
+
return { contacts: Array.from(this.contacts.entries()) }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Restore state from persistence */
|
|
124
|
+
restore_state(state) {
|
|
125
|
+
if (state?.contacts && Array.isArray(state.contacts)) {
|
|
126
|
+
this.contacts = new Map(state.contacts)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Check if JID is a group */
|
|
131
|
+
is_group(jid) {
|
|
132
|
+
return jid.endsWith('@g.us')
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
exports.reply_ratio_guard = reply_ratio_guard
|
|
137
|
+
module.exports = { reply_ratio_guard }
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
const { createHmac } = require('crypto')
|
|
2
|
+
const { proto } = require('../../WAProto')
|
|
3
|
+
const { hkdf } = require('./crypto')
|
|
4
|
+
const reportingFields = [
|
|
5
|
+
{ f: 1 },
|
|
6
|
+
{
|
|
7
|
+
f: 3,
|
|
8
|
+
s: [
|
|
9
|
+
{ f: 2 },
|
|
10
|
+
{ f: 3 },
|
|
11
|
+
{ f: 8 },
|
|
12
|
+
{ f: 11 },
|
|
13
|
+
{ f: 17, s: [{ f: 21 }, { f: 22 }] },
|
|
14
|
+
{ f: 25 }
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
{ f: 4, s: [{ f: 1 }, { f: 16 }, { f: 17, s: [{ f: 21 }, { f: 22 }] }] },
|
|
18
|
+
{ f: 5, s: [{ f: 3 }, { f: 4 }, { f: 5 }, { f: 16 }, { f: 17, s: [{ f: 21 }, { f: 22 }] }] },
|
|
19
|
+
{ f: 6, s: [{ f: 1 }, { f: 17, s: [{ f: 21 }, { f: 22 }] }, { f: 30 }] },
|
|
20
|
+
{ f: 7, s: [{ f: 2 }, { f: 7 }, { f: 10 }, { f: 17, s: [{ f: 21 }, { f: 22 }] }, { f: 20 }] },
|
|
21
|
+
{ f: 8, s: [{ f: 2 }, { f: 7 }, { f: 9 }, { f: 17, s: [{ f: 21 }, { f: 22 }] }, { f: 21 }] },
|
|
22
|
+
{
|
|
23
|
+
f: 9,
|
|
24
|
+
s: [
|
|
25
|
+
{ f: 2 },
|
|
26
|
+
{ f: 6 },
|
|
27
|
+
{ f: 7 },
|
|
28
|
+
{ f: 13 },
|
|
29
|
+
{ f: 17, s: [{ f: 21 }, { f: 22 }] },
|
|
30
|
+
{ f: 20 }
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
{ f: 12, s: [{ f: 1 }, { f: 2 }, { f: 14, m: true }, { f: 15 }] },
|
|
34
|
+
{ f: 18, s: [{ f: 6 }, { f: 16 }, { f: 17, s: [{ f: 21 }, { f: 22 }] }] },
|
|
35
|
+
{ f: 26, s: [{ f: 4 }, { f: 5 }, { f: 8 }, { f: 13 }, { f: 17, s: [{ f: 21 }, { f: 22 }] }] },
|
|
36
|
+
{
|
|
37
|
+
f: 28,
|
|
38
|
+
s: [{ f: 1 }, { f: 2 }, { f: 4 }, { f: 5 }, { f: 6 }, { f: 7, s: [{ f: 21 }, { f: 22 }] }]
|
|
39
|
+
},
|
|
40
|
+
{ f: 37, s: [{ f: 1, m: true }] },
|
|
41
|
+
{
|
|
42
|
+
f: 49,
|
|
43
|
+
s: [
|
|
44
|
+
{ f: 2 },
|
|
45
|
+
{ f: 3, s: [{ f: 1 }, { f: 2 }] },
|
|
46
|
+
{ f: 5, s: [{ f: 21 }, { f: 22 }] },
|
|
47
|
+
{ f: 8, s: [{ f: 1 }, { f: 2 }] }
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
{ f: 53, s: [{ f: 1, m: true }] },
|
|
51
|
+
{ f: 55, s: [{ f: 1, m: true }] },
|
|
52
|
+
{ f: 58, s: [{ f: 1, m: true }] },
|
|
53
|
+
{ f: 59, s: [{ f: 1, m: true }] },
|
|
54
|
+
{
|
|
55
|
+
f: 60,
|
|
56
|
+
s: [
|
|
57
|
+
{ f: 2 },
|
|
58
|
+
{ f: 3, s: [{ f: 1 }, { f: 2 }] },
|
|
59
|
+
{ f: 5, s: [{ f: 21 }, { f: 22 }] },
|
|
60
|
+
{ f: 8, s: [{ f: 1 }, { f: 2 }] }
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
f: 64,
|
|
65
|
+
s: [
|
|
66
|
+
{ f: 2 },
|
|
67
|
+
{ f: 3, s: [{ f: 1 }, { f: 2 }] },
|
|
68
|
+
{ f: 5, s: [{ f: 21 }, { f: 22 }] },
|
|
69
|
+
{ f: 8, s: [{ f: 1 }, { f: 2 }] }
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
f: 66,
|
|
74
|
+
s: [
|
|
75
|
+
{ f: 2 },
|
|
76
|
+
{ f: 6 },
|
|
77
|
+
{ f: 7 },
|
|
78
|
+
{ f: 13 },
|
|
79
|
+
{ f: 17, s: [{ f: 21 }, { f: 22 }] },
|
|
80
|
+
{ f: 20 }
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
{ f: 74, s: [{ f: 1, m: true }] },
|
|
84
|
+
{ f: 87, s: [{ f: 1, m: true }] },
|
|
85
|
+
{ f: 88, s: [{ f: 1 }, { f: 2, s: [{ f: 1 }] }, { f: 3, s: [{ f: 21 }, { f: 22 }] }] },
|
|
86
|
+
{ f: 92, s: [{ f: 1, m: true }] },
|
|
87
|
+
{ f: 93, s: [{ f: 1, m: true }] },
|
|
88
|
+
{ f: 94, s: [{ f: 1, m: true }] }
|
|
89
|
+
]
|
|
90
|
+
const compileReportingFields = (fields) => {
|
|
91
|
+
const map = new Map()
|
|
92
|
+
for (const f of fields) {
|
|
93
|
+
map.set(f.f, {
|
|
94
|
+
m: f.m,
|
|
95
|
+
children: f.s ? compileReportingFields(f.s) : undefined
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
return map
|
|
99
|
+
}
|
|
100
|
+
const compiledReportingFields = compileReportingFields(reportingFields)
|
|
101
|
+
const EMPTY_MAP = new Map()
|
|
102
|
+
const ENC_SECRET_REPORT_TOKEN = 'Report Token'
|
|
103
|
+
const WIRE = {
|
|
104
|
+
VARINT: 0,
|
|
105
|
+
FIXED64: 1,
|
|
106
|
+
BYTES: 2,
|
|
107
|
+
FIXED32: 5
|
|
108
|
+
}
|
|
109
|
+
const shouldIncludeReportingToken = (message) =>
|
|
110
|
+
!message.reactionMessage &&
|
|
111
|
+
!message.encReactionMessage &&
|
|
112
|
+
!message.encEventResponseMessage &&
|
|
113
|
+
!message.pollUpdateMessage
|
|
114
|
+
const generateMsgSecretKey = (
|
|
115
|
+
modificationType,
|
|
116
|
+
origMsgId,
|
|
117
|
+
origMsgSender,
|
|
118
|
+
modificationSender,
|
|
119
|
+
origMsgSecret
|
|
120
|
+
) => {
|
|
121
|
+
const useCaseSecret = Buffer.concat([
|
|
122
|
+
Buffer.from(origMsgId, 'utf8'),
|
|
123
|
+
Buffer.from(origMsgSender, 'utf8'),
|
|
124
|
+
Buffer.from(modificationSender, 'utf8'),
|
|
125
|
+
Buffer.from(modificationType, 'utf8')
|
|
126
|
+
])
|
|
127
|
+
return hkdf(origMsgSecret, 32, { info: useCaseSecret.toString('latin1') })
|
|
128
|
+
}
|
|
129
|
+
const extractReportingTokenContent = (data, cfg) => {
|
|
130
|
+
const out = []
|
|
131
|
+
let i = 0
|
|
132
|
+
while (i < data.length) {
|
|
133
|
+
const tag = decodeVarint(data, i)
|
|
134
|
+
if (!tag.ok) {
|
|
135
|
+
return null
|
|
136
|
+
}
|
|
137
|
+
const fieldNum = tag.value >> 3
|
|
138
|
+
const wireType = tag.value & 0x7
|
|
139
|
+
const fieldStart = i
|
|
140
|
+
i += tag.bytes
|
|
141
|
+
const fieldCfg = cfg.get(fieldNum)
|
|
142
|
+
const pushSlice = (end) => {
|
|
143
|
+
if (end > data.length) {
|
|
144
|
+
return false
|
|
145
|
+
}
|
|
146
|
+
out.push({ num: fieldNum, bytes: data.subarray(fieldStart, end) })
|
|
147
|
+
i = end
|
|
148
|
+
return true
|
|
149
|
+
}
|
|
150
|
+
const skip = (end) => {
|
|
151
|
+
if (end > data.length) {
|
|
152
|
+
return false
|
|
153
|
+
}
|
|
154
|
+
i = end
|
|
155
|
+
return true
|
|
156
|
+
}
|
|
157
|
+
if (wireType === WIRE.VARINT) {
|
|
158
|
+
const v = decodeVarint(data, i)
|
|
159
|
+
if (!v.ok) {
|
|
160
|
+
return null
|
|
161
|
+
}
|
|
162
|
+
const end = i + v.bytes
|
|
163
|
+
if (!fieldCfg) {
|
|
164
|
+
if (!skip(end)) {
|
|
165
|
+
return null
|
|
166
|
+
}
|
|
167
|
+
continue
|
|
168
|
+
}
|
|
169
|
+
if (!pushSlice(end)) {
|
|
170
|
+
return null
|
|
171
|
+
}
|
|
172
|
+
continue
|
|
173
|
+
}
|
|
174
|
+
if (wireType === WIRE.FIXED64) {
|
|
175
|
+
const end = i + 8
|
|
176
|
+
if (!fieldCfg) {
|
|
177
|
+
if (!skip(end)) {
|
|
178
|
+
return null
|
|
179
|
+
}
|
|
180
|
+
continue
|
|
181
|
+
}
|
|
182
|
+
if (!pushSlice(end)) {
|
|
183
|
+
return null
|
|
184
|
+
}
|
|
185
|
+
continue
|
|
186
|
+
}
|
|
187
|
+
if (wireType === WIRE.FIXED32) {
|
|
188
|
+
const end = i + 4
|
|
189
|
+
if (!fieldCfg) {
|
|
190
|
+
if (!skip(end)) {
|
|
191
|
+
return null
|
|
192
|
+
}
|
|
193
|
+
continue
|
|
194
|
+
}
|
|
195
|
+
if (!pushSlice(end)) {
|
|
196
|
+
return null
|
|
197
|
+
}
|
|
198
|
+
continue
|
|
199
|
+
}
|
|
200
|
+
if (wireType === WIRE.BYTES) {
|
|
201
|
+
const len = decodeVarint(data, i)
|
|
202
|
+
if (!len.ok) {
|
|
203
|
+
return null
|
|
204
|
+
}
|
|
205
|
+
const valStart = i + len.bytes
|
|
206
|
+
const valEnd = valStart + len.value
|
|
207
|
+
if (valEnd > data.length) {
|
|
208
|
+
return null
|
|
209
|
+
}
|
|
210
|
+
if (!fieldCfg) {
|
|
211
|
+
i = valEnd
|
|
212
|
+
continue
|
|
213
|
+
}
|
|
214
|
+
if (fieldCfg.m || fieldCfg.children) {
|
|
215
|
+
const sub = extractReportingTokenContent(
|
|
216
|
+
data.subarray(valStart, valEnd),
|
|
217
|
+
fieldCfg.children ?? EMPTY_MAP
|
|
218
|
+
)
|
|
219
|
+
if (sub === null) {
|
|
220
|
+
return null
|
|
221
|
+
}
|
|
222
|
+
if (sub.length > 0) {
|
|
223
|
+
const newTag = encodeVarint(tag.value)
|
|
224
|
+
const newLen = encodeVarint(sub.length)
|
|
225
|
+
out.push({
|
|
226
|
+
num: fieldNum,
|
|
227
|
+
bytes: Buffer.concat([newTag, newLen, sub])
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
i = valEnd
|
|
231
|
+
continue
|
|
232
|
+
}
|
|
233
|
+
out.push({ num: fieldNum, bytes: data.subarray(fieldStart, valEnd) })
|
|
234
|
+
i = valEnd
|
|
235
|
+
continue
|
|
236
|
+
}
|
|
237
|
+
return null
|
|
238
|
+
}
|
|
239
|
+
if (out.length === 0) {
|
|
240
|
+
return Buffer.alloc(0)
|
|
241
|
+
}
|
|
242
|
+
out.sort((a, b) => a.num - b.num)
|
|
243
|
+
return Buffer.concat(out.map((f) => f.bytes))
|
|
244
|
+
}
|
|
245
|
+
const decodeVarint = (buffer, offset) => {
|
|
246
|
+
let value = 0
|
|
247
|
+
let bytes = 0
|
|
248
|
+
let shift = 0
|
|
249
|
+
while (offset + bytes < buffer.length) {
|
|
250
|
+
const current = buffer[offset + bytes]
|
|
251
|
+
value |= (current & 0x7f) << shift
|
|
252
|
+
bytes++
|
|
253
|
+
if ((current & 0x80) === 0) {
|
|
254
|
+
return { value, bytes, ok: true }
|
|
255
|
+
}
|
|
256
|
+
shift += 7
|
|
257
|
+
if (shift > 35) {
|
|
258
|
+
return { value: 0, bytes: 0, ok: false }
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return { value: 0, bytes: 0, ok: false }
|
|
262
|
+
}
|
|
263
|
+
const encodeVarint = (value) => {
|
|
264
|
+
const parts = []
|
|
265
|
+
let remaining = value >>> 0
|
|
266
|
+
while (remaining > 0x7f) {
|
|
267
|
+
parts.push((remaining & 0x7f) | 0x80)
|
|
268
|
+
remaining >>>= 7
|
|
269
|
+
}
|
|
270
|
+
parts.push(remaining)
|
|
271
|
+
return Buffer.from(parts)
|
|
272
|
+
}
|
|
273
|
+
const getMessageReportingToken = async (msgProtobuf, message, key) => {
|
|
274
|
+
const msgSecret = message.messageContextInfo?.messageSecret
|
|
275
|
+
if (!msgSecret || !key.id) {
|
|
276
|
+
return null
|
|
277
|
+
}
|
|
278
|
+
const from = key.fromMe ? key.remoteJid : key.participant || key.remoteJid
|
|
279
|
+
const to = key.fromMe ? key.participant || key.remoteJid : key.remoteJid
|
|
280
|
+
const reportingSecret = generateMsgSecretKey(
|
|
281
|
+
ENC_SECRET_REPORT_TOKEN,
|
|
282
|
+
key.id,
|
|
283
|
+
from,
|
|
284
|
+
to,
|
|
285
|
+
msgSecret
|
|
286
|
+
)
|
|
287
|
+
const content = extractReportingTokenContent(msgProtobuf, compiledReportingFields)
|
|
288
|
+
if (!content || content.length === 0) {
|
|
289
|
+
return null
|
|
290
|
+
}
|
|
291
|
+
const reportingToken = createHmac('sha256', reportingSecret)
|
|
292
|
+
.update(content)
|
|
293
|
+
.digest()
|
|
294
|
+
.subarray(0, 16)
|
|
295
|
+
return {
|
|
296
|
+
tag: 'reporting',
|
|
297
|
+
attrs: {},
|
|
298
|
+
content: [
|
|
299
|
+
{
|
|
300
|
+
tag: 'reporting_token',
|
|
301
|
+
attrs: { v: '2' },
|
|
302
|
+
content: reportingToken
|
|
303
|
+
}
|
|
304
|
+
]
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
//# sourceMappingURL=reporting-utils.js.map
|