waengine 1.7.3 → 1.7.4
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/CHANGELOG.md +29 -0
- package/README.md +34 -3
- package/package.json +3 -2
- package/src/ab-testing.js +698 -0
- package/src/advanced-scheduler.js +577 -0
- package/src/ai-features.js +459 -0
- package/src/ai-integration.js +2 -1
- package/src/analytics-manager.js +458 -0
- package/src/business-manager.js +362 -0
- package/src/client.js +447 -39
- package/src/console-logger.js +256 -0
- package/src/core.js +28 -3
- package/src/cross-platform.js +538 -0
- package/src/database-manager.js +766 -0
- package/src/device-manager.js +1 -1
- package/src/easy-bot-fixed.js +341 -0
- package/src/easy-bot.js +503 -22
- package/src/error-handler.js +230 -0
- package/src/gaming-manager.js +842 -0
- package/src/http-client.js +1 -1
- package/src/index.js +15 -0
- package/src/message.js +197 -94
- package/src/multi-client.js +26 -12
- package/src/plugin-manager.js +59 -10
- package/src/prefix-manager.js +48 -1
- package/src/qr-terminal-fix.js +239 -0
- package/src/qr.js +170 -27
- package/src/quick-bot.js +63 -0
- package/src/reporting-manager.js +867 -0
- package/src/scheduler.js +14 -1
- package/src/security-manager.js +678 -0
- package/src/session-manager-old.js +314 -0
- package/src/session-manager.js +429 -24
- package/src/storage.js +254 -194
- package/src/ui-components.js +560 -0
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
import { getStorage } from "./storage.js";
|
|
2
|
+
import cron from "node-cron";
|
|
3
|
+
|
|
4
|
+
export class AdvancedScheduler {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
this.storage = getStorage();
|
|
8
|
+
this.scheduledTasks = new Map();
|
|
9
|
+
this.cronJobs = new Map();
|
|
10
|
+
this.recurringTasks = new Map();
|
|
11
|
+
this.taskQueue = [];
|
|
12
|
+
this.isProcessing = false;
|
|
13
|
+
|
|
14
|
+
this.initializeScheduler();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// ===== INITIALIZATION =====
|
|
18
|
+
|
|
19
|
+
initializeScheduler() {
|
|
20
|
+
this.loadScheduledTasks();
|
|
21
|
+
this.startTaskProcessor();
|
|
22
|
+
this.startMaintenanceJob();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
loadScheduledTasks() {
|
|
26
|
+
const tasks = this.storage.read.from("scheduler").get("tasks") || {};
|
|
27
|
+
const cronTasks = this.storage.read.from("scheduler").get("cronTasks") || {};
|
|
28
|
+
|
|
29
|
+
// Restore scheduled tasks
|
|
30
|
+
Object.entries(tasks).forEach(([id, task]) => {
|
|
31
|
+
if (task.executeAt > Date.now()) {
|
|
32
|
+
this.scheduledTasks.set(id, task);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Restore cron jobs
|
|
37
|
+
Object.entries(cronTasks).forEach(([id, task]) => {
|
|
38
|
+
this.createCronJob(id, task.pattern, task.action, task.data);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
startTaskProcessor() {
|
|
43
|
+
setInterval(() => {
|
|
44
|
+
if (!this.isProcessing && this.taskQueue.length > 0) {
|
|
45
|
+
this.processTaskQueue();
|
|
46
|
+
}
|
|
47
|
+
}, 1000);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
startMaintenanceJob() {
|
|
51
|
+
// Clean up expired tasks every hour
|
|
52
|
+
cron.schedule('0 * * * *', () => {
|
|
53
|
+
this.cleanupExpiredTasks();
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ===== SCHEDULING METHODS =====
|
|
58
|
+
|
|
59
|
+
scheduleMessage(chatId, message, executeAt, options = {}) {
|
|
60
|
+
const taskId = this.generateTaskId();
|
|
61
|
+
const task = {
|
|
62
|
+
id: taskId,
|
|
63
|
+
type: 'message',
|
|
64
|
+
chatId,
|
|
65
|
+
message,
|
|
66
|
+
executeAt: new Date(executeAt).getTime(),
|
|
67
|
+
options,
|
|
68
|
+
status: 'scheduled',
|
|
69
|
+
createdAt: Date.now()
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
this.scheduledTasks.set(taskId, task);
|
|
73
|
+
this.saveTask(taskId, task);
|
|
74
|
+
|
|
75
|
+
return taskId;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
scheduleRecurringMessage(chatId, message, pattern, options = {}) {
|
|
79
|
+
const taskId = this.generateTaskId();
|
|
80
|
+
const task = {
|
|
81
|
+
id: taskId,
|
|
82
|
+
type: 'recurring_message',
|
|
83
|
+
chatId,
|
|
84
|
+
message,
|
|
85
|
+
pattern,
|
|
86
|
+
options,
|
|
87
|
+
status: 'active',
|
|
88
|
+
createdAt: Date.now()
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
this.createCronJob(taskId, pattern, 'sendMessage', { chatId, message, options });
|
|
92
|
+
this.recurringTasks.set(taskId, task);
|
|
93
|
+
this.saveCronTask(taskId, task);
|
|
94
|
+
|
|
95
|
+
return taskId;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
scheduleCustomAction(actionName, data, executeAt, options = {}) {
|
|
99
|
+
const taskId = this.generateTaskId();
|
|
100
|
+
const task = {
|
|
101
|
+
id: taskId,
|
|
102
|
+
type: 'custom_action',
|
|
103
|
+
actionName,
|
|
104
|
+
data,
|
|
105
|
+
executeAt: new Date(executeAt).getTime(),
|
|
106
|
+
options,
|
|
107
|
+
status: 'scheduled',
|
|
108
|
+
createdAt: Date.now()
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
this.scheduledTasks.set(taskId, task);
|
|
112
|
+
this.saveTask(taskId, task);
|
|
113
|
+
|
|
114
|
+
return taskId;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
scheduleRecurringAction(actionName, data, pattern, options = {}) {
|
|
118
|
+
const taskId = this.generateTaskId();
|
|
119
|
+
const task = {
|
|
120
|
+
id: taskId,
|
|
121
|
+
type: 'recurring_action',
|
|
122
|
+
actionName,
|
|
123
|
+
data,
|
|
124
|
+
pattern,
|
|
125
|
+
options,
|
|
126
|
+
status: 'active',
|
|
127
|
+
createdAt: Date.now()
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
this.createCronJob(taskId, pattern, actionName, data);
|
|
131
|
+
this.recurringTasks.set(taskId, task);
|
|
132
|
+
this.saveCronTask(taskId, task);
|
|
133
|
+
|
|
134
|
+
return taskId;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ===== BULK SCHEDULING =====
|
|
138
|
+
|
|
139
|
+
scheduleBulkMessages(messages, executeAt, options = {}) {
|
|
140
|
+
const taskIds = [];
|
|
141
|
+
const delay = options.delay || 1000; // 1 second delay between messages
|
|
142
|
+
|
|
143
|
+
messages.forEach((msg, index) => {
|
|
144
|
+
const adjustedTime = new Date(executeAt).getTime() + (index * delay);
|
|
145
|
+
const taskId = this.scheduleMessage(msg.chatId, msg.message, adjustedTime, msg.options);
|
|
146
|
+
taskIds.push(taskId);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
return taskIds;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
scheduleMessageChain(chatId, messages, startTime, options = {}) {
|
|
153
|
+
const taskIds = [];
|
|
154
|
+
const delay = options.delay || 2000; // 2 seconds delay between chain messages
|
|
155
|
+
|
|
156
|
+
messages.forEach((message, index) => {
|
|
157
|
+
const executeTime = new Date(startTime).getTime() + (index * delay);
|
|
158
|
+
const taskId = this.scheduleMessage(chatId, message, executeTime, options);
|
|
159
|
+
taskIds.push(taskId);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
return taskIds;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ===== CONDITIONAL SCHEDULING =====
|
|
166
|
+
|
|
167
|
+
scheduleConditionalMessage(chatId, message, condition, checkInterval = 60000) {
|
|
168
|
+
const taskId = this.generateTaskId();
|
|
169
|
+
const task = {
|
|
170
|
+
id: taskId,
|
|
171
|
+
type: 'conditional_message',
|
|
172
|
+
chatId,
|
|
173
|
+
message,
|
|
174
|
+
condition,
|
|
175
|
+
checkInterval,
|
|
176
|
+
status: 'waiting',
|
|
177
|
+
createdAt: Date.now(),
|
|
178
|
+
lastCheck: Date.now()
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
this.scheduledTasks.set(taskId, task);
|
|
182
|
+
this.saveTask(taskId, task);
|
|
183
|
+
|
|
184
|
+
// Start checking condition
|
|
185
|
+
this.startConditionalCheck(taskId);
|
|
186
|
+
|
|
187
|
+
return taskId;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
startConditionalCheck(taskId) {
|
|
191
|
+
const task = this.scheduledTasks.get(taskId);
|
|
192
|
+
if (!task) return;
|
|
193
|
+
|
|
194
|
+
const checkCondition = async () => {
|
|
195
|
+
try {
|
|
196
|
+
const conditionMet = await this.evaluateCondition(task.condition);
|
|
197
|
+
if (conditionMet) {
|
|
198
|
+
await this.executeTask(task);
|
|
199
|
+
this.cancelTask(taskId);
|
|
200
|
+
} else {
|
|
201
|
+
task.lastCheck = Date.now();
|
|
202
|
+
setTimeout(checkCondition, task.checkInterval);
|
|
203
|
+
}
|
|
204
|
+
} catch (error) {
|
|
205
|
+
console.error(`Condition check failed for task ${taskId}:`, error);
|
|
206
|
+
this.cancelTask(taskId);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
setTimeout(checkCondition, task.checkInterval);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ===== SMART SCHEDULING =====
|
|
214
|
+
|
|
215
|
+
scheduleSmartMessage(chatId, message, options = {}) {
|
|
216
|
+
const smartOptions = {
|
|
217
|
+
timezone: options.timezone || 'UTC',
|
|
218
|
+
preferredHours: options.preferredHours || [9, 10, 11, 14, 15, 16, 17],
|
|
219
|
+
avoidWeekends: options.avoidWeekends || false,
|
|
220
|
+
userActivity: options.considerUserActivity || false,
|
|
221
|
+
...options
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const optimalTime = this.calculateOptimalTime(chatId, smartOptions);
|
|
225
|
+
return this.scheduleMessage(chatId, message, optimalTime, smartOptions);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
calculateOptimalTime(chatId, options) {
|
|
229
|
+
const now = new Date();
|
|
230
|
+
let optimalTime = new Date(now);
|
|
231
|
+
|
|
232
|
+
// Add base delay
|
|
233
|
+
optimalTime.setMinutes(optimalTime.getMinutes() + (options.minDelay || 5));
|
|
234
|
+
|
|
235
|
+
// Adjust for preferred hours
|
|
236
|
+
if (options.preferredHours && options.preferredHours.length > 0) {
|
|
237
|
+
const currentHour = optimalTime.getHours();
|
|
238
|
+
if (!options.preferredHours.includes(currentHour)) {
|
|
239
|
+
const nextPreferredHour = options.preferredHours.find(h => h > currentHour) || options.preferredHours[0];
|
|
240
|
+
if (nextPreferredHour > currentHour) {
|
|
241
|
+
optimalTime.setHours(nextPreferredHour, 0, 0, 0);
|
|
242
|
+
} else {
|
|
243
|
+
optimalTime.setDate(optimalTime.getDate() + 1);
|
|
244
|
+
optimalTime.setHours(nextPreferredHour, 0, 0, 0);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Avoid weekends if specified
|
|
250
|
+
if (options.avoidWeekends) {
|
|
251
|
+
const dayOfWeek = optimalTime.getDay();
|
|
252
|
+
if (dayOfWeek === 0 || dayOfWeek === 6) { // Sunday or Saturday
|
|
253
|
+
const daysToAdd = dayOfWeek === 0 ? 1 : (7 - dayOfWeek + 1);
|
|
254
|
+
optimalTime.setDate(optimalTime.getDate() + daysToAdd);
|
|
255
|
+
optimalTime.setHours(options.preferredHours[0] || 9, 0, 0, 0);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return optimalTime;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// ===== TASK MANAGEMENT =====
|
|
263
|
+
|
|
264
|
+
cancelTask(taskId) {
|
|
265
|
+
const task = this.scheduledTasks.get(taskId) || this.recurringTasks.get(taskId);
|
|
266
|
+
if (!task) return false;
|
|
267
|
+
|
|
268
|
+
// Cancel cron job if it exists
|
|
269
|
+
if (this.cronJobs.has(taskId)) {
|
|
270
|
+
this.cronJobs.get(taskId).destroy();
|
|
271
|
+
this.cronJobs.delete(taskId);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Remove from maps
|
|
275
|
+
this.scheduledTasks.delete(taskId);
|
|
276
|
+
this.recurringTasks.delete(taskId);
|
|
277
|
+
|
|
278
|
+
// Remove from storage
|
|
279
|
+
this.removeTask(taskId);
|
|
280
|
+
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
pauseTask(taskId) {
|
|
285
|
+
const task = this.scheduledTasks.get(taskId) || this.recurringTasks.get(taskId);
|
|
286
|
+
if (!task) return false;
|
|
287
|
+
|
|
288
|
+
task.status = 'paused';
|
|
289
|
+
|
|
290
|
+
if (this.cronJobs.has(taskId)) {
|
|
291
|
+
this.cronJobs.get(taskId).stop();
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
this.saveTask(taskId, task);
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
resumeTask(taskId) {
|
|
299
|
+
const task = this.scheduledTasks.get(taskId) || this.recurringTasks.get(taskId);
|
|
300
|
+
if (!task) return false;
|
|
301
|
+
|
|
302
|
+
task.status = task.type.includes('recurring') ? 'active' : 'scheduled';
|
|
303
|
+
|
|
304
|
+
if (this.cronJobs.has(taskId)) {
|
|
305
|
+
this.cronJobs.get(taskId).start();
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
this.saveTask(taskId, task);
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
updateTask(taskId, updates) {
|
|
313
|
+
const task = this.scheduledTasks.get(taskId) || this.recurringTasks.get(taskId);
|
|
314
|
+
if (!task) return false;
|
|
315
|
+
|
|
316
|
+
Object.assign(task, updates);
|
|
317
|
+
|
|
318
|
+
// If it's a recurring task and pattern changed, recreate cron job
|
|
319
|
+
if (task.type.includes('recurring') && updates.pattern) {
|
|
320
|
+
if (this.cronJobs.has(taskId)) {
|
|
321
|
+
this.cronJobs.get(taskId).destroy();
|
|
322
|
+
}
|
|
323
|
+
this.createCronJob(taskId, updates.pattern, task.actionName || 'sendMessage', task.data || { chatId: task.chatId, message: task.message });
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
this.saveTask(taskId, task);
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// ===== TASK EXECUTION =====
|
|
331
|
+
|
|
332
|
+
async processTaskQueue() {
|
|
333
|
+
this.isProcessing = true;
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
const now = Date.now();
|
|
337
|
+
const tasksToExecute = [];
|
|
338
|
+
|
|
339
|
+
// Find tasks ready for execution
|
|
340
|
+
for (const [taskId, task] of this.scheduledTasks) {
|
|
341
|
+
if (task.status === 'scheduled' && task.executeAt <= now) {
|
|
342
|
+
tasksToExecute.push(task);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Execute tasks
|
|
347
|
+
for (const task of tasksToExecute) {
|
|
348
|
+
try {
|
|
349
|
+
await this.executeTask(task);
|
|
350
|
+
this.scheduledTasks.delete(task.id);
|
|
351
|
+
this.removeTask(task.id);
|
|
352
|
+
} catch (error) {
|
|
353
|
+
console.error(`Task execution failed for ${task.id}:`, error);
|
|
354
|
+
task.status = 'failed';
|
|
355
|
+
task.error = error.message;
|
|
356
|
+
this.saveTask(task.id, task);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
} finally {
|
|
360
|
+
this.isProcessing = false;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
async executeTask(task) {
|
|
365
|
+
switch (task.type) {
|
|
366
|
+
case 'message':
|
|
367
|
+
case 'conditional_message':
|
|
368
|
+
await this.client.sendMessage(task.chatId, task.message, task.options);
|
|
369
|
+
break;
|
|
370
|
+
|
|
371
|
+
case 'custom_action':
|
|
372
|
+
await this.executeCustomAction(task.actionName, task.data);
|
|
373
|
+
break;
|
|
374
|
+
|
|
375
|
+
default:
|
|
376
|
+
throw new Error(`Unknown task type: ${task.type}`);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Log execution
|
|
380
|
+
console.log(`✅ Task executed: ${task.id} (${task.type})`);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
async executeCustomAction(actionName, data) {
|
|
384
|
+
// This method can be extended to handle custom actions
|
|
385
|
+
if (typeof this.client[actionName] === 'function') {
|
|
386
|
+
await this.client[actionName](data);
|
|
387
|
+
} else {
|
|
388
|
+
throw new Error(`Custom action not found: ${actionName}`);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// ===== CRON JOB MANAGEMENT =====
|
|
393
|
+
|
|
394
|
+
createCronJob(taskId, pattern, action, data) {
|
|
395
|
+
try {
|
|
396
|
+
const job = cron.schedule(pattern, async () => {
|
|
397
|
+
try {
|
|
398
|
+
if (action === 'sendMessage') {
|
|
399
|
+
await this.client.sendMessage(data.chatId, data.message, data.options);
|
|
400
|
+
} else {
|
|
401
|
+
await this.executeCustomAction(action, data);
|
|
402
|
+
}
|
|
403
|
+
console.log(`🔄 Recurring task executed: ${taskId}`);
|
|
404
|
+
} catch (error) {
|
|
405
|
+
console.error(`Recurring task failed: ${taskId}`, error);
|
|
406
|
+
}
|
|
407
|
+
}, {
|
|
408
|
+
scheduled: true
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
this.cronJobs.set(taskId, job);
|
|
412
|
+
return job;
|
|
413
|
+
} catch (error) {
|
|
414
|
+
console.error(`Failed to create cron job: ${taskId}`, error);
|
|
415
|
+
throw error;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// ===== CONDITION EVALUATION =====
|
|
420
|
+
|
|
421
|
+
async evaluateCondition(condition) {
|
|
422
|
+
try {
|
|
423
|
+
if (typeof condition === 'function') {
|
|
424
|
+
return await condition();
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (typeof condition === 'object') {
|
|
428
|
+
return await this.evaluateObjectCondition(condition);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return Boolean(condition);
|
|
432
|
+
} catch (error) {
|
|
433
|
+
console.error('Condition evaluation failed:', error);
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
async evaluateObjectCondition(condition) {
|
|
439
|
+
const { type, ...params } = condition;
|
|
440
|
+
|
|
441
|
+
switch (type) {
|
|
442
|
+
case 'time':
|
|
443
|
+
return this.checkTimeCondition(params);
|
|
444
|
+
case 'user_online':
|
|
445
|
+
return await this.checkUserOnlineCondition(params);
|
|
446
|
+
case 'message_count':
|
|
447
|
+
return await this.checkMessageCountCondition(params);
|
|
448
|
+
default:
|
|
449
|
+
return false;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
checkTimeCondition(params) {
|
|
454
|
+
const now = new Date();
|
|
455
|
+
const { hour, minute = 0, dayOfWeek } = params;
|
|
456
|
+
|
|
457
|
+
if (dayOfWeek !== undefined && now.getDay() !== dayOfWeek) {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return now.getHours() === hour && now.getMinutes() >= minute;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async checkUserOnlineCondition(params) {
|
|
465
|
+
// This would need to be implemented based on your WhatsApp client's capabilities
|
|
466
|
+
// For now, return true as a placeholder
|
|
467
|
+
return true;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
async checkMessageCountCondition(params) {
|
|
471
|
+
// This would check message count in a chat
|
|
472
|
+
// Implementation depends on your storage system
|
|
473
|
+
return true;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// ===== UTILITY METHODS =====
|
|
477
|
+
|
|
478
|
+
generateTaskId() {
|
|
479
|
+
return `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
saveTask(taskId, task) {
|
|
483
|
+
const tasks = this.storage.read.from("scheduler").get("tasks") || {};
|
|
484
|
+
tasks[taskId] = task;
|
|
485
|
+
this.storage.write.to("scheduler").set("tasks", tasks);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
saveCronTask(taskId, task) {
|
|
489
|
+
const cronTasks = this.storage.read.from("scheduler").get("cronTasks") || {};
|
|
490
|
+
cronTasks[taskId] = task;
|
|
491
|
+
this.storage.write.to("scheduler").set("cronTasks", cronTasks);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
removeTask(taskId) {
|
|
495
|
+
const tasks = this.storage.read.from("scheduler").get("tasks") || {};
|
|
496
|
+
const cronTasks = this.storage.read.from("scheduler").get("cronTasks") || {};
|
|
497
|
+
|
|
498
|
+
delete tasks[taskId];
|
|
499
|
+
delete cronTasks[taskId];
|
|
500
|
+
|
|
501
|
+
this.storage.write.to("scheduler").set("tasks", tasks);
|
|
502
|
+
this.storage.write.to("scheduler").set("cronTasks", cronTasks);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
cleanupExpiredTasks() {
|
|
506
|
+
const now = Date.now();
|
|
507
|
+
const expiredTasks = [];
|
|
508
|
+
|
|
509
|
+
for (const [taskId, task] of this.scheduledTasks) {
|
|
510
|
+
if (task.status === 'failed' || (task.executeAt && task.executeAt < now - 86400000)) { // 24 hours old
|
|
511
|
+
expiredTasks.push(taskId);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
expiredTasks.forEach(taskId => {
|
|
516
|
+
this.scheduledTasks.delete(taskId);
|
|
517
|
+
this.removeTask(taskId);
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
console.log(`🧹 Cleaned up ${expiredTasks.length} expired tasks`);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// ===== QUERY METHODS =====
|
|
524
|
+
|
|
525
|
+
getAllTasks() {
|
|
526
|
+
return {
|
|
527
|
+
scheduled: Array.from(this.scheduledTasks.values()),
|
|
528
|
+
recurring: Array.from(this.recurringTasks.values())
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
getTask(taskId) {
|
|
533
|
+
return this.scheduledTasks.get(taskId) || this.recurringTasks.get(taskId);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
getTasksByChat(chatId) {
|
|
537
|
+
const scheduled = Array.from(this.scheduledTasks.values()).filter(task => task.chatId === chatId);
|
|
538
|
+
const recurring = Array.from(this.recurringTasks.values()).filter(task => task.chatId === chatId);
|
|
539
|
+
|
|
540
|
+
return { scheduled, recurring };
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
getTasksByStatus(status) {
|
|
544
|
+
const scheduled = Array.from(this.scheduledTasks.values()).filter(task => task.status === status);
|
|
545
|
+
const recurring = Array.from(this.recurringTasks.values()).filter(task => task.status === status);
|
|
546
|
+
|
|
547
|
+
return { scheduled, recurring };
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
getUpcomingTasks(limit = 10) {
|
|
551
|
+
return Array.from(this.scheduledTasks.values())
|
|
552
|
+
.filter(task => task.status === 'scheduled')
|
|
553
|
+
.sort((a, b) => a.executeAt - b.executeAt)
|
|
554
|
+
.slice(0, limit);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// ===== STATISTICS =====
|
|
558
|
+
|
|
559
|
+
getSchedulerStats() {
|
|
560
|
+
const scheduled = this.scheduledTasks.size;
|
|
561
|
+
const recurring = this.recurringTasks.size;
|
|
562
|
+
const cronJobs = this.cronJobs.size;
|
|
563
|
+
|
|
564
|
+
const statusCounts = {};
|
|
565
|
+
for (const task of [...this.scheduledTasks.values(), ...this.recurringTasks.values()]) {
|
|
566
|
+
statusCounts[task.status] = (statusCounts[task.status] || 0) + 1;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
return {
|
|
570
|
+
totalTasks: scheduled + recurring,
|
|
571
|
+
scheduledTasks: scheduled,
|
|
572
|
+
recurringTasks: recurring,
|
|
573
|
+
activeCronJobs: cronJobs,
|
|
574
|
+
statusBreakdown: statusCounts
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
}
|