waengine 1.0.6 → 1.0.8
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 +37 -5
- package/package.json +6 -1
- package/src/ai-integration.js +185 -0
- package/src/client.js +79 -26
- package/src/http-client.js +276 -0
- package/src/index.js +5 -0
- package/src/message.js +18 -0
- package/src/prefix-manager.js +158 -0
- package/src/scheduler.js +322 -0
- package/src/storage.js +421 -0
package/README.md
CHANGED
|
@@ -496,20 +496,52 @@ const messageTypes = await msg.stats.getMessagesByType()
|
|
|
496
496
|
|
|
497
497
|
### **Prefix Setup**
|
|
498
498
|
```javascript
|
|
499
|
-
//
|
|
499
|
+
// Global Prefix (Fallback)
|
|
500
500
|
const prefix = "!"
|
|
501
501
|
client.setPrefix(prefix)
|
|
502
502
|
|
|
503
|
-
//
|
|
503
|
+
// Chat-spezifische Prefixes (NEU in v1.0.7!)
|
|
504
|
+
client.setChatPrefix(chatId, "#") // Für spezifischen Chat
|
|
505
|
+
client.getChatPrefix(chatId) // Prefix abrufen
|
|
506
|
+
client.removeChatPrefix(chatId) // Prefix entfernen
|
|
507
|
+
|
|
508
|
+
// Commands registrieren
|
|
504
509
|
client.addCommand('help', async (msg, args) => {
|
|
505
510
|
await msg.reply('Help text')
|
|
506
511
|
})
|
|
512
|
+
```
|
|
507
513
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
514
|
+
### **Chat-spezifische Prefixes (v1.0.7)**
|
|
515
|
+
Jeder Chat/Gruppe kann einen eigenen Prefix haben:
|
|
516
|
+
|
|
517
|
+
```javascript
|
|
518
|
+
// Setprefix Command (Admin only)
|
|
519
|
+
client.addCommand('setprefix', async (msg, args) => {
|
|
520
|
+
if (msg.isGroup && !(await msg.isAdmin())) {
|
|
521
|
+
return msg.reply('❌ Nur Admins können den Prefix ändern!');
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const newPrefix = args[0];
|
|
525
|
+
client.setChatPrefix(msg.from, newPrefix);
|
|
526
|
+
await msg.reply(`✅ Prefix geändert zu: "${newPrefix}"`);
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
// Prefix Info Command
|
|
530
|
+
client.addCommand('prefixinfo', async (msg) => {
|
|
531
|
+
const chatPrefix = client.getChatPrefix(msg.from);
|
|
532
|
+
const stats = client.getPrefixStats();
|
|
533
|
+
|
|
534
|
+
await msg.reply(`🎯 Aktueller Prefix: "${chatPrefix}"\n📊 Gesamt Chats: ${stats.totalChats}`);
|
|
535
|
+
});
|
|
511
536
|
```
|
|
512
537
|
|
|
538
|
+
**Features:**
|
|
539
|
+
- ✅ **Persistent Storage** - Prefixes werden automatisch gespeichert
|
|
540
|
+
- ✅ **Admin-only** - Nur Admins können Prefixes in Gruppen ändern
|
|
541
|
+
- ✅ **Validierung** - Max 5 Zeichen, keine Leerzeichen
|
|
542
|
+
- ✅ **Statistics** - Übersicht über alle verwendeten Prefixes
|
|
543
|
+
- ✅ **Fallback** - Global Prefix als Standard
|
|
544
|
+
|
|
513
545
|
### **Command Properties**
|
|
514
546
|
```javascript
|
|
515
547
|
// In message events
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "waengine",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "🚀 WAEngine - The most powerful WhatsApp Bot Library with Multi-Device Support & EasyBot API",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -24,6 +24,9 @@
|
|
|
24
24
|
"messaging",
|
|
25
25
|
"multi-account",
|
|
26
26
|
"load-balancing",
|
|
27
|
+
"prefix-system",
|
|
28
|
+
"chat-specific",
|
|
29
|
+
"group-management",
|
|
27
30
|
"typescript",
|
|
28
31
|
"javascript",
|
|
29
32
|
"nodejs"
|
|
@@ -47,6 +50,8 @@
|
|
|
47
50
|
},
|
|
48
51
|
"dependencies": {
|
|
49
52
|
"@whiskeysockets/baileys": "^7.0.0-rc.9",
|
|
53
|
+
"axios": "^1.13.4",
|
|
54
|
+
"node-cron": "^3.0.3",
|
|
50
55
|
"pino": "^8.0.0",
|
|
51
56
|
"playwright": "^1.58.1",
|
|
52
57
|
"qrcode": "^1.5.4",
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// AI Integration für WAEngine
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
|
|
4
|
+
export class AIIntegration {
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
this.apiKey = options.apiKey || process.env.OPENAI_API_KEY;
|
|
7
|
+
this.baseURL = options.baseURL || 'https://api.openai.com/v1';
|
|
8
|
+
this.model = options.model || 'gpt-3.5-turbo';
|
|
9
|
+
this.enabled = !!this.apiKey;
|
|
10
|
+
|
|
11
|
+
if (!this.enabled) {
|
|
12
|
+
console.log('⚠️ AI Integration deaktiviert - Kein API Key gefunden');
|
|
13
|
+
} else {
|
|
14
|
+
console.log('🤖 AI Integration aktiviert');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// ===== CHAT COMPLETION =====
|
|
19
|
+
|
|
20
|
+
async chat(prompt, options = {}) {
|
|
21
|
+
if (!this.enabled) {
|
|
22
|
+
throw new Error('❌ AI Integration nicht konfiguriert! Setze OPENAI_API_KEY');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const response = await axios.post(`${this.baseURL}/chat/completions`, {
|
|
27
|
+
model: options.model || this.model,
|
|
28
|
+
messages: [
|
|
29
|
+
{
|
|
30
|
+
role: 'system',
|
|
31
|
+
content: options.systemPrompt || 'Du bist ein hilfreicher WhatsApp Bot Assistent. Antworte kurz und freundlich auf Deutsch.'
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
role: 'user',
|
|
35
|
+
content: prompt
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
max_tokens: options.maxTokens || 500,
|
|
39
|
+
temperature: options.temperature || 0.7
|
|
40
|
+
}, {
|
|
41
|
+
headers: {
|
|
42
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
43
|
+
'Content-Type': 'application/json'
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return response.data.choices[0].message.content.trim();
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error('❌ AI Chat Fehler:', error.response?.data || error.message);
|
|
50
|
+
throw new Error(`AI Fehler: ${error.response?.data?.error?.message || error.message}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ===== SMART RESPONSES =====
|
|
55
|
+
|
|
56
|
+
async smartReply(message, context = {}) {
|
|
57
|
+
const prompt = `
|
|
58
|
+
Kontext: WhatsApp Bot Nachricht
|
|
59
|
+
User schrieb: "${message}"
|
|
60
|
+
Chat-Type: ${context.isGroup ? 'Gruppe' : 'Privat'}
|
|
61
|
+
${context.userName ? `User: ${context.userName}` : ''}
|
|
62
|
+
|
|
63
|
+
Antworte natürlich und hilfreich. Maximal 2 Sätze.
|
|
64
|
+
`.trim();
|
|
65
|
+
|
|
66
|
+
return await this.chat(prompt, {
|
|
67
|
+
systemPrompt: 'Du bist ein freundlicher WhatsApp Bot. Antworte natürlich und kurz.',
|
|
68
|
+
maxTokens: 200
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ===== TEXT ANALYSIS =====
|
|
73
|
+
|
|
74
|
+
async analyzeMessage(text) {
|
|
75
|
+
const prompt = `
|
|
76
|
+
Analysiere diese Nachricht:
|
|
77
|
+
"${text}"
|
|
78
|
+
|
|
79
|
+
Gib zurück als JSON:
|
|
80
|
+
{
|
|
81
|
+
"sentiment": "positive/negative/neutral",
|
|
82
|
+
"language": "de/en/es/fr/etc",
|
|
83
|
+
"category": "question/command/greeting/complaint/other",
|
|
84
|
+
"toxicity": "low/medium/high",
|
|
85
|
+
"confidence": 0.0-1.0
|
|
86
|
+
}
|
|
87
|
+
`.trim();
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const response = await this.chat(prompt, {
|
|
91
|
+
systemPrompt: 'Du bist ein Text-Analyse-Experte. Antworte nur mit gültigem JSON.',
|
|
92
|
+
maxTokens: 150
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
return JSON.parse(response);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
return {
|
|
98
|
+
sentiment: 'neutral',
|
|
99
|
+
language: 'unknown',
|
|
100
|
+
category: 'other',
|
|
101
|
+
toxicity: 'low',
|
|
102
|
+
confidence: 0.5
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ===== MODERATION =====
|
|
108
|
+
|
|
109
|
+
async moderateContent(text) {
|
|
110
|
+
try {
|
|
111
|
+
const response = await axios.post(`${this.baseURL}/moderations`, {
|
|
112
|
+
input: text
|
|
113
|
+
}, {
|
|
114
|
+
headers: {
|
|
115
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
116
|
+
'Content-Type': 'application/json'
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const result = response.data.results[0];
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
flagged: result.flagged,
|
|
124
|
+
categories: result.categories,
|
|
125
|
+
scores: result.category_scores,
|
|
126
|
+
safe: !result.flagged
|
|
127
|
+
};
|
|
128
|
+
} catch (error) {
|
|
129
|
+
console.error('❌ Moderation Fehler:', error.message);
|
|
130
|
+
return { flagged: false, safe: true };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ===== TRANSLATION =====
|
|
135
|
+
|
|
136
|
+
async translate(text, targetLang = 'de') {
|
|
137
|
+
const prompt = `Übersetze folgenden Text nach ${targetLang}. Antworte nur mit der Übersetzung:\n\n"${text}"`;
|
|
138
|
+
|
|
139
|
+
return await this.chat(prompt, {
|
|
140
|
+
systemPrompt: 'Du bist ein professioneller Übersetzer. Antworte nur mit der Übersetzung.',
|
|
141
|
+
maxTokens: 300
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ===== SUMMARIZATION =====
|
|
146
|
+
|
|
147
|
+
async summarize(text, maxLength = 100) {
|
|
148
|
+
const prompt = `Fasse folgenden Text in maximal ${maxLength} Zeichen zusammen:\n\n"${text}"`;
|
|
149
|
+
|
|
150
|
+
return await this.chat(prompt, {
|
|
151
|
+
systemPrompt: 'Du bist ein Experte für Textzusammenfassungen. Sei präzise und kurz.',
|
|
152
|
+
maxTokens: Math.ceil(maxLength / 2)
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ===== QUESTION ANSWERING =====
|
|
157
|
+
|
|
158
|
+
async answerQuestion(question, context = '') {
|
|
159
|
+
const prompt = context ?
|
|
160
|
+
`Kontext: ${context}\n\nFrage: ${question}` :
|
|
161
|
+
question;
|
|
162
|
+
|
|
163
|
+
return await this.chat(prompt, {
|
|
164
|
+
systemPrompt: 'Du bist ein hilfreicher Assistent. Beantworte Fragen präzise und verständlich.',
|
|
165
|
+
maxTokens: 400
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ===== CREATIVE WRITING =====
|
|
170
|
+
|
|
171
|
+
async generateText(prompt, style = 'normal') {
|
|
172
|
+
const stylePrompts = {
|
|
173
|
+
funny: 'Schreibe lustig und humorvoll',
|
|
174
|
+
formal: 'Schreibe formal und professionell',
|
|
175
|
+
casual: 'Schreibe locker und umgangssprachlich',
|
|
176
|
+
creative: 'Schreibe kreativ und fantasievoll',
|
|
177
|
+
normal: 'Schreibe natürlich und verständlich'
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
return await this.chat(prompt, {
|
|
181
|
+
systemPrompt: `Du bist ein kreativer Schreiber. ${stylePrompts[style] || stylePrompts.normal}.`,
|
|
182
|
+
maxTokens: 600
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
package/src/client.js
CHANGED
|
@@ -3,6 +3,11 @@ import pino from "pino";
|
|
|
3
3
|
import { generateQRCode, closeBrowser } from "./qr.js";
|
|
4
4
|
import { Message } from "./message.js";
|
|
5
5
|
import { SessionManager } from "./session-manager.js";
|
|
6
|
+
import { PrefixManager } from "./prefix-manager.js";
|
|
7
|
+
import { getStorage } from "./storage.js";
|
|
8
|
+
import { AIIntegration } from "./ai-integration.js";
|
|
9
|
+
import { HTTPClient } from "./http-client.js";
|
|
10
|
+
import { Scheduler } from "./scheduler.js";
|
|
6
11
|
|
|
7
12
|
export class WhatsAppClient {
|
|
8
13
|
constructor(options = {}) {
|
|
@@ -24,10 +29,26 @@ export class WhatsAppClient {
|
|
|
24
29
|
// Session Manager
|
|
25
30
|
this.sessionManager = new SessionManager(this.options.authDir);
|
|
26
31
|
|
|
27
|
-
// Prefix System
|
|
28
|
-
this.prefix = null;
|
|
32
|
+
// Prefix System (erweitert für gruppen-spezifische Prefixes)
|
|
33
|
+
this.prefix = null; // Global fallback
|
|
34
|
+
this.prefixManager = new PrefixManager('./data');
|
|
29
35
|
this.commands = new Map();
|
|
30
36
|
|
|
37
|
+
// Storage System
|
|
38
|
+
this.storage = getStorage();
|
|
39
|
+
|
|
40
|
+
// AI Integration
|
|
41
|
+
this.ai = new AIIntegration(options.ai || {});
|
|
42
|
+
|
|
43
|
+
// HTTP Client
|
|
44
|
+
this.http = new HTTPClient(options.http || {});
|
|
45
|
+
|
|
46
|
+
// Scheduler System
|
|
47
|
+
this.scheduler = new Scheduler(this);
|
|
48
|
+
|
|
49
|
+
// Waiting System für Messages
|
|
50
|
+
this.waiting = this.scheduler.createWaiting();
|
|
51
|
+
|
|
31
52
|
// Deine eigenen API-Objekte
|
|
32
53
|
this.get = new GetAPI(this);
|
|
33
54
|
this.add = new AddAPI(this);
|
|
@@ -308,28 +329,32 @@ export class WhatsAppClient {
|
|
|
308
329
|
|
|
309
330
|
const messageObj = new Message(this, messageData);
|
|
310
331
|
|
|
311
|
-
// Prefix System - Command Check
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
console.log(`⚡ Command: ${this.prefix}${command}`);
|
|
317
|
-
|
|
318
|
-
messageObj.isCommand = true;
|
|
319
|
-
messageObj.command = command.toLowerCase();
|
|
320
|
-
messageObj.args = args;
|
|
321
|
-
messageObj.commandText = commandText;
|
|
322
|
-
|
|
323
|
-
// Command Event emittieren
|
|
324
|
-
this.emit('command', messageObj);
|
|
332
|
+
// Prefix System - Command Check (erweitert für Chat-spezifische Prefixes)
|
|
333
|
+
const chatPrefix = this.prefixManager.getPrefix(messageData.from);
|
|
334
|
+
|
|
335
|
+
if (messageData.text && messageData.text.startsWith(chatPrefix)) {
|
|
336
|
+
const commandData = this.prefixManager.parseCommand(messageData.text, messageData.from);
|
|
325
337
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
338
|
+
if (commandData) {
|
|
339
|
+
console.log(`⚡ Command in ${messageData.from}: ${commandData.prefix}${commandData.command}`);
|
|
340
|
+
|
|
341
|
+
messageObj.isCommand = true;
|
|
342
|
+
messageObj.command = commandData.command;
|
|
343
|
+
messageObj.args = commandData.args;
|
|
344
|
+
messageObj.commandText = commandData.commandText;
|
|
345
|
+
messageObj.prefix = commandData.prefix;
|
|
346
|
+
|
|
347
|
+
// Command Event emittieren
|
|
348
|
+
this.emit('command', messageObj);
|
|
349
|
+
|
|
350
|
+
// Spezifischen Command Handler aufrufen falls vorhanden
|
|
351
|
+
if (this.commands.has(commandData.command)) {
|
|
352
|
+
const handler = this.commands.get(commandData.command);
|
|
353
|
+
try {
|
|
354
|
+
handler(messageObj, commandData.args);
|
|
355
|
+
} catch (error) {
|
|
356
|
+
console.error(`❌ Fehler in Command '${commandData.command}':`, error);
|
|
357
|
+
}
|
|
333
358
|
}
|
|
334
359
|
}
|
|
335
360
|
} else {
|
|
@@ -424,15 +449,43 @@ export class WhatsAppClient {
|
|
|
424
449
|
};
|
|
425
450
|
}
|
|
426
451
|
|
|
427
|
-
// ===== PREFIX SYSTEM =====
|
|
452
|
+
// ===== PREFIX SYSTEM (ERWEITERT) =====
|
|
428
453
|
|
|
454
|
+
// Global Prefix (Fallback)
|
|
429
455
|
setPrefix(prefix) {
|
|
430
456
|
this.prefix = prefix;
|
|
457
|
+
this.prefixManager.defaultPrefix = prefix;
|
|
458
|
+
console.log(`🌐 Global Prefix gesetzt: "${prefix}"`);
|
|
431
459
|
return this;
|
|
432
460
|
}
|
|
433
461
|
|
|
434
|
-
getPrefix() {
|
|
435
|
-
|
|
462
|
+
getPrefix(chatId = null) {
|
|
463
|
+
if (chatId) {
|
|
464
|
+
return this.prefixManager.getPrefix(chatId);
|
|
465
|
+
}
|
|
466
|
+
return this.prefix || this.prefixManager.defaultPrefix;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Chat-spezifische Prefixes
|
|
470
|
+
setChatPrefix(chatId, prefix) {
|
|
471
|
+
return this.prefixManager.setPrefix(chatId, prefix);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
getChatPrefix(chatId) {
|
|
475
|
+
return this.prefixManager.getPrefix(chatId);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
removeChatPrefix(chatId) {
|
|
479
|
+
return this.prefixManager.removePrefix(chatId);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Prefix Statistics
|
|
483
|
+
getPrefixStats() {
|
|
484
|
+
return this.prefixManager.getStats();
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
getAllPrefixes() {
|
|
488
|
+
return this.prefixManager.getAllPrefixes();
|
|
436
489
|
}
|
|
437
490
|
|
|
438
491
|
addCommand(command, handler) {
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
// HTTP Client für WAEngine
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
|
|
4
|
+
export class HTTPClient {
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
this.timeout = options.timeout || 10000;
|
|
7
|
+
this.retries = options.retries || 3;
|
|
8
|
+
this.baseHeaders = options.headers || {};
|
|
9
|
+
|
|
10
|
+
// Axios Instance erstellen
|
|
11
|
+
this.axios = axios.create({
|
|
12
|
+
timeout: this.timeout,
|
|
13
|
+
headers: this.baseHeaders
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
console.log('🌐 HTTP Client initialisiert');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// ===== BASIC HTTP METHODS =====
|
|
20
|
+
|
|
21
|
+
async get(url, options = {}) {
|
|
22
|
+
return await this.request('GET', url, null, options);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async post(url, data = null, options = {}) {
|
|
26
|
+
return await this.request('POST', url, data, options);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async put(url, data = null, options = {}) {
|
|
30
|
+
return await this.request('PUT', url, data, options);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async delete(url, options = {}) {
|
|
34
|
+
return await this.request('DELETE', url, null, options);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ===== CORE REQUEST METHOD =====
|
|
38
|
+
|
|
39
|
+
async request(method, url, data = null, options = {}) {
|
|
40
|
+
let lastError;
|
|
41
|
+
|
|
42
|
+
for (let attempt = 1; attempt <= this.retries; attempt++) {
|
|
43
|
+
try {
|
|
44
|
+
const config = {
|
|
45
|
+
method,
|
|
46
|
+
url,
|
|
47
|
+
...options
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
if (data) {
|
|
51
|
+
config.data = data;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const response = await this.axios(config);
|
|
55
|
+
return response.data;
|
|
56
|
+
|
|
57
|
+
} catch (error) {
|
|
58
|
+
lastError = error;
|
|
59
|
+
|
|
60
|
+
if (attempt < this.retries && this.shouldRetry(error)) {
|
|
61
|
+
console.log(`🔄 HTTP Retry ${attempt}/${this.retries} für ${url}`);
|
|
62
|
+
await this.delay(1000 * attempt); // Exponential backoff
|
|
63
|
+
} else {
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
throw new Error(`HTTP Request failed: ${lastError.message}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
shouldRetry(error) {
|
|
73
|
+
// Retry bei Netzwerk-Fehlern oder 5xx Status Codes
|
|
74
|
+
return !error.response || error.response.status >= 500;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
delay(ms) {
|
|
78
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ===== API WRAPPERS =====
|
|
82
|
+
|
|
83
|
+
// Weather API
|
|
84
|
+
async getWeather(city, apiKey = process.env.WEATHER_API_KEY) {
|
|
85
|
+
if (!apiKey) {
|
|
86
|
+
throw new Error('❌ Weather API Key fehlt! Setze WEATHER_API_KEY');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const data = await this.get(`https://api.openweathermap.org/data/2.5/weather`, {
|
|
91
|
+
params: {
|
|
92
|
+
q: city,
|
|
93
|
+
appid: apiKey,
|
|
94
|
+
units: 'metric',
|
|
95
|
+
lang: 'de'
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
city: data.name,
|
|
101
|
+
country: data.sys.country,
|
|
102
|
+
temperature: Math.round(data.main.temp),
|
|
103
|
+
description: data.weather[0].description,
|
|
104
|
+
humidity: data.main.humidity,
|
|
105
|
+
windSpeed: data.wind.speed,
|
|
106
|
+
icon: data.weather[0].icon
|
|
107
|
+
};
|
|
108
|
+
} catch (error) {
|
|
109
|
+
throw new Error(`Wetter-Fehler: ${error.message}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// News API
|
|
114
|
+
async getNews(category = 'general', apiKey = process.env.NEWS_API_KEY) {
|
|
115
|
+
if (!apiKey) {
|
|
116
|
+
throw new Error('❌ News API Key fehlt! Setze NEWS_API_KEY');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const data = await this.get(`https://newsapi.org/v2/top-headlines`, {
|
|
121
|
+
params: {
|
|
122
|
+
country: 'de',
|
|
123
|
+
category: category,
|
|
124
|
+
apiKey: apiKey,
|
|
125
|
+
pageSize: 5
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return data.articles.map(article => ({
|
|
130
|
+
title: article.title,
|
|
131
|
+
description: article.description,
|
|
132
|
+
url: article.url,
|
|
133
|
+
source: article.source.name,
|
|
134
|
+
publishedAt: new Date(article.publishedAt).toLocaleDateString('de-DE')
|
|
135
|
+
}));
|
|
136
|
+
} catch (error) {
|
|
137
|
+
throw new Error(`News-Fehler: ${error.message}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Crypto Prices
|
|
142
|
+
async getCryptoPrice(symbol = 'bitcoin') {
|
|
143
|
+
try {
|
|
144
|
+
const data = await this.get(`https://api.coingecko.com/api/v3/simple/price`, {
|
|
145
|
+
params: {
|
|
146
|
+
ids: symbol,
|
|
147
|
+
vs_currencies: 'eur,usd',
|
|
148
|
+
include_24hr_change: true
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const coin = data[symbol];
|
|
153
|
+
return {
|
|
154
|
+
symbol: symbol,
|
|
155
|
+
priceEUR: coin.eur,
|
|
156
|
+
priceUSD: coin.usd,
|
|
157
|
+
change24h: coin.eur_24h_change?.toFixed(2) || 0
|
|
158
|
+
};
|
|
159
|
+
} catch (error) {
|
|
160
|
+
throw new Error(`Crypto-Fehler: ${error.message}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// QR Code Generator
|
|
165
|
+
async generateQRCode(text, size = 200) {
|
|
166
|
+
try {
|
|
167
|
+
const url = `https://api.qrserver.com/v1/create-qr-code/?size=${size}x${size}&data=${encodeURIComponent(text)}`;
|
|
168
|
+
|
|
169
|
+
// Return URL instead of downloading
|
|
170
|
+
return {
|
|
171
|
+
url: url,
|
|
172
|
+
text: text,
|
|
173
|
+
size: size
|
|
174
|
+
};
|
|
175
|
+
} catch (error) {
|
|
176
|
+
throw new Error(`QR-Code-Fehler: ${error.message}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// URL Shortener
|
|
181
|
+
async shortenUrl(longUrl, apiKey = process.env.BITLY_API_KEY) {
|
|
182
|
+
if (!apiKey) {
|
|
183
|
+
// Fallback zu kostenlosem Service
|
|
184
|
+
try {
|
|
185
|
+
const data = await this.post('https://is.gd/create.php', null, {
|
|
186
|
+
params: {
|
|
187
|
+
format: 'json',
|
|
188
|
+
url: longUrl
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
shortUrl: data.shorturl,
|
|
194
|
+
longUrl: longUrl,
|
|
195
|
+
service: 'is.gd'
|
|
196
|
+
};
|
|
197
|
+
} catch (error) {
|
|
198
|
+
throw new Error(`URL-Shortener-Fehler: ${error.message}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Bitly API
|
|
203
|
+
try {
|
|
204
|
+
const data = await this.post('https://api-ssl.bitly.com/v4/shorten', {
|
|
205
|
+
long_url: longUrl
|
|
206
|
+
}, {
|
|
207
|
+
headers: {
|
|
208
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
209
|
+
'Content-Type': 'application/json'
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
shortUrl: data.link,
|
|
215
|
+
longUrl: longUrl,
|
|
216
|
+
service: 'bitly'
|
|
217
|
+
};
|
|
218
|
+
} catch (error) {
|
|
219
|
+
throw new Error(`Bitly-Fehler: ${error.message}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Random Facts
|
|
224
|
+
async getRandomFact() {
|
|
225
|
+
try {
|
|
226
|
+
const data = await this.get('https://uselessfacts.jsph.pl/random.json?language=en');
|
|
227
|
+
return data.text;
|
|
228
|
+
} catch (error) {
|
|
229
|
+
throw new Error(`Random-Fact-Fehler: ${error.message}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Random Joke
|
|
234
|
+
async getRandomJoke() {
|
|
235
|
+
try {
|
|
236
|
+
const data = await this.get('https://official-joke-api.appspot.com/random_joke');
|
|
237
|
+
return `${data.setup}\n\n${data.punchline}`;
|
|
238
|
+
} catch (error) {
|
|
239
|
+
throw new Error(`Joke-Fehler: ${error.message}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// IP Info
|
|
244
|
+
async getIPInfo(ip = '') {
|
|
245
|
+
try {
|
|
246
|
+
const url = ip ? `http://ip-api.com/json/${ip}` : 'http://ip-api.com/json/';
|
|
247
|
+
const data = await this.get(url);
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
ip: data.query,
|
|
251
|
+
country: data.country,
|
|
252
|
+
city: data.city,
|
|
253
|
+
region: data.regionName,
|
|
254
|
+
timezone: data.timezone,
|
|
255
|
+
isp: data.isp
|
|
256
|
+
};
|
|
257
|
+
} catch (error) {
|
|
258
|
+
throw new Error(`IP-Info-Fehler: ${error.message}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Website Screenshot (via API)
|
|
263
|
+
async getWebsiteScreenshot(url, width = 1280, height = 720) {
|
|
264
|
+
try {
|
|
265
|
+
const screenshotUrl = `https://api.screenshotmachine.com/?key=${process.env.SCREENSHOT_API_KEY}&url=${encodeURIComponent(url)}&dimension=${width}x${height}`;
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
screenshotUrl: screenshotUrl,
|
|
269
|
+
originalUrl: url,
|
|
270
|
+
dimensions: `${width}x${height}`
|
|
271
|
+
};
|
|
272
|
+
} catch (error) {
|
|
273
|
+
throw new Error(`Screenshot-Fehler: ${error.message}`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|