@fer2809fl/baileys 7.0.4 → 7.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +347 -50
- package/lib/Defaults/index.js +1 -1
- package/lib/Modded/message_builder.js +2356 -0
- package/lib/Socket/chats.d.ts +14 -0
- package/lib/Socket/chats.js +48 -0
- package/lib/Socket/index.d.ts +17 -0
- package/lib/Socket/messages-send.d.ts +18 -0
- package/lib/Socket/messages-send.js +50 -2
- package/lib/Utils/anti-ban.d.ts +41 -0
- package/lib/Utils/anti-ban.js +182 -0
- package/lib/Utils/banner.d.ts +8 -0
- package/lib/Utils/banner.js +76 -0
- package/lib/Utils/bot-utils.d.ts +57 -0
- package/lib/Utils/bot-utils.js +241 -0
- package/lib/Utils/enhanced-cache.d.ts +40 -0
- package/lib/Utils/enhanced-cache.js +242 -0
- package/lib/Utils/enhanced-logger.d.ts +41 -0
- package/lib/Utils/enhanced-logger.js +185 -0
- package/lib/Utils/index.d.ts +14 -0
- package/lib/Utils/index.js +13 -0
- package/lib/Utils/lid-utils.d.ts +139 -0
- package/lib/Utils/lid-utils.js +503 -0
- package/lib/Utils/message-queue.d.ts +47 -0
- package/lib/Utils/message-queue.js +226 -0
- package/lib/Utils/rich-message-utils.d.ts +21 -0
- package/lib/Utils/rich-message-utils.js +229 -0
- package/lib/Utils/rich-messages.d.ts +52 -0
- package/lib/Utils/rich-messages.js +185 -0
- package/lib/Utils/scheduled-messages.d.ts +122 -0
- package/lib/Utils/scheduled-messages.js +289 -0
- package/lib/Utils/smart-reconnect.d.ts +48 -0
- package/lib/Utils/smart-reconnect.js +207 -0
- package/lib/Utils/use-sqlite-auth-state.d.ts +11 -0
- package/lib/Utils/use-sqlite-auth-state.js +95 -0
- package/lib/VoIP/audio-feeder.d.ts +15 -0
- package/lib/VoIP/audio-feeder.js +132 -0
- package/lib/VoIP/index.js +277 -0
- package/lib/VoIP/relay-transport.d.ts +43 -0
- package/lib/VoIP/relay-transport.js +559 -0
- package/lib/VoIP/signaling.js +594 -0
- package/lib/VoIP/types.d.ts +69 -0
- package/lib/VoIP/types.js +17 -0
- package/lib/VoIP/wasm-engine.d.ts +103 -0
- package/lib/VoIP/wasm-engine.js +1214 -0
- package/lib/VoIP/worker-bootstrap.js +1042 -0
- package/lib/assets/wasm/loader.js +5 -0
- package/lib/assets/wasm/whatsapp.wasm +0 -0
- package/lib/assets/wasm/worker-modules.js +273 -0
- package/lib/index.d.ts +41 -0
- package/lib/index.js +3 -0
- package/package.json +10 -2
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { isJidGroup, jidDecode, jidNormalizedUser } from '../WABinary/index.js';
|
|
2
|
+
import { randomBytes } from 'crypto';
|
|
3
|
+
|
|
4
|
+
function parseCommand(text, prefix = '.') {
|
|
5
|
+
if (!text || typeof text !== 'string') return { isCommand: false };
|
|
6
|
+
const trimmed = text.trim();
|
|
7
|
+
if (!trimmed.startsWith(prefix)) return { isCommand: false };
|
|
8
|
+
const withoutPrefix = trimmed.slice(prefix.length);
|
|
9
|
+
const parts = withoutPrefix.split(/\s+/);
|
|
10
|
+
const command = parts[0].toLowerCase();
|
|
11
|
+
const args = parts.slice(1);
|
|
12
|
+
const fullArgs = withoutPrefix.slice(command.length).trim();
|
|
13
|
+
const flags = {};
|
|
14
|
+
const cleanArgs = [];
|
|
15
|
+
for (const arg of args) {
|
|
16
|
+
if (arg.startsWith('--')) {
|
|
17
|
+
const [key, value] = arg.slice(2).split('=');
|
|
18
|
+
flags[key] = value || true;
|
|
19
|
+
} else if (arg.startsWith('-') && arg.length === 2) {
|
|
20
|
+
flags[arg.slice(1)] = true;
|
|
21
|
+
} else {
|
|
22
|
+
cleanArgs.push(arg);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return { isCommand: true, command, args: cleanArgs, fullArgs, flags, raw: text, prefix };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function extractMentions(message) {
|
|
29
|
+
const mentions = [];
|
|
30
|
+
const extendedText = message.extendedTextMessage;
|
|
31
|
+
if (extendedText?.contextInfo?.mentionedJid) mentions.push(...extendedText.contextInfo.mentionedJid);
|
|
32
|
+
if (message.mentionedJid) mentions.push(...message.mentionedJid);
|
|
33
|
+
const text = extractText(message);
|
|
34
|
+
if (text) {
|
|
35
|
+
const phoneMatches = text.match(/@(\d+)/g);
|
|
36
|
+
if (phoneMatches) {
|
|
37
|
+
phoneMatches.forEach(match => {
|
|
38
|
+
const phone = match.slice(1);
|
|
39
|
+
const jid = `${phone}@s.whatsapp.net`;
|
|
40
|
+
if (!mentions.includes(jid)) mentions.push(jid);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return [...new Set(mentions)];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function extractText(message) {
|
|
48
|
+
if (!message) return '';
|
|
49
|
+
if (typeof message === 'string') return message;
|
|
50
|
+
const textSources = [
|
|
51
|
+
message.conversation,
|
|
52
|
+
message.extendedTextMessage?.text,
|
|
53
|
+
message.imageMessage?.caption,
|
|
54
|
+
message.videoMessage?.caption,
|
|
55
|
+
message.documentMessage?.caption,
|
|
56
|
+
message.buttonsResponseMessage?.selectedButtonId,
|
|
57
|
+
message.listResponseMessage?.singleSelectReply?.selectedRowId,
|
|
58
|
+
message.templateButtonReplyMessage?.selectedId
|
|
59
|
+
];
|
|
60
|
+
return textSources.find(t => t) || '';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function extractQuotedMessage(message) {
|
|
64
|
+
const contextInfo =
|
|
65
|
+
message.extendedTextMessage?.contextInfo ||
|
|
66
|
+
message.imageMessage?.contextInfo ||
|
|
67
|
+
message.videoMessage?.contextInfo ||
|
|
68
|
+
message.documentMessage?.contextInfo ||
|
|
69
|
+
message.stickerMessage?.contextInfo;
|
|
70
|
+
if (!contextInfo?.quotedMessage) return null;
|
|
71
|
+
return {
|
|
72
|
+
message: contextInfo.quotedMessage,
|
|
73
|
+
stanzaId: contextInfo.stanzaId,
|
|
74
|
+
participant: contextInfo.participant,
|
|
75
|
+
remoteJid: contextInfo.remoteJid,
|
|
76
|
+
text: extractText(contextInfo.quotedMessage)
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function isGroupAdmin(sock, jid, participantJid) {
|
|
81
|
+
try {
|
|
82
|
+
if (!isJidGroup(jid)) return false;
|
|
83
|
+
const metadata = await sock.groupMetadata(jid);
|
|
84
|
+
const participant = metadata.participants.find(
|
|
85
|
+
p => jidNormalizedUser(p.id) === jidNormalizedUser(participantJid)
|
|
86
|
+
);
|
|
87
|
+
return participant?.admin === 'admin' || participant?.admin === 'superadmin';
|
|
88
|
+
} catch { return false; }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function isBotAdmin(sock, jid) {
|
|
92
|
+
const botJid = sock.user?.id;
|
|
93
|
+
if (!botJid) return false;
|
|
94
|
+
return isGroupAdmin(sock, jid, botJid);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getSenderJid(msg) {
|
|
98
|
+
const isGroup = isJidGroup(msg.key.remoteJid);
|
|
99
|
+
return isGroup ? msg.key.participant : msg.key.remoteJid;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function formatJid(jid) {
|
|
103
|
+
if (!jid) return 'Unknown';
|
|
104
|
+
const decoded = jidDecode(jid);
|
|
105
|
+
return decoded?.user || jid.split('@')[0] || jid;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function generateMessageId() {
|
|
109
|
+
const timestamp = Date.now().toString(36);
|
|
110
|
+
const random = randomBytes(8).toString('hex');
|
|
111
|
+
return `${timestamp}-${random}`.toUpperCase();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function truncateText(text, maxLength = 100, suffix = '...') {
|
|
115
|
+
if (!text || text.length <= maxLength) return text;
|
|
116
|
+
return text.slice(0, maxLength - suffix.length) + suffix;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function escapeMarkdown(text) {
|
|
120
|
+
if (!text) return '';
|
|
121
|
+
return text.replace(/([*_`\[\]()])/g, '\\$1');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function formatPhoneNumber(jid) {
|
|
125
|
+
const number = jid.replace(/[^0-9]/g, '');
|
|
126
|
+
if (number.length >= 10) {
|
|
127
|
+
const countryCode = number.slice(0, -10);
|
|
128
|
+
const areaCode = number.slice(-10, -7);
|
|
129
|
+
const first = number.slice(-7, -4);
|
|
130
|
+
const last = number.slice(-4);
|
|
131
|
+
return `+${countryCode} ${areaCode} ${first} ${last}`;
|
|
132
|
+
}
|
|
133
|
+
return number;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
class CooldownManager {
|
|
137
|
+
constructor() { this.cooldowns = new Map(); }
|
|
138
|
+
isOnCooldown(key, cooldownMs) {
|
|
139
|
+
const expiry = this.cooldowns.get(key);
|
|
140
|
+
if (!expiry) return false;
|
|
141
|
+
if (Date.now() < expiry) return true;
|
|
142
|
+
this.cooldowns.delete(key);
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
setCooldown(key, cooldownMs) { this.cooldowns.set(key, Date.now() + cooldownMs); }
|
|
146
|
+
getRemainingTime(key) {
|
|
147
|
+
const expiry = this.cooldowns.get(key);
|
|
148
|
+
if (!expiry) return 0;
|
|
149
|
+
return Math.max(0, expiry - Date.now());
|
|
150
|
+
}
|
|
151
|
+
cleanup() {
|
|
152
|
+
const now = Date.now();
|
|
153
|
+
for (const [key, expiry] of this.cooldowns) {
|
|
154
|
+
if (now >= expiry) this.cooldowns.delete(key);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
class PermissionManager {
|
|
160
|
+
constructor(config = {}) {
|
|
161
|
+
this.owners = new Set(config.owners || []);
|
|
162
|
+
this.admins = new Set(config.admins || []);
|
|
163
|
+
this.banned = new Set(config.banned || []);
|
|
164
|
+
this.premiums = new Set(config.premiums || []);
|
|
165
|
+
}
|
|
166
|
+
isOwner(jid) { return this.owners.has(jidNormalizedUser(jid)); }
|
|
167
|
+
isAdmin(jid) {
|
|
168
|
+
const normalized = jidNormalizedUser(jid);
|
|
169
|
+
return this.admins.has(normalized) || this.owners.has(normalized);
|
|
170
|
+
}
|
|
171
|
+
isBanned(jid) { return this.banned.has(jidNormalizedUser(jid)); }
|
|
172
|
+
isPremium(jid) { return this.premiums.has(jidNormalizedUser(jid)); }
|
|
173
|
+
addOwner(jid) { this.owners.add(jidNormalizedUser(jid)); }
|
|
174
|
+
removeOwner(jid) { this.owners.delete(jidNormalizedUser(jid)); }
|
|
175
|
+
addAdmin(jid) { this.admins.add(jidNormalizedUser(jid)); }
|
|
176
|
+
removeAdmin(jid) { this.admins.delete(jidNormalizedUser(jid)); }
|
|
177
|
+
ban(jid) { this.banned.add(jidNormalizedUser(jid)); }
|
|
178
|
+
unban(jid) { this.banned.delete(jidNormalizedUser(jid)); }
|
|
179
|
+
addPremium(jid) { this.premiums.add(jidNormalizedUser(jid)); }
|
|
180
|
+
removePremium(jid) { this.premiums.delete(jidNormalizedUser(jid)); }
|
|
181
|
+
toJSON() {
|
|
182
|
+
return { owners: [...this.owners], admins: [...this.admins], banned: [...this.banned], premiums: [...this.premiums] };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function createReply(options) {
|
|
187
|
+
const { text, mentions = [], quoted = null } = options;
|
|
188
|
+
const message = { text };
|
|
189
|
+
if (mentions.length > 0) message.mentions = mentions;
|
|
190
|
+
if (quoted) message.quoted = quoted;
|
|
191
|
+
return message;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function parseTime(timeStr) {
|
|
195
|
+
if (!timeStr || typeof timeStr !== 'string') return 0;
|
|
196
|
+
const units = { s: 1000, m: 60 * 1000, h: 60 * 60 * 1000, d: 24 * 60 * 60 * 1000, w: 7 * 24 * 60 * 60 * 1000 };
|
|
197
|
+
const match = timeStr.match(/^(\d+)([smhdw])$/i);
|
|
198
|
+
if (!match) return 0;
|
|
199
|
+
const value = parseInt(match[1]);
|
|
200
|
+
const unit = match[2].toLowerCase();
|
|
201
|
+
return value * (units[unit] || 0);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function formatDuration(ms) {
|
|
205
|
+
const seconds = Math.floor(ms / 1000);
|
|
206
|
+
const minutes = Math.floor(seconds / 60);
|
|
207
|
+
const hours = Math.floor(minutes / 60);
|
|
208
|
+
const days = Math.floor(hours / 24);
|
|
209
|
+
if (days > 0) return `${days}d ${hours % 24}h`;
|
|
210
|
+
if (hours > 0) return `${hours}h ${minutes % 60}m`;
|
|
211
|
+
if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
|
|
212
|
+
return `${seconds}s`;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function sendFast(sock, jid, content, opts = {}) {
|
|
216
|
+
const { quoted = null, markRead = false } = opts;
|
|
217
|
+
const message = typeof content === 'string' ? { text: content } : content;
|
|
218
|
+
if (markRead && quoted?.key) sock.readMessages([quoted.key]).catch(() => {});
|
|
219
|
+
return sock.sendMessage(jid, message, quoted ? { quoted } : {});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export {
|
|
223
|
+
parseCommand,
|
|
224
|
+
extractMentions,
|
|
225
|
+
extractText,
|
|
226
|
+
extractQuotedMessage,
|
|
227
|
+
isGroupAdmin,
|
|
228
|
+
isBotAdmin,
|
|
229
|
+
getSenderJid,
|
|
230
|
+
formatJid,
|
|
231
|
+
generateMessageId,
|
|
232
|
+
truncateText,
|
|
233
|
+
escapeMarkdown,
|
|
234
|
+
formatPhoneNumber,
|
|
235
|
+
CooldownManager,
|
|
236
|
+
PermissionManager,
|
|
237
|
+
createReply,
|
|
238
|
+
parseTime,
|
|
239
|
+
formatDuration,
|
|
240
|
+
sendFast
|
|
241
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export declare const CACHE_CONFIG: {
|
|
2
|
+
MEMORY: { MAX_SIZE: number; TTL: number; STALE_TTL: number };
|
|
3
|
+
SIGNAL: { MAX_SIZE: number; TTL: number };
|
|
4
|
+
GROUPS: { MAX_SIZE: number; TTL: number };
|
|
5
|
+
PROFILES: { MAX_SIZE: number; TTL: number };
|
|
6
|
+
PERSIST_INTERVAL: number;
|
|
7
|
+
PERSIST_ON_SIZE: number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export declare class EnhancedCache<T = any> {
|
|
11
|
+
constructor(name: string, options?: { MAX_SIZE?: number; TTL?: number; STALE_TTL?: number; persistPath?: string });
|
|
12
|
+
get(key: string): T | undefined;
|
|
13
|
+
set(key: string, value: T, ttl?: number): void;
|
|
14
|
+
delete(key: string): void;
|
|
15
|
+
has(key: string): boolean;
|
|
16
|
+
getMany(keys: string[]): { found: Record<string, T>; missing: string[] };
|
|
17
|
+
setMany(entries: Record<string, T>): void;
|
|
18
|
+
load(): Promise<void>;
|
|
19
|
+
getStats(): {
|
|
20
|
+
hits: number;
|
|
21
|
+
misses: number;
|
|
22
|
+
sets: number;
|
|
23
|
+
deletes: number;
|
|
24
|
+
hitRate: string;
|
|
25
|
+
size: number;
|
|
26
|
+
hotCacheSize: number;
|
|
27
|
+
dirtyKeys: number;
|
|
28
|
+
};
|
|
29
|
+
clear(): void;
|
|
30
|
+
cleanup(): void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export declare class CacheManager {
|
|
34
|
+
constructor(baseDir?: string, logger?: any);
|
|
35
|
+
loadAll(): Promise<void>;
|
|
36
|
+
getCache(name: 'signal' | 'groups' | 'profiles' | 'messages' | 'misc'): EnhancedCache;
|
|
37
|
+
getAllStats(): Record<string, ReturnType<EnhancedCache['getStats']>>;
|
|
38
|
+
clearAll(): void;
|
|
39
|
+
cleanup(): Promise<void>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { LRUCache } from 'lru-cache';
|
|
2
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
|
|
5
|
+
const CACHE_CONFIG = {
|
|
6
|
+
MEMORY: {
|
|
7
|
+
MAX_SIZE: 10000,
|
|
8
|
+
TTL: 30 * 60 * 1000,
|
|
9
|
+
STALE_TTL: 5 * 60 * 1000
|
|
10
|
+
},
|
|
11
|
+
SIGNAL: {
|
|
12
|
+
MAX_SIZE: 5000,
|
|
13
|
+
TTL: 60 * 60 * 1000
|
|
14
|
+
},
|
|
15
|
+
GROUPS: {
|
|
16
|
+
MAX_SIZE: 500,
|
|
17
|
+
TTL: 15 * 60 * 1000
|
|
18
|
+
},
|
|
19
|
+
PROFILES: {
|
|
20
|
+
MAX_SIZE: 1000,
|
|
21
|
+
TTL: 60 * 60 * 1000
|
|
22
|
+
},
|
|
23
|
+
PERSIST_INTERVAL: 5 * 60 * 1000,
|
|
24
|
+
PERSIST_ON_SIZE: 100
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
class EnhancedCache {
|
|
28
|
+
constructor(name, options = {}) {
|
|
29
|
+
this.name = name;
|
|
30
|
+
this.options = { ...CACHE_CONFIG.MEMORY, ...options };
|
|
31
|
+
this.persistPath = options.persistPath;
|
|
32
|
+
this.dirty = new Set();
|
|
33
|
+
this.persistTimer = null;
|
|
34
|
+
this.stats = { hits: 0, misses: 0, sets: 0, deletes: 0 };
|
|
35
|
+
this.cache = new LRUCache({
|
|
36
|
+
max: this.options.MAX_SIZE,
|
|
37
|
+
ttl: this.options.TTL,
|
|
38
|
+
allowStale: true,
|
|
39
|
+
updateAgeOnGet: true,
|
|
40
|
+
updateAgeOnHas: true
|
|
41
|
+
});
|
|
42
|
+
this.hotCache = new Map();
|
|
43
|
+
this.hotCacheMaxSize = Math.floor(this.options.MAX_SIZE * 0.1);
|
|
44
|
+
this.accessCount = new Map();
|
|
45
|
+
if (this.persistPath) this._startPersistence();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get(key) {
|
|
49
|
+
if (this.hotCache.has(key)) {
|
|
50
|
+
this.stats.hits++;
|
|
51
|
+
this._trackAccess(key);
|
|
52
|
+
return this.hotCache.get(key);
|
|
53
|
+
}
|
|
54
|
+
const value = this.cache.get(key);
|
|
55
|
+
if (value !== undefined) {
|
|
56
|
+
this.stats.hits++;
|
|
57
|
+
this._trackAccess(key);
|
|
58
|
+
if (this._isHotKey(key)) this._addToHotCache(key, value);
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
this.stats.misses++;
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
set(key, value, ttl = undefined) {
|
|
66
|
+
this.cache.set(key, value, { ttl: ttl || this.options.TTL });
|
|
67
|
+
this.stats.sets++;
|
|
68
|
+
if (this.hotCache.has(key)) this.hotCache.set(key, value);
|
|
69
|
+
if (this.persistPath) {
|
|
70
|
+
this.dirty.add(key);
|
|
71
|
+
if (this.dirty.size >= CACHE_CONFIG.PERSIST_ON_SIZE) this._persist();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
delete(key) {
|
|
76
|
+
this.cache.delete(key);
|
|
77
|
+
this.hotCache.delete(key);
|
|
78
|
+
this.accessCount.delete(key);
|
|
79
|
+
this.dirty.delete(key);
|
|
80
|
+
this.stats.deletes++;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
has(key) {
|
|
84
|
+
return this.hotCache.has(key) || this.cache.has(key);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
getMany(keys) {
|
|
88
|
+
const result = {};
|
|
89
|
+
const missing = [];
|
|
90
|
+
for (const key of keys) {
|
|
91
|
+
const value = this.get(key);
|
|
92
|
+
if (value !== undefined) result[key] = value;
|
|
93
|
+
else missing.push(key);
|
|
94
|
+
}
|
|
95
|
+
return { found: result, missing };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
setMany(entries) {
|
|
99
|
+
for (const [key, value] of Object.entries(entries)) this.set(key, value);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
_trackAccess(key) {
|
|
103
|
+
const count = (this.accessCount.get(key) || 0) + 1;
|
|
104
|
+
this.accessCount.set(key, count);
|
|
105
|
+
if (this.accessCount.size > this.options.MAX_SIZE) this._cleanupAccessCount();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
_isHotKey(key) {
|
|
109
|
+
return (this.accessCount.get(key) || 0) >= 3;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
_addToHotCache(key, value) {
|
|
113
|
+
if (this.hotCache.size >= this.hotCacheMaxSize) {
|
|
114
|
+
let minKey = null;
|
|
115
|
+
let minCount = Infinity;
|
|
116
|
+
for (const [k] of this.hotCache) {
|
|
117
|
+
const count = this.accessCount.get(k) || 0;
|
|
118
|
+
if (count < minCount) { minCount = count; minKey = k; }
|
|
119
|
+
}
|
|
120
|
+
if (minKey) this.hotCache.delete(minKey);
|
|
121
|
+
}
|
|
122
|
+
this.hotCache.set(key, value);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
_cleanupAccessCount() {
|
|
126
|
+
const entries = [...this.accessCount.entries()];
|
|
127
|
+
entries.sort((a, b) => b[1] - a[1]);
|
|
128
|
+
this.accessCount.clear();
|
|
129
|
+
entries.slice(0, this.options.MAX_SIZE / 2).forEach(([k, v]) => {
|
|
130
|
+
this.accessCount.set(k, Math.floor(v / 2));
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
_startPersistence() {
|
|
135
|
+
this.persistTimer = setInterval(() => {
|
|
136
|
+
if (this.dirty.size > 0) this._persist();
|
|
137
|
+
}, CACHE_CONFIG.PERSIST_INTERVAL);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async _persist() {
|
|
141
|
+
if (!this.persistPath || this.dirty.size === 0) return;
|
|
142
|
+
try {
|
|
143
|
+
const data = {};
|
|
144
|
+
for (const key of this.dirty) {
|
|
145
|
+
const value = this.cache.get(key);
|
|
146
|
+
if (value !== undefined) data[key] = value;
|
|
147
|
+
}
|
|
148
|
+
const filePath = join(this.persistPath, `${this.name}-cache.json`);
|
|
149
|
+
let existing = {};
|
|
150
|
+
try {
|
|
151
|
+
const content = await readFile(filePath, 'utf8');
|
|
152
|
+
existing = JSON.parse(content);
|
|
153
|
+
} catch {}
|
|
154
|
+
const merged = { ...existing, ...data };
|
|
155
|
+
await writeFile(filePath, JSON.stringify(merged), 'utf8');
|
|
156
|
+
this.dirty.clear();
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.error('Cache persist error:', error);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async load() {
|
|
163
|
+
if (!this.persistPath) return;
|
|
164
|
+
try {
|
|
165
|
+
const filePath = join(this.persistPath, `${this.name}-cache.json`);
|
|
166
|
+
const content = await readFile(filePath, 'utf8');
|
|
167
|
+
const data = JSON.parse(content);
|
|
168
|
+
for (const [key, value] of Object.entries(data)) this.cache.set(key, value);
|
|
169
|
+
} catch {}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
getStats() {
|
|
173
|
+
const hitRate = this.stats.hits / (this.stats.hits + this.stats.misses) || 0;
|
|
174
|
+
return {
|
|
175
|
+
...this.stats,
|
|
176
|
+
hitRate: (hitRate * 100).toFixed(2) + '%',
|
|
177
|
+
size: this.cache.size,
|
|
178
|
+
hotCacheSize: this.hotCache.size,
|
|
179
|
+
dirtyKeys: this.dirty.size
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
clear() {
|
|
184
|
+
this.cache.clear();
|
|
185
|
+
this.hotCache.clear();
|
|
186
|
+
this.accessCount.clear();
|
|
187
|
+
this.dirty.clear();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
cleanup() {
|
|
191
|
+
if (this.persistTimer) clearInterval(this.persistTimer);
|
|
192
|
+
this._persist();
|
|
193
|
+
this.clear();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
class CacheManager {
|
|
198
|
+
constructor(baseDir, logger) {
|
|
199
|
+
this.baseDir = baseDir;
|
|
200
|
+
this.logger = logger;
|
|
201
|
+
this.caches = {
|
|
202
|
+
signal: new EnhancedCache('signal', { ...CACHE_CONFIG.SIGNAL, persistPath: baseDir }),
|
|
203
|
+
groups: new EnhancedCache('groups', { ...CACHE_CONFIG.GROUPS, persistPath: baseDir }),
|
|
204
|
+
profiles: new EnhancedCache('profiles', { ...CACHE_CONFIG.PROFILES, persistPath: baseDir }),
|
|
205
|
+
messages: new EnhancedCache('messages', CACHE_CONFIG.MEMORY),
|
|
206
|
+
misc: new EnhancedCache('misc', CACHE_CONFIG.MEMORY)
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async loadAll() {
|
|
211
|
+
await Promise.all([
|
|
212
|
+
this.caches.signal.load(),
|
|
213
|
+
this.caches.groups.load(),
|
|
214
|
+
this.caches.profiles.load()
|
|
215
|
+
]);
|
|
216
|
+
this.logger?.info('All caches loaded');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
getCache(name) {
|
|
220
|
+
return this.caches[name];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
getAllStats() {
|
|
224
|
+
const stats = {};
|
|
225
|
+
for (const [name, cache] of Object.entries(this.caches)) stats[name] = cache.getStats();
|
|
226
|
+
return stats;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
clearAll() {
|
|
230
|
+
for (const cache of Object.values(this.caches)) cache.clear();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async cleanup() {
|
|
234
|
+
for (const cache of Object.values(this.caches)) cache.cleanup();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export {
|
|
239
|
+
CACHE_CONFIG,
|
|
240
|
+
EnhancedCache,
|
|
241
|
+
CacheManager
|
|
242
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export declare const LOG_LEVELS: {
|
|
2
|
+
fatal: number; error: number; warn: number; info: number; debug: number; trace: number; silent: number;
|
|
3
|
+
};
|
|
4
|
+
export declare const COLORS: Record<string, string>;
|
|
5
|
+
export declare const CATEGORY_ICONS: Record<string, string>;
|
|
6
|
+
|
|
7
|
+
export type LogLevel = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace';
|
|
8
|
+
|
|
9
|
+
export declare class RyzeLogger {
|
|
10
|
+
constructor(options?: {
|
|
11
|
+
level?: LogLevel;
|
|
12
|
+
prettyPrint?: boolean;
|
|
13
|
+
category?: string;
|
|
14
|
+
colors?: boolean;
|
|
15
|
+
timestamps?: boolean;
|
|
16
|
+
filters?: string[];
|
|
17
|
+
maxHistory?: number;
|
|
18
|
+
});
|
|
19
|
+
fatal(messageOrData: string | object, data?: object): void;
|
|
20
|
+
error(messageOrData: string | object, data?: object): void;
|
|
21
|
+
warn(messageOrData: string | object, data?: object): void;
|
|
22
|
+
info(messageOrData: string | object, data?: object): void;
|
|
23
|
+
debug(messageOrData: string | object, data?: object): void;
|
|
24
|
+
trace(messageOrData: string | object, data?: object): void;
|
|
25
|
+
success(message: string, data?: object): void;
|
|
26
|
+
child(bindings: { class?: string; category?: string }): RyzeLogger;
|
|
27
|
+
setLevel(level: LogLevel): void;
|
|
28
|
+
addFilter(pattern: string): void;
|
|
29
|
+
removeFilter(pattern: string): void;
|
|
30
|
+
getHistory(options?: { level?: LogLevel; category?: string; since?: string | number | Date; limit?: number }): Array<{ timestamp: string; level: LogLevel; category: string; message: string; data: any }>;
|
|
31
|
+
clearHistory(): void;
|
|
32
|
+
exportLogs(format?: 'json' | 'text'): string;
|
|
33
|
+
time(label: string): { end: (message?: string) => number };
|
|
34
|
+
if(condition: boolean, level: LogLevel, message: string, data?: object): void;
|
|
35
|
+
throttle(key: string, message: string, data?: object, intervalMs?: number): void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export declare function createLogger(options?: ConstructorParameters<typeof RyzeLogger>[0]): RyzeLogger;
|
|
39
|
+
|
|
40
|
+
declare const defaultLogger: RyzeLogger;
|
|
41
|
+
export default defaultLogger;
|