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,560 @@
|
|
|
1
|
+
export class UIComponents {
|
|
2
|
+
constructor(client) {
|
|
3
|
+
this.client = client;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// ===== CAROUSEL MESSAGES =====
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Send carousel message with multiple cards
|
|
10
|
+
*/
|
|
11
|
+
async sendCarousel(chatId, cards, options = {}) {
|
|
12
|
+
try {
|
|
13
|
+
// WhatsApp doesn't support native carousels, so we create a rich text alternative
|
|
14
|
+
let carouselText = `🎠 **${options.title || 'Carousel'}**\n\n`;
|
|
15
|
+
|
|
16
|
+
cards.forEach((card, index) => {
|
|
17
|
+
carouselText += `**${index + 1}. ${card.title}**\n`;
|
|
18
|
+
if (card.subtitle) carouselText += `${card.subtitle}\n`;
|
|
19
|
+
if (card.body) carouselText += `${card.body}\n`;
|
|
20
|
+
if (card.price) carouselText += `💰 ${card.price}\n`;
|
|
21
|
+
|
|
22
|
+
// Add buttons as text options
|
|
23
|
+
if (card.buttons && card.buttons.length > 0) {
|
|
24
|
+
carouselText += `Options: `;
|
|
25
|
+
card.buttons.forEach((btn, btnIndex) => {
|
|
26
|
+
carouselText += `[${btnIndex + 1}] ${btn.title} `;
|
|
27
|
+
});
|
|
28
|
+
carouselText += `\n`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
carouselText += `\n`;
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (options.footer) {
|
|
35
|
+
carouselText += `\n_${options.footer}_`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Send with image if provided
|
|
39
|
+
if (cards[0]?.image) {
|
|
40
|
+
await this.client.socket.sendMessage(chatId, {
|
|
41
|
+
image: { url: cards[0].image },
|
|
42
|
+
caption: carouselText
|
|
43
|
+
});
|
|
44
|
+
} else {
|
|
45
|
+
await this.client.socket.sendMessage(chatId, { text: carouselText });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return { success: true, type: 'carousel', cards: cards.length };
|
|
49
|
+
|
|
50
|
+
} catch (error) {
|
|
51
|
+
// Fallback to simple text
|
|
52
|
+
const fallbackText = `📋 **${options.title || 'Options'}**\n\n` +
|
|
53
|
+
cards.map((card, i) => `${i + 1}. ${card.title}`).join('\n');
|
|
54
|
+
|
|
55
|
+
await this.client.socket.sendMessage(chatId, { text: fallbackText });
|
|
56
|
+
return { success: true, type: 'fallback', cards: cards.length };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ===== PERSISTENT MENU =====
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Set persistent menu for user
|
|
64
|
+
*/
|
|
65
|
+
async setPersistentMenu(userId, menuItems) {
|
|
66
|
+
// Store menu in user data
|
|
67
|
+
this.client.storage.write.in("ui").set(`persistentMenu.${userId}`, {
|
|
68
|
+
items: menuItems,
|
|
69
|
+
created: new Date(),
|
|
70
|
+
active: true
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Send menu as message
|
|
74
|
+
let menuText = `📋 **Hauptmenü**\n\n`;
|
|
75
|
+
menuItems.forEach((item, index) => {
|
|
76
|
+
menuText += `${item.emoji || '▫️'} **${item.title}**\n`;
|
|
77
|
+
if (item.description) menuText += ` ${item.description}\n`;
|
|
78
|
+
menuText += ` Befehl: \`${item.payload || item.command}\`\n\n`;
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
menuText += `_Verwende die Befehle oben oder tippe "menu" für dieses Menü._`;
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
await this.client.socket.sendMessage(userId, { text: menuText });
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error('❌ Fehler beim Senden des Menüs:', error);
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return { success: true, items: menuItems.length };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get persistent menu for user
|
|
95
|
+
*/
|
|
96
|
+
getPersistentMenu(userId) {
|
|
97
|
+
return this.client.storage.read.from("ui").get(`persistentMenu.${userId}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Show persistent menu
|
|
102
|
+
*/
|
|
103
|
+
async showPersistentMenu(userId) {
|
|
104
|
+
const menu = this.getPersistentMenu(userId);
|
|
105
|
+
if (!menu || !menu.active) {
|
|
106
|
+
return { success: false, error: 'No active menu found' };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return await this.setPersistentMenu(userId, menu.items);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ===== QUICK REPLIES =====
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Send message with quick reply options
|
|
116
|
+
*/
|
|
117
|
+
async sendQuickReplies(chatId, text, replies, options = {}) {
|
|
118
|
+
let message = `${text}\n\n`;
|
|
119
|
+
|
|
120
|
+
// Add quick reply options
|
|
121
|
+
message += `**Quick Replies:**\n`;
|
|
122
|
+
replies.forEach((reply, index) => {
|
|
123
|
+
const emoji = reply.emoji || `${index + 1}️⃣`;
|
|
124
|
+
message += `${emoji} ${reply.title}\n`;
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
if (options.footer) {
|
|
128
|
+
message += `\n_${options.footer}_`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
await this.client.socket.sendMessage(chatId, { text: message });
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error('❌ Fehler beim Senden der Quick Replies:', error);
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Store quick replies for processing
|
|
139
|
+
this.client.storage.write.in("ui").set(`quickReplies.${chatId}`, {
|
|
140
|
+
replies: replies,
|
|
141
|
+
timestamp: Date.now(),
|
|
142
|
+
expires: Date.now() + (options.timeout || 300000) // 5 minutes default
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
return { success: true, replies: replies.length };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Process quick reply response
|
|
150
|
+
*/
|
|
151
|
+
processQuickReply(chatId, userInput) {
|
|
152
|
+
const quickReplies = this.client.storage.read.from("ui").get(`quickReplies.${chatId}`);
|
|
153
|
+
|
|
154
|
+
if (!quickReplies || Date.now() > quickReplies.expires) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Find matching reply
|
|
159
|
+
const reply = quickReplies.replies.find((r, index) => {
|
|
160
|
+
return userInput.toLowerCase() === r.title.toLowerCase() ||
|
|
161
|
+
userInput === `${index + 1}` ||
|
|
162
|
+
userInput === r.payload;
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
if (reply) {
|
|
166
|
+
// Clean up stored replies
|
|
167
|
+
this.client.storage.delete.from("ui").key(`quickReplies.${chatId}`);
|
|
168
|
+
return reply;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ===== RICH MEDIA TEMPLATES =====
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Send product template
|
|
178
|
+
*/
|
|
179
|
+
async sendProductTemplate(chatId, product, options = {}) {
|
|
180
|
+
const template = `🛍️ **${product.name}**\n\n` +
|
|
181
|
+
`${product.description || 'No description available'}\n\n` +
|
|
182
|
+
`💰 **Price:** ${product.price} ${product.currency || 'EUR'}\n` +
|
|
183
|
+
`📦 **Stock:** ${product.inStock ? 'Available' : 'Out of Stock'}\n` +
|
|
184
|
+
`🏷️ **Category:** ${product.category || 'General'}\n\n` +
|
|
185
|
+
`**Actions:**\n` +
|
|
186
|
+
`🛒 Buy Now - \`buy ${product.id}\`\n` +
|
|
187
|
+
`ℹ️ More Info - \`info ${product.id}\`\n` +
|
|
188
|
+
`❤️ Add to Wishlist - \`wishlist ${product.id}\``;
|
|
189
|
+
|
|
190
|
+
if (product.images && product.images.length > 0) {
|
|
191
|
+
await this.client.socket.sendMessage(chatId, {
|
|
192
|
+
image: { url: product.images[0] },
|
|
193
|
+
caption: template
|
|
194
|
+
});
|
|
195
|
+
} else {
|
|
196
|
+
await this.client.socket.sendMessage(chatId, { text: template });
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return { success: true, type: 'product', productId: product.id };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Send service template
|
|
204
|
+
*/
|
|
205
|
+
async sendServiceTemplate(chatId, service, options = {}) {
|
|
206
|
+
const template = `⚙️ **${service.name}**\n\n` +
|
|
207
|
+
`${service.description || 'Professional service'}\n\n` +
|
|
208
|
+
`💰 **Price:** ${service.price} ${service.currency || 'EUR'}\n` +
|
|
209
|
+
`⏱️ **Duration:** ${service.duration || 'Varies'}\n` +
|
|
210
|
+
`📅 **Availability:** ${service.available ? 'Available' : 'Booked'}\n\n` +
|
|
211
|
+
`**Book Service:**\n` +
|
|
212
|
+
`📞 Book Now - \`book ${service.id}\`\n` +
|
|
213
|
+
`💬 Ask Questions - \`ask ${service.id}\`\n` +
|
|
214
|
+
`📋 View Details - \`details ${service.id}\``;
|
|
215
|
+
|
|
216
|
+
await this.client.socket.sendMessage(chatId, { text: template });
|
|
217
|
+
|
|
218
|
+
return { success: true, type: 'service', serviceId: service.id };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ===== INTERACTIVE FORMS =====
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Create interactive form
|
|
225
|
+
*/
|
|
226
|
+
async createForm(chatId, formConfig) {
|
|
227
|
+
const form = {
|
|
228
|
+
id: `form_${Date.now()}`,
|
|
229
|
+
chatId: chatId,
|
|
230
|
+
title: formConfig.title,
|
|
231
|
+
fields: formConfig.fields,
|
|
232
|
+
currentField: 0,
|
|
233
|
+
responses: {},
|
|
234
|
+
created: Date.now(),
|
|
235
|
+
status: 'active'
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// Store form
|
|
239
|
+
this.client.storage.write.in("ui").set(`forms.${form.id}`, form);
|
|
240
|
+
this.client.storage.write.in("ui").set(`activeForm.${chatId}`, form.id);
|
|
241
|
+
|
|
242
|
+
// Start form
|
|
243
|
+
await this.sendFormField(chatId, form, 0);
|
|
244
|
+
|
|
245
|
+
return { success: true, formId: form.id };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Send form field
|
|
250
|
+
*/
|
|
251
|
+
async sendFormField(chatId, form, fieldIndex) {
|
|
252
|
+
if (fieldIndex >= form.fields.length) {
|
|
253
|
+
return await this.completeForm(chatId, form);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const field = form.fields[fieldIndex];
|
|
257
|
+
let message = `📝 **${form.title}** (${fieldIndex + 1}/${form.fields.length})\n\n`;
|
|
258
|
+
message += `**${field.label}**\n`;
|
|
259
|
+
|
|
260
|
+
if (field.description) {
|
|
261
|
+
message += `${field.description}\n\n`;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Add field-specific instructions
|
|
265
|
+
switch (field.type) {
|
|
266
|
+
case 'text':
|
|
267
|
+
message += `✏️ Please enter your text:`;
|
|
268
|
+
break;
|
|
269
|
+
case 'number':
|
|
270
|
+
message += `🔢 Please enter a number:`;
|
|
271
|
+
break;
|
|
272
|
+
case 'email':
|
|
273
|
+
message += `📧 Please enter your email:`;
|
|
274
|
+
break;
|
|
275
|
+
case 'phone':
|
|
276
|
+
message += `📱 Please enter your phone number:`;
|
|
277
|
+
break;
|
|
278
|
+
case 'choice':
|
|
279
|
+
message += `**Choose one:**\n`;
|
|
280
|
+
field.options.forEach((option, i) => {
|
|
281
|
+
message += `${i + 1}. ${option}\n`;
|
|
282
|
+
});
|
|
283
|
+
break;
|
|
284
|
+
case 'multiple':
|
|
285
|
+
message += `**Choose multiple (separate with commas):**\n`;
|
|
286
|
+
field.options.forEach((option, i) => {
|
|
287
|
+
message += `${i + 1}. ${option}\n`;
|
|
288
|
+
});
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (field.required) {
|
|
293
|
+
message += `\n_*Required field_`;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
await this.client.socket.sendMessage(chatId, { text: message });
|
|
297
|
+
|
|
298
|
+
return { success: true, field: fieldIndex };
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Process form response
|
|
303
|
+
*/
|
|
304
|
+
async processFormResponse(chatId, userInput) {
|
|
305
|
+
const activeFormId = this.client.storage.read.from("ui").get(`activeForm.${chatId}`);
|
|
306
|
+
if (!activeFormId) return null;
|
|
307
|
+
|
|
308
|
+
const form = this.client.storage.read.from("ui").get(`forms.${activeFormId}`);
|
|
309
|
+
if (!form || form.status !== 'active') return null;
|
|
310
|
+
|
|
311
|
+
const currentField = form.fields[form.currentField];
|
|
312
|
+
let processedValue = userInput;
|
|
313
|
+
|
|
314
|
+
// Validate and process based on field type
|
|
315
|
+
switch (currentField.type) {
|
|
316
|
+
case 'number':
|
|
317
|
+
processedValue = parseFloat(userInput);
|
|
318
|
+
if (isNaN(processedValue)) {
|
|
319
|
+
await this.client.socket.sendMessage(chatId, {
|
|
320
|
+
text: '❌ Please enter a valid number.'
|
|
321
|
+
});
|
|
322
|
+
return { success: false, retry: true };
|
|
323
|
+
}
|
|
324
|
+
break;
|
|
325
|
+
|
|
326
|
+
case 'email':
|
|
327
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
328
|
+
if (!emailRegex.test(userInput)) {
|
|
329
|
+
await this.client.socket.sendMessage(chatId, {
|
|
330
|
+
text: '❌ Please enter a valid email address.'
|
|
331
|
+
});
|
|
332
|
+
return { success: false, retry: true };
|
|
333
|
+
}
|
|
334
|
+
break;
|
|
335
|
+
|
|
336
|
+
case 'choice':
|
|
337
|
+
const choiceIndex = parseInt(userInput) - 1;
|
|
338
|
+
if (choiceIndex < 0 || choiceIndex >= currentField.options.length) {
|
|
339
|
+
await this.client.socket.sendMessage(chatId, {
|
|
340
|
+
text: '❌ Please select a valid option number.'
|
|
341
|
+
});
|
|
342
|
+
return { success: false, retry: true };
|
|
343
|
+
}
|
|
344
|
+
processedValue = currentField.options[choiceIndex];
|
|
345
|
+
break;
|
|
346
|
+
|
|
347
|
+
case 'multiple':
|
|
348
|
+
const selectedIndices = userInput.split(',').map(s => parseInt(s.trim()) - 1);
|
|
349
|
+
const validSelections = selectedIndices.filter(i =>
|
|
350
|
+
i >= 0 && i < currentField.options.length
|
|
351
|
+
);
|
|
352
|
+
if (validSelections.length === 0) {
|
|
353
|
+
await this.client.socket.sendMessage(chatId, {
|
|
354
|
+
text: '❌ Please select valid option numbers.'
|
|
355
|
+
});
|
|
356
|
+
return { success: false, retry: true };
|
|
357
|
+
}
|
|
358
|
+
processedValue = validSelections.map(i => currentField.options[i]);
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Store response
|
|
363
|
+
form.responses[currentField.name] = processedValue;
|
|
364
|
+
form.currentField++;
|
|
365
|
+
|
|
366
|
+
// Update form
|
|
367
|
+
this.client.storage.write.in("ui").set(`forms.${activeFormId}`, form);
|
|
368
|
+
|
|
369
|
+
// Send next field or complete form
|
|
370
|
+
if (form.currentField < form.fields.length) {
|
|
371
|
+
await this.sendFormField(chatId, form, form.currentField);
|
|
372
|
+
return { success: true, nextField: form.currentField };
|
|
373
|
+
} else {
|
|
374
|
+
return await this.completeForm(chatId, form);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Complete form
|
|
380
|
+
*/
|
|
381
|
+
async completeForm(chatId, form) {
|
|
382
|
+
form.status = 'completed';
|
|
383
|
+
form.completed = Date.now();
|
|
384
|
+
|
|
385
|
+
// Update form
|
|
386
|
+
this.client.storage.write.in("ui").set(`forms.${form.id}`, form);
|
|
387
|
+
this.client.storage.delete.from("ui").key(`activeForm.${chatId}`);
|
|
388
|
+
|
|
389
|
+
// Send completion message
|
|
390
|
+
let completionMessage = `✅ **Form Completed: ${form.title}**\n\n`;
|
|
391
|
+
completionMessage += `**Your Responses:**\n`;
|
|
392
|
+
|
|
393
|
+
Object.entries(form.responses).forEach(([fieldName, value]) => {
|
|
394
|
+
const field = form.fields.find(f => f.name === fieldName);
|
|
395
|
+
completionMessage += `**${field.label}:** ${Array.isArray(value) ? value.join(', ') : value}\n`;
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
completionMessage += `\n_Form ID: ${form.id}_\n`;
|
|
399
|
+
completionMessage += `_Submitted: ${new Date().toLocaleString()}_`;
|
|
400
|
+
|
|
401
|
+
await this.client.socket.sendMessage(chatId, { text: completionMessage });
|
|
402
|
+
|
|
403
|
+
// Emit form completion event
|
|
404
|
+
this.client.emit('form_completed', {
|
|
405
|
+
formId: form.id,
|
|
406
|
+
chatId: chatId,
|
|
407
|
+
responses: form.responses,
|
|
408
|
+
form: form
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
return { success: true, formId: form.id, responses: form.responses };
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// ===== PROGRESS INDICATORS =====
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Send progress bar
|
|
418
|
+
*/
|
|
419
|
+
async sendProgressBar(chatId, current, total, title = "Progress") {
|
|
420
|
+
const percentage = Math.round((current / total) * 100);
|
|
421
|
+
const filledBars = Math.round((current / total) * 10);
|
|
422
|
+
const emptyBars = 10 - filledBars;
|
|
423
|
+
|
|
424
|
+
const progressBar = '█'.repeat(filledBars) + '░'.repeat(emptyBars);
|
|
425
|
+
|
|
426
|
+
const message = `📊 **${title}**\n\n` +
|
|
427
|
+
`${progressBar} ${percentage}%\n` +
|
|
428
|
+
`${current} / ${total}`;
|
|
429
|
+
|
|
430
|
+
await this.client.socket.sendMessage(chatId, { text: message });
|
|
431
|
+
|
|
432
|
+
return { success: true, percentage, current, total };
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Send loading animation with proper cleanup
|
|
437
|
+
*/
|
|
438
|
+
async sendLoadingAnimation(chatId, text = "Loading", duration = 3000) {
|
|
439
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
440
|
+
let frameIndex = 0;
|
|
441
|
+
let interval = null;
|
|
442
|
+
let isActive = true;
|
|
443
|
+
|
|
444
|
+
try {
|
|
445
|
+
const message = await this.client.socket.sendMessage(chatId, {
|
|
446
|
+
text: `${frames[frameIndex]} ${text}...`
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
// Sichere Interval-Verwaltung mit Cleanup
|
|
450
|
+
interval = setInterval(async () => {
|
|
451
|
+
if (!isActive) {
|
|
452
|
+
clearInterval(interval);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
frameIndex = (frameIndex + 1) % frames.length;
|
|
457
|
+
try {
|
|
458
|
+
await this.client.socket.sendMessage(chatId, {
|
|
459
|
+
text: `${frames[frameIndex]} ${text}...`,
|
|
460
|
+
edit: message.key
|
|
461
|
+
});
|
|
462
|
+
} catch (error) {
|
|
463
|
+
// Editing not supported, send new message
|
|
464
|
+
try {
|
|
465
|
+
await this.client.socket.sendMessage(chatId, {
|
|
466
|
+
text: `${frames[frameIndex]} ${text}...`
|
|
467
|
+
});
|
|
468
|
+
} catch (sendError) {
|
|
469
|
+
// Stop animation on send error
|
|
470
|
+
isActive = false;
|
|
471
|
+
clearInterval(interval);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}, 200);
|
|
475
|
+
|
|
476
|
+
// Garantierte Bereinigung nach Timeout
|
|
477
|
+
const cleanup = () => {
|
|
478
|
+
isActive = false;
|
|
479
|
+
if (interval) {
|
|
480
|
+
clearInterval(interval);
|
|
481
|
+
interval = null;
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
// Timeout mit garantierter Bereinigung
|
|
486
|
+
const timeoutId = setTimeout(cleanup, duration || 3000);
|
|
487
|
+
|
|
488
|
+
// Cleanup-Funktion für externe Verwendung
|
|
489
|
+
return {
|
|
490
|
+
success: true,
|
|
491
|
+
duration,
|
|
492
|
+
stop: () => {
|
|
493
|
+
clearTimeout(timeoutId);
|
|
494
|
+
cleanup();
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
} catch (error) {
|
|
499
|
+
// Bereinigung bei Fehler
|
|
500
|
+
isActive = false;
|
|
501
|
+
if (interval) {
|
|
502
|
+
clearInterval(interval);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
console.error('❌ Loading animation error:', error);
|
|
506
|
+
return { success: false, error: error.message };
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// ===== UTILITY METHODS =====
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Get UI statistics
|
|
514
|
+
*/
|
|
515
|
+
getUIStats() {
|
|
516
|
+
const forms = this.client.storage.read.from("ui").get("forms") || {};
|
|
517
|
+
const menus = this.client.storage.read.from("ui").get("persistentMenu") || {};
|
|
518
|
+
const quickReplies = this.client.storage.read.from("ui").get("quickReplies") || {};
|
|
519
|
+
|
|
520
|
+
return {
|
|
521
|
+
forms: {
|
|
522
|
+
total: Object.keys(forms).length,
|
|
523
|
+
completed: Object.values(forms).filter(f => f.status === 'completed').length,
|
|
524
|
+
active: Object.values(forms).filter(f => f.status === 'active').length
|
|
525
|
+
},
|
|
526
|
+
menus: {
|
|
527
|
+
total: Object.keys(menus).length,
|
|
528
|
+
active: Object.values(menus).filter(m => m.active).length
|
|
529
|
+
},
|
|
530
|
+
quickReplies: {
|
|
531
|
+
active: Object.keys(quickReplies).length
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Clean up expired UI elements
|
|
538
|
+
*/
|
|
539
|
+
cleanupExpiredElements() {
|
|
540
|
+
const now = Date.now();
|
|
541
|
+
|
|
542
|
+
// Clean up expired quick replies
|
|
543
|
+
const quickReplies = this.client.storage.read.from("ui").get("quickReplies") || {};
|
|
544
|
+
Object.entries(quickReplies).forEach(([chatId, data]) => {
|
|
545
|
+
if (now > data.expires) {
|
|
546
|
+
this.client.storage.delete.from("ui").key(`quickReplies.${chatId}`);
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
// Clean up old forms (older than 24 hours)
|
|
551
|
+
const forms = this.client.storage.read.from("ui").get("forms") || {};
|
|
552
|
+
Object.entries(forms).forEach(([formId, form]) => {
|
|
553
|
+
if (now - form.created > 24 * 60 * 60 * 1000) {
|
|
554
|
+
this.client.storage.delete.from("ui").key(`forms.${formId}`);
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
return { success: true, cleaned: true };
|
|
559
|
+
}
|
|
560
|
+
}
|