@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,188 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true })
|
|
3
|
+
exports.getNextPreKeysNode =
|
|
4
|
+
exports.getNextPreKeys =
|
|
5
|
+
exports.extractDeviceJids =
|
|
6
|
+
exports.parseAndInjectE2ESessions =
|
|
7
|
+
exports.xmppPreKey =
|
|
8
|
+
exports.xmppSignedPreKey =
|
|
9
|
+
exports.generateOrGetPreKeys =
|
|
10
|
+
exports.getPreKeys =
|
|
11
|
+
exports.createSignalIdentity =
|
|
12
|
+
void 0
|
|
13
|
+
const Defaults_1 = require('../Defaults')
|
|
14
|
+
const WABinary_1 = require('../WABinary')
|
|
15
|
+
const crypto_1 = require('./crypto')
|
|
16
|
+
const lodash_1 = require('lodash')
|
|
17
|
+
const generics_1 = require('./generics')
|
|
18
|
+
const createSignalIdentity = (wid, accountSignatureKey) => {
|
|
19
|
+
return {
|
|
20
|
+
identifier: { name: wid, deviceId: 0 },
|
|
21
|
+
identifierKey: (0, crypto_1.generateSignalPubKey)(accountSignatureKey)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.createSignalIdentity = createSignalIdentity
|
|
25
|
+
const getPreKeys = async ({ get }, min, limit) => {
|
|
26
|
+
const idList = []
|
|
27
|
+
for (let id = min; id < limit; id++) {
|
|
28
|
+
idList.push(id.toString())
|
|
29
|
+
}
|
|
30
|
+
return get('pre-key', idList)
|
|
31
|
+
}
|
|
32
|
+
exports.getPreKeys = getPreKeys
|
|
33
|
+
const generateOrGetPreKeys = (creds, range) => {
|
|
34
|
+
const avaliable = creds.nextPreKeyId - creds.firstUnuploadedPreKeyId
|
|
35
|
+
const remaining = range - avaliable
|
|
36
|
+
const lastPreKeyId = creds.nextPreKeyId + remaining - 1
|
|
37
|
+
const newPreKeys = {}
|
|
38
|
+
if (remaining > 0) {
|
|
39
|
+
for (let i = creds.nextPreKeyId; i <= lastPreKeyId; i++) {
|
|
40
|
+
newPreKeys[i] = crypto_1.Curve.generateKeyPair()
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
newPreKeys,
|
|
45
|
+
lastPreKeyId,
|
|
46
|
+
preKeysRange: [creds.firstUnuploadedPreKeyId, range]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.generateOrGetPreKeys = generateOrGetPreKeys
|
|
50
|
+
const xmppSignedPreKey = (key) => ({
|
|
51
|
+
tag: 'skey',
|
|
52
|
+
attrs: {},
|
|
53
|
+
content: [
|
|
54
|
+
{ tag: 'id', attrs: {}, content: (0, generics_1.encodeBigEndian)(key.keyId, 3) },
|
|
55
|
+
{ tag: 'value', attrs: {}, content: key.keyPair.public },
|
|
56
|
+
{ tag: 'signature', attrs: {}, content: key.signature }
|
|
57
|
+
]
|
|
58
|
+
})
|
|
59
|
+
exports.xmppSignedPreKey = xmppSignedPreKey
|
|
60
|
+
const xmppPreKey = (pair, id) => ({
|
|
61
|
+
tag: 'key',
|
|
62
|
+
attrs: {},
|
|
63
|
+
content: [
|
|
64
|
+
{ tag: 'id', attrs: {}, content: (0, generics_1.encodeBigEndian)(id, 3) },
|
|
65
|
+
{ tag: 'value', attrs: {}, content: pair.public }
|
|
66
|
+
]
|
|
67
|
+
})
|
|
68
|
+
exports.xmppPreKey = xmppPreKey
|
|
69
|
+
const parseAndInjectE2ESessions = async (node, repository) => {
|
|
70
|
+
const extractKey = (key) =>
|
|
71
|
+
key
|
|
72
|
+
? {
|
|
73
|
+
keyId: WABinary_1.getBinaryNodeChildUInt(key, 'id', 3),
|
|
74
|
+
publicKey: crypto_1.generateSignalPubKey(
|
|
75
|
+
WABinary_1.getBinaryNodeChildBuffer(key, 'value')
|
|
76
|
+
),
|
|
77
|
+
signature: WABinary_1.getBinaryNodeChildBuffer(key, 'signature')
|
|
78
|
+
}
|
|
79
|
+
: undefined
|
|
80
|
+
const nodes = WABinary_1.getBinaryNodeChildren(
|
|
81
|
+
WABinary_1.getBinaryNodeChild(node, 'list'),
|
|
82
|
+
'user'
|
|
83
|
+
)
|
|
84
|
+
for (const node of nodes) {
|
|
85
|
+
WABinary_1.assertNodeErrorFree(node)
|
|
86
|
+
}
|
|
87
|
+
// Most of the work in repository.injectE2ESession is CPU intensive, not IO
|
|
88
|
+
// So Promise.all doesn't really help here,
|
|
89
|
+
// but blocks even loop if we're using it inside keys.transaction, and it makes it "sync" actually
|
|
90
|
+
// This way we chunk it in smaller parts and between those parts we can yield to the event loop
|
|
91
|
+
// It's rare case when you need to E2E sessions for so many users, but it's possible
|
|
92
|
+
const chunkSize = 100
|
|
93
|
+
const chunks = lodash_1.chunk(nodes, chunkSize)
|
|
94
|
+
for (const nodesChunk of chunks) {
|
|
95
|
+
await Promise.all(
|
|
96
|
+
nodesChunk.map(async (node) => {
|
|
97
|
+
const signedKey = WABinary_1.getBinaryNodeChild(node, 'skey')
|
|
98
|
+
const key = WABinary_1.getBinaryNodeChild(node, 'key')
|
|
99
|
+
const identity = WABinary_1.getBinaryNodeChildBuffer(node, 'identity')
|
|
100
|
+
const jid = node.attrs.jid
|
|
101
|
+
const registrationId = WABinary_1.getBinaryNodeChildUInt(node, 'registration', 4)
|
|
102
|
+
await repository.injectE2ESession({
|
|
103
|
+
jid,
|
|
104
|
+
session: {
|
|
105
|
+
registrationId: registrationId,
|
|
106
|
+
identityKey: crypto_1.generateSignalPubKey(identity),
|
|
107
|
+
signedPreKey: extractKey(signedKey),
|
|
108
|
+
preKey: extractKey(key)
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
exports.parseAndInjectE2ESessions = parseAndInjectE2ESessions
|
|
116
|
+
const extractDeviceJids = (result, myJid, excludeZeroDevices) => {
|
|
117
|
+
const { user: myUser, device: myDevice } = WABinary_1.jidDecode(myJid)
|
|
118
|
+
const extracted = []
|
|
119
|
+
for (const userResult of result) {
|
|
120
|
+
const { devices, id } = userResult
|
|
121
|
+
const { user } = WABinary_1.jidDecode(id)
|
|
122
|
+
const deviceList = devices?.deviceList
|
|
123
|
+
if (Array.isArray(deviceList)) {
|
|
124
|
+
for (const { id: device, keyIndex } of deviceList) {
|
|
125
|
+
if (
|
|
126
|
+
(!excludeZeroDevices || device !== 0) && // if zero devices are not-excluded, or device is non zero
|
|
127
|
+
(myUser !== user || myDevice !== device) && // either different user or if me user, not this device
|
|
128
|
+
(device === 0 || !!keyIndex) // ensure that "key-index" is specified for "non-zero" devices, produces a bad req otherwise
|
|
129
|
+
) {
|
|
130
|
+
extracted.push({ user, device })
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return extracted
|
|
136
|
+
}
|
|
137
|
+
exports.extractDeviceJids = extractDeviceJids
|
|
138
|
+
/**
|
|
139
|
+
* get the next N keys for upload or processing
|
|
140
|
+
* @param count number of pre-keys to get or generate
|
|
141
|
+
*/
|
|
142
|
+
const getNextPreKeys = async ({ creds, keys }, count) => {
|
|
143
|
+
const { newPreKeys, lastPreKeyId, preKeysRange } = (0, exports.generateOrGetPreKeys)(
|
|
144
|
+
creds,
|
|
145
|
+
count
|
|
146
|
+
)
|
|
147
|
+
const update = {
|
|
148
|
+
nextPreKeyId: Math.max(lastPreKeyId + 1, creds.nextPreKeyId),
|
|
149
|
+
firstUnuploadedPreKeyId: Math.max(creds.firstUnuploadedPreKeyId, lastPreKeyId + 1)
|
|
150
|
+
}
|
|
151
|
+
await keys.set({ 'pre-key': newPreKeys })
|
|
152
|
+
const preKeys = await (0, exports.getPreKeys)(
|
|
153
|
+
keys,
|
|
154
|
+
preKeysRange[0],
|
|
155
|
+
preKeysRange[0] + preKeysRange[1]
|
|
156
|
+
)
|
|
157
|
+
return { update, preKeys }
|
|
158
|
+
}
|
|
159
|
+
exports.getNextPreKeys = getNextPreKeys
|
|
160
|
+
const getNextPreKeysNode = async (state, count) => {
|
|
161
|
+
const { creds } = state
|
|
162
|
+
const { update, preKeys } = await (0, exports.getNextPreKeys)(state, count)
|
|
163
|
+
const node = {
|
|
164
|
+
tag: 'iq',
|
|
165
|
+
attrs: {
|
|
166
|
+
xmlns: 'encrypt',
|
|
167
|
+
type: 'set',
|
|
168
|
+
to: WABinary_1.S_WHATSAPP_NET
|
|
169
|
+
},
|
|
170
|
+
content: [
|
|
171
|
+
{
|
|
172
|
+
tag: 'registration',
|
|
173
|
+
attrs: {},
|
|
174
|
+
content: (0, generics_1.encodeBigEndian)(creds.registrationId)
|
|
175
|
+
},
|
|
176
|
+
{ tag: 'type', attrs: {}, content: Defaults_1.KEY_BUNDLE_TYPE },
|
|
177
|
+
{ tag: 'identity', attrs: {}, content: creds.signedIdentityKey.public },
|
|
178
|
+
{
|
|
179
|
+
tag: 'list',
|
|
180
|
+
attrs: {},
|
|
181
|
+
content: Object.keys(preKeys).map((k) => (0, exports.xmppPreKey)(preKeys[+k], +k))
|
|
182
|
+
},
|
|
183
|
+
(0, exports.xmppSignedPreKey)(creds.signedPreKey)
|
|
184
|
+
]
|
|
185
|
+
}
|
|
186
|
+
return { update, node }
|
|
187
|
+
}
|
|
188
|
+
exports.getNextPreKeysNode = getNextPreKeysNode
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds an ACK stanza for a received node.
|
|
3
|
+
* Pure function -- no I/O, no side effects.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors WhatsApp Web's ACK construction:
|
|
6
|
+
* - WAWebHandleMsgSendAck.sendAck / sendNack
|
|
7
|
+
* - WAWebCreateNackFromStanza.createNackFromStanza
|
|
8
|
+
*/
|
|
9
|
+
export function buildAckStanza(node, errorCode, meId) {
|
|
10
|
+
const { tag, attrs } = node
|
|
11
|
+
const stanza = {
|
|
12
|
+
tag: 'ack',
|
|
13
|
+
attrs: {
|
|
14
|
+
id: attrs.id,
|
|
15
|
+
to: attrs.from,
|
|
16
|
+
class: tag
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (errorCode) {
|
|
20
|
+
stanza.attrs.error = errorCode.toString()
|
|
21
|
+
}
|
|
22
|
+
if (attrs.participant) {
|
|
23
|
+
stanza.attrs.participant = attrs.participant
|
|
24
|
+
}
|
|
25
|
+
if (attrs.recipient) {
|
|
26
|
+
stanza.attrs.recipient = attrs.recipient
|
|
27
|
+
}
|
|
28
|
+
// WA Web always includes type when present: `n.type || DROP_ATTR`
|
|
29
|
+
if (attrs.type) {
|
|
30
|
+
stanza.attrs.type = attrs.type
|
|
31
|
+
}
|
|
32
|
+
// WA Web WAWebHandleMsgSendAck.sendAck/sendNack always include `from` for message-class ACKs
|
|
33
|
+
if (tag === 'message' && meId) {
|
|
34
|
+
stanza.attrs.from = meId
|
|
35
|
+
}
|
|
36
|
+
return stanza
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=stanza-ack.js.map
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true })
|
|
3
|
+
exports.state_manager = void 0
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file state-manager.js
|
|
7
|
+
* @description Debounced file-based persistence for anti-ban state
|
|
8
|
+
* (known chats, warmup progress, health stats, etc). Writes are
|
|
9
|
+
* debounced 5s after each send to avoid disk I/O overhead, but can
|
|
10
|
+
* be flushed immediately after critical health events (ban/restriction).
|
|
11
|
+
* @author Denzy ZeroDay
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs')
|
|
15
|
+
|
|
16
|
+
const KNOWN_CHATS_MAX = 1000
|
|
17
|
+
const DEBOUNCE_MS = 5000
|
|
18
|
+
|
|
19
|
+
class state_manager {
|
|
20
|
+
constructor(file_path) {
|
|
21
|
+
this.path = file_path
|
|
22
|
+
this.debounce_timer = null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Load persisted state from disk. Returns null if missing/corrupt/wrong version. */
|
|
26
|
+
load() {
|
|
27
|
+
try {
|
|
28
|
+
const raw = fs.readFileSync(this.path, 'utf-8')
|
|
29
|
+
const parsed = JSON.parse(raw)
|
|
30
|
+
if (parsed.version !== 3) {
|
|
31
|
+
process.stderr.write('[bilis-antiban] WARN: corrupt state file or version mismatch, starting fresh\n')
|
|
32
|
+
return null
|
|
33
|
+
}
|
|
34
|
+
return parsed
|
|
35
|
+
} catch {
|
|
36
|
+
if (fs.existsSync(this.path)) {
|
|
37
|
+
process.stderr.write('[bilis-antiban] WARN: corrupt state file, starting fresh\n')
|
|
38
|
+
}
|
|
39
|
+
return null
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Debounced save — call after every send (5s delay before actual write) */
|
|
44
|
+
save_debounced(state) {
|
|
45
|
+
if (this.debounce_timer) clearTimeout(this.debounce_timer)
|
|
46
|
+
this.debounce_timer = setTimeout(() => {
|
|
47
|
+
this.write_file(state)
|
|
48
|
+
this.debounce_timer = null
|
|
49
|
+
}, DEBOUNCE_MS)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Immediate save — call after critical health events (ban/restriction) */
|
|
53
|
+
save_immediate(state) {
|
|
54
|
+
if (this.debounce_timer) {
|
|
55
|
+
clearTimeout(this.debounce_timer)
|
|
56
|
+
this.debounce_timer = null
|
|
57
|
+
}
|
|
58
|
+
this.write_file(state)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Flush/cancel pending debounced write (for tests and process exit) */
|
|
62
|
+
flush() {
|
|
63
|
+
if (this.debounce_timer) {
|
|
64
|
+
clearTimeout(this.debounce_timer)
|
|
65
|
+
this.debounce_timer = null
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
destroy() {
|
|
70
|
+
this.flush()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
write_file(state) {
|
|
74
|
+
const to_save = {
|
|
75
|
+
...state,
|
|
76
|
+
savedAt: Date.now(),
|
|
77
|
+
// LRU eviction: keep last KNOWN_CHATS_MAX entries
|
|
78
|
+
knownChats: state.knownChats.length > KNOWN_CHATS_MAX
|
|
79
|
+
? state.knownChats.slice(-KNOWN_CHATS_MAX)
|
|
80
|
+
: state.knownChats
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
fs.writeFileSync(this.path, JSON.stringify(to_save, null, 2), 'utf-8')
|
|
84
|
+
} catch (err) {
|
|
85
|
+
process.stderr.write(`[bilis-antiban] WARN: failed to write state to ${this.path}: ${err}\n`)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
exports.state_manager = state_manager
|
|
91
|
+
module.exports = { state_manager }
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { AnyMessageContent } from '../Types'
|
|
2
|
+
|
|
3
|
+
export type StatusType = 'text' | 'image' | 'video' | 'audio'
|
|
4
|
+
|
|
5
|
+
export interface TextStatusOptions {
|
|
6
|
+
text: string
|
|
7
|
+
backgroundColor?: string
|
|
8
|
+
font?: number
|
|
9
|
+
textColor?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface MediaStatusOptions {
|
|
13
|
+
media: Buffer | string
|
|
14
|
+
caption?: string
|
|
15
|
+
viewOnce?: boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface StatusPostResult {
|
|
19
|
+
messageId: string
|
|
20
|
+
timestamp: Date
|
|
21
|
+
type: StatusType
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Pre-defined background colors for text status */
|
|
25
|
+
export declare const STATUS_BACKGROUNDS: {
|
|
26
|
+
solid: {
|
|
27
|
+
green: string
|
|
28
|
+
blue: string
|
|
29
|
+
purple: string
|
|
30
|
+
red: string
|
|
31
|
+
orange: string
|
|
32
|
+
yellow: string
|
|
33
|
+
pink: string
|
|
34
|
+
teal: string
|
|
35
|
+
gray: string
|
|
36
|
+
black: string
|
|
37
|
+
white: string
|
|
38
|
+
}
|
|
39
|
+
gradient: {
|
|
40
|
+
sunset: string[]
|
|
41
|
+
ocean: string[]
|
|
42
|
+
forest: string[]
|
|
43
|
+
purple: string[]
|
|
44
|
+
midnight: string[]
|
|
45
|
+
aurora: string[]
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Font types for text status (0-9) */
|
|
50
|
+
export declare const STATUS_FONTS: {
|
|
51
|
+
readonly SANS_SERIF: 0
|
|
52
|
+
readonly SERIF: 1
|
|
53
|
+
readonly NORICAN: 2
|
|
54
|
+
readonly BRYNDAN: 3
|
|
55
|
+
readonly BEBASNEUE: 4
|
|
56
|
+
readonly OSWALD: 5
|
|
57
|
+
readonly DAMION: 6
|
|
58
|
+
readonly DANCING: 7
|
|
59
|
+
readonly COMFORTAA: 8
|
|
60
|
+
readonly EXOTWO: 9
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export declare const generateStatusMessageId: () => string
|
|
64
|
+
|
|
65
|
+
export declare const createTextStatus: (options: TextStatusOptions) => AnyMessageContent
|
|
66
|
+
export declare const createImageStatus: (media: Buffer | string, options?: { caption?: string; viewOnce?: boolean }) => AnyMessageContent
|
|
67
|
+
export declare const createVideoStatus: (media: Buffer | string, options?: { caption?: string; viewOnce?: boolean; gifPlayback?: boolean }) => AnyMessageContent
|
|
68
|
+
export declare const createAudioStatus: (media: Buffer | string, options?: { ptt?: boolean }) => AnyMessageContent
|
|
69
|
+
|
|
70
|
+
export declare const STATUS_BROADCAST_JID = "status@broadcast"
|
|
71
|
+
export declare const getStatusJid: () => string
|
|
72
|
+
|
|
73
|
+
export declare const StatusHelper: {
|
|
74
|
+
text: (text: string, backgroundColor?: string) => AnyMessageContent
|
|
75
|
+
image: (buffer: Buffer, caption?: string) => AnyMessageContent
|
|
76
|
+
imageUrl: (url: string, caption?: string) => AnyMessageContent
|
|
77
|
+
video: (buffer: Buffer, caption?: string) => AnyMessageContent
|
|
78
|
+
videoUrl: (url: string, caption?: string) => AnyMessageContent
|
|
79
|
+
gif: (buffer: Buffer, caption?: string) => AnyMessageContent
|
|
80
|
+
voiceNote: (buffer: Buffer) => AnyMessageContent
|
|
81
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true })
|
|
4
|
+
exports.StatusHelper =
|
|
5
|
+
exports.getStatusJid =
|
|
6
|
+
exports.STATUS_BROADCAST_JID =
|
|
7
|
+
exports.createAudioStatus =
|
|
8
|
+
exports.createVideoStatus =
|
|
9
|
+
exports.createImageStatus =
|
|
10
|
+
exports.createTextStatus =
|
|
11
|
+
exports.generateStatusMessageId =
|
|
12
|
+
exports.STATUS_FONTS =
|
|
13
|
+
exports.STATUS_BACKGROUNDS =
|
|
14
|
+
void 0
|
|
15
|
+
|
|
16
|
+
const crypto_1 = require('crypto')
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Pre-defined background colors for text status
|
|
20
|
+
*/
|
|
21
|
+
exports.STATUS_BACKGROUNDS = {
|
|
22
|
+
solid: {
|
|
23
|
+
green: '#25D366',
|
|
24
|
+
blue: '#34B7F1',
|
|
25
|
+
purple: '#8B5CF6',
|
|
26
|
+
red: '#EF4444',
|
|
27
|
+
orange: '#F97316',
|
|
28
|
+
yellow: '#EAB308',
|
|
29
|
+
pink: '#EC4899',
|
|
30
|
+
teal: '#14B8A6',
|
|
31
|
+
gray: '#6B7280',
|
|
32
|
+
black: '#000000',
|
|
33
|
+
white: '#FFFFFF'
|
|
34
|
+
},
|
|
35
|
+
gradient: {
|
|
36
|
+
sunset: ['#F97316', '#EF4444'],
|
|
37
|
+
ocean: ['#3B82F6', '#06B6D4'],
|
|
38
|
+
forest: ['#22C55E', '#10B981'],
|
|
39
|
+
purple: ['#8B5CF6', '#EC4899'],
|
|
40
|
+
midnight: ['#1E3A8A', '#4C1D95'],
|
|
41
|
+
aurora: ['#06B6D4', '#8B5CF6', '#EC4899']
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Font types for text status (0-9)
|
|
47
|
+
*/
|
|
48
|
+
exports.STATUS_FONTS = {
|
|
49
|
+
SANS_SERIF: 0,
|
|
50
|
+
SERIF: 1,
|
|
51
|
+
NORICAN: 2,
|
|
52
|
+
BRYNDAN: 3,
|
|
53
|
+
BEBASNEUE: 4,
|
|
54
|
+
OSWALD: 5,
|
|
55
|
+
DAMION: 6,
|
|
56
|
+
DANCING: 7,
|
|
57
|
+
COMFORTAA: 8,
|
|
58
|
+
EXOTWO: 9
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Generate a typical WhatsApp status message ID starting with 3EB0
|
|
63
|
+
*/
|
|
64
|
+
const generateStatusMessageId = () => {
|
|
65
|
+
return `3EB0${(0, crypto_1.randomBytes)(16).toString('hex').toUpperCase()}`
|
|
66
|
+
}
|
|
67
|
+
exports.generateStatusMessageId = generateStatusMessageId
|
|
68
|
+
|
|
69
|
+
/** Create text status content */
|
|
70
|
+
const createTextStatus = (options) => {
|
|
71
|
+
const backgroundColor = options.backgroundColor || exports.STATUS_BACKGROUNDS.solid.green
|
|
72
|
+
const font = options.font ?? exports.STATUS_FONTS.SANS_SERIF
|
|
73
|
+
const textColor = options.textColor || '#FFFFFF'
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
text: options.text,
|
|
77
|
+
backgroundColor,
|
|
78
|
+
font,
|
|
79
|
+
textColor,
|
|
80
|
+
contextInfo: {
|
|
81
|
+
mentionedJid: options.mentions || [],
|
|
82
|
+
isForwarded: false
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.createTextStatus = createTextStatus
|
|
87
|
+
|
|
88
|
+
/** Create image status content */
|
|
89
|
+
const createImageStatus = (media, options) => {
|
|
90
|
+
return {
|
|
91
|
+
image: typeof media === 'string' ? { url: media } : media,
|
|
92
|
+
caption: options?.caption || ''
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.createImageStatus = createImageStatus
|
|
96
|
+
|
|
97
|
+
/** Create video status content */
|
|
98
|
+
const createVideoStatus = (media, options) => {
|
|
99
|
+
return {
|
|
100
|
+
video: typeof media === 'string' ? { url: media } : media,
|
|
101
|
+
caption: options?.caption || '',
|
|
102
|
+
gifPlayback: options?.gifPlayback || false
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.createVideoStatus = createVideoStatus
|
|
106
|
+
|
|
107
|
+
/** Create audio/PTT status content */
|
|
108
|
+
const createAudioStatus = (media, options) => {
|
|
109
|
+
return {
|
|
110
|
+
audio: typeof media === 'string' ? { url: media } : media,
|
|
111
|
+
ptt: true, // Status audio is always PTT style
|
|
112
|
+
mimetype: 'audio/ogg; codecs=opus',
|
|
113
|
+
waveform: options?.waveform
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
exports.createAudioStatus = createAudioStatus
|
|
117
|
+
|
|
118
|
+
/** Status target JID */
|
|
119
|
+
exports.STATUS_BROADCAST_JID = 'status@broadcast'
|
|
120
|
+
|
|
121
|
+
/** Helper to return the broadcast JID */
|
|
122
|
+
const getStatusJid = () => exports.STATUS_BROADCAST_JID
|
|
123
|
+
exports.getStatusJid = getStatusJid
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* High-level builder to create and send status messages
|
|
127
|
+
*/
|
|
128
|
+
exports.StatusHelper = {
|
|
129
|
+
text: (text, backgroundColor, font) =>
|
|
130
|
+
(0, exports.createTextStatus)({ text, backgroundColor, font }),
|
|
131
|
+
image: (buffer, caption) => (0, exports.createImageStatus)(buffer, { caption }),
|
|
132
|
+
imageUrl: (url, caption) => (0, exports.createImageStatus)(url, { caption }),
|
|
133
|
+
video: (buffer, caption) => (0, exports.createVideoStatus)(buffer, { caption }),
|
|
134
|
+
videoUrl: (url, caption) => (0, exports.createVideoStatus)(url, { caption }),
|
|
135
|
+
gif: (buffer, caption) =>
|
|
136
|
+
(0, exports.createVideoStatus)(buffer, { caption, gifPlayback: true }),
|
|
137
|
+
voiceNote: (buffer) => (0, exports.createAudioStatus)(buffer),
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Helper to send status to all contacts, specific groups, or individuals.
|
|
141
|
+
* @param {import('../Socket').Socket} sock
|
|
142
|
+
* @param {AnyMessageContent} content
|
|
143
|
+
* @param {string[]} jidList List of JIDs (individuals or groups) who should see this status
|
|
144
|
+
*/
|
|
145
|
+
send: async (sock, content, jidList = []) => {
|
|
146
|
+
if (!jidList || jidList.length === 0) {
|
|
147
|
+
console.warn(
|
|
148
|
+
'StatusHelper: jidList is empty. Status will potentially be visible to all contacts.'
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const groups = jidList.filter((jid) => jid && jid.endsWith('@g.us'))
|
|
153
|
+
const individuals = jidList.filter(
|
|
154
|
+
(jid) => jid && (jid.endsWith('@s.whatsapp.net') || jid.endsWith('@lid'))
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
let lastResult
|
|
158
|
+
|
|
159
|
+
// Handle Group Status
|
|
160
|
+
// Note: In WhatsApp, a group status is a message sent to the group JID with isGroupStatus: true.
|
|
161
|
+
// It appears as a message in the chat but also as a status update for members.
|
|
162
|
+
if (groups.length > 0) {
|
|
163
|
+
const groupContent = {
|
|
164
|
+
...content,
|
|
165
|
+
groupStatus: true
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
for (const groupJid of groups) {
|
|
169
|
+
lastResult = await sock.sendMessage(groupJid, groupContent, {
|
|
170
|
+
messageId: (0, exports.generateStatusMessageId)()
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Handle Personal status@broadcast
|
|
176
|
+
// Send to broadcast only if individuals are specified or if targeting all contacts (empty jidList)
|
|
177
|
+
if (individuals.length > 0 || jidList.length === 0) {
|
|
178
|
+
const result = await sock.sendMessage(exports.STATUS_BROADCAST_JID, content, {
|
|
179
|
+
statusJidList: individuals.length > 0 ? individuals : undefined,
|
|
180
|
+
messageId: (0, exports.generateStatusMessageId)()
|
|
181
|
+
})
|
|
182
|
+
if (!lastResult) lastResult = result
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return lastResult
|
|
186
|
+
}
|
|
187
|
+
}
|