waengine 1.0.8 → 1.0.10

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.
@@ -0,0 +1,413 @@
1
+ // 🎨 WAEngine Sticker Creator mit Sharp Integration
2
+ import sharp from 'sharp';
3
+ import { readFileSync, writeFileSync, unlinkSync, existsSync, mkdirSync, readdirSync } from 'fs';
4
+ import { join } from 'path';
5
+ import axios from 'axios';
6
+
7
+ export class StickerCreator {
8
+ constructor(message) {
9
+ this.message = message;
10
+ this.client = message.client;
11
+ this.tempDir = './temp-stickers';
12
+
13
+ // Erstelle temp Ordner falls nicht vorhanden
14
+ this.ensureTempDir();
15
+ }
16
+
17
+ ensureTempDir() {
18
+ try {
19
+ if (!existsSync(this.tempDir)) {
20
+ mkdirSync(this.tempDir, { recursive: true });
21
+ }
22
+ } catch (error) {
23
+ console.log('⚠️ Temp-Ordner konnte nicht erstellt werden:', error.message);
24
+ }
25
+ }
26
+
27
+ get sticker() {
28
+ return {
29
+ fromMedia: async (mediaBuffer, options = {}) => {
30
+ try {
31
+ const {
32
+ pack = 'WAEngine',
33
+ author = 'Bot',
34
+ quality = 'high',
35
+ crop = true,
36
+ animated = false
37
+ } = options;
38
+
39
+ console.log('🎨 Erstelle Sticker mit Sharp...');
40
+
41
+ let processedBuffer;
42
+
43
+ if (animated) {
44
+ // Für animierte Sticker (GIF/WebP)
45
+ processedBuffer = await this.processAnimatedSticker(mediaBuffer, options);
46
+ } else {
47
+ // Für statische Sticker
48
+ processedBuffer = await this.processStaticSticker(mediaBuffer, options);
49
+ }
50
+
51
+ // Sticker-Metadaten für WhatsApp
52
+ const stickerMessage = {
53
+ sticker: processedBuffer,
54
+ mimetype: animated ? 'image/webp' : 'image/webp'
55
+ };
56
+
57
+ // Sende Sticker
58
+ const result = await this.client.socket.sendMessage(this.message.from, stickerMessage);
59
+ console.log('✅ Sticker erfolgreich gesendet');
60
+ return result;
61
+
62
+ } catch (error) {
63
+ console.error('❌ Fehler beim Erstellen des Stickers:', error);
64
+ throw new Error(`Sticker konnte nicht erstellt werden: ${error.message}`);
65
+ }
66
+ },
67
+
68
+ fromImage: async (imagePath, options = {}) => {
69
+ try {
70
+ console.log('🖼️ Erstelle Sticker aus Bild:', imagePath);
71
+
72
+ let imageBuffer;
73
+
74
+ if (typeof imagePath === 'string') {
75
+ if (imagePath.startsWith('http')) {
76
+ // URL - lade herunter
77
+ console.log('📥 Lade Bild von URL...');
78
+ const response = await axios.get(imagePath, {
79
+ responseType: 'arraybuffer',
80
+ timeout: 10000
81
+ });
82
+ imageBuffer = Buffer.from(response.data);
83
+ } else {
84
+ // Lokaler Pfad
85
+ console.log('📁 Lade lokales Bild...');
86
+ imageBuffer = readFileSync(imagePath);
87
+ }
88
+ } else {
89
+ // Bereits ein Buffer
90
+ imageBuffer = imagePath;
91
+ }
92
+
93
+ return await this.sticker.fromMedia(imageBuffer, options);
94
+
95
+ } catch (error) {
96
+ console.error('❌ Fehler beim Laden des Bildes:', error);
97
+ throw new Error(`Bild konnte nicht geladen werden: ${error.message}`);
98
+ }
99
+ },
100
+
101
+ fromVideo: async (videoPath, options = {}) => {
102
+ try {
103
+ console.log('🎥 Erstelle animierten Sticker aus Video:', videoPath);
104
+
105
+ const {
106
+ duration = 6, // Max 6 Sekunden für Sticker
107
+ fps = 15,
108
+ ...stickerOptions
109
+ } = options;
110
+
111
+ let videoBuffer;
112
+
113
+ if (typeof videoPath === 'string') {
114
+ if (videoPath.startsWith('http')) {
115
+ const response = await axios.get(videoPath, {
116
+ responseType: 'arraybuffer',
117
+ timeout: 15000
118
+ });
119
+ videoBuffer = Buffer.from(response.data);
120
+ } else {
121
+ videoBuffer = readFileSync(videoPath);
122
+ }
123
+ } else {
124
+ videoBuffer = videoPath;
125
+ }
126
+
127
+ // Video zu animiertem WebP konvertieren
128
+ const animatedOptions = {
129
+ ...stickerOptions,
130
+ animated: true,
131
+ duration: duration,
132
+ fps: fps
133
+ };
134
+
135
+ return await this.sticker.fromMedia(videoBuffer, animatedOptions);
136
+
137
+ } catch (error) {
138
+ console.error('❌ Fehler beim Erstellen des Video-Stickers:', error);
139
+ throw new Error(`Video-Sticker konnte nicht erstellt werden: ${error.message}`);
140
+ }
141
+ },
142
+
143
+ fromText: async (text, options = {}) => {
144
+ try {
145
+ console.log('📝 Erstelle Text-Sticker:', text);
146
+
147
+ const {
148
+ backgroundColor = '#FFFFFF',
149
+ textColor = '#000000',
150
+ fontSize = 64,
151
+ fontFamily = 'Arial',
152
+ width = 512,
153
+ height = 512,
154
+ padding = 50,
155
+ ...stickerOptions
156
+ } = options;
157
+
158
+ // Erstelle Text-Bild mit Sharp
159
+ const textImage = await this.createTextImage(text, {
160
+ backgroundColor,
161
+ textColor,
162
+ fontSize,
163
+ fontFamily,
164
+ width,
165
+ height,
166
+ padding
167
+ });
168
+
169
+ return await this.sticker.fromMedia(textImage, stickerOptions);
170
+
171
+ } catch (error) {
172
+ console.error('❌ Fehler beim Text-Sticker:', error);
173
+ throw new Error(`Text-Sticker konnte nicht erstellt werden: ${error.message}`);
174
+ }
175
+ },
176
+
177
+ fromUrl: async (url, options = {}) => {
178
+ try {
179
+ console.log('🌐 Erstelle Sticker aus URL:', url);
180
+
181
+ // Lade von URL mit Timeout
182
+ const response = await axios.get(url, {
183
+ responseType: 'arraybuffer',
184
+ timeout: 10000,
185
+ headers: {
186
+ 'User-Agent': 'WAEngine/1.0.8'
187
+ }
188
+ });
189
+
190
+ if (response.status !== 200) {
191
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
192
+ }
193
+
194
+ const mediaBuffer = Buffer.from(response.data);
195
+ const contentType = response.headers['content-type'];
196
+
197
+ console.log('📄 Content-Type:', contentType);
198
+
199
+ // Bestimme Typ basierend auf Content-Type
200
+ if (contentType?.includes('image/gif') || contentType?.includes('video/')) {
201
+ return await this.sticker.fromVideo(mediaBuffer, { ...options, animated: true });
202
+ } else if (contentType?.startsWith('image/')) {
203
+ return await this.sticker.fromImage(mediaBuffer, options);
204
+ } else {
205
+ // Versuche als Bild
206
+ return await this.sticker.fromMedia(mediaBuffer, options);
207
+ }
208
+
209
+ } catch (error) {
210
+ console.error('❌ Fehler beim Laden von URL:', error);
211
+ throw new Error(`URL konnte nicht geladen werden: ${error.message}`);
212
+ }
213
+ }
214
+ };
215
+ }
216
+
217
+ async processStaticSticker(imageBuffer, options = {}) {
218
+ const {
219
+ quality = 'high',
220
+ crop = true,
221
+ width = 512,
222
+ height = 512
223
+ } = options;
224
+
225
+ try {
226
+ let sharpImage = sharp(imageBuffer);
227
+
228
+ // Hole Metadaten
229
+ const metadata = await sharpImage.metadata();
230
+ console.log(`📊 Bild-Info: ${metadata.width}x${metadata.height}, Format: ${metadata.format}`);
231
+
232
+ // Resize und Crop Logic
233
+ if (crop) {
234
+ // Quadratisch croppen (zentriert)
235
+ const size = Math.min(metadata.width, metadata.height);
236
+ const left = Math.floor((metadata.width - size) / 2);
237
+ const top = Math.floor((metadata.height - size) / 2);
238
+
239
+ sharpImage = sharpImage
240
+ .extract({ left, top, width: size, height: size })
241
+ .resize(width, height, {
242
+ fit: 'cover',
243
+ position: 'center'
244
+ });
245
+ } else {
246
+ // Proportional resize
247
+ sharpImage = sharpImage.resize(width, height, {
248
+ fit: 'inside',
249
+ withoutEnlargement: false,
250
+ background: { r: 255, g: 255, b: 255, alpha: 0 }
251
+ });
252
+ }
253
+
254
+ // Qualitäts-Einstellungen
255
+ const qualitySettings = {
256
+ high: { quality: 90, effort: 6 },
257
+ medium: { quality: 75, effort: 4 },
258
+ low: { quality: 60, effort: 2 }
259
+ };
260
+
261
+ const settings = qualitySettings[quality] || qualitySettings.high;
262
+
263
+ // Zu WebP konvertieren (optimal für WhatsApp Sticker)
264
+ const processedBuffer = await sharpImage
265
+ .webp(settings)
266
+ .toBuffer();
267
+
268
+ console.log(`✅ Sticker verarbeitet: ${processedBuffer.length} bytes`);
269
+ return processedBuffer;
270
+
271
+ } catch (error) {
272
+ console.error('❌ Sharp Verarbeitungsfehler:', error);
273
+ throw error;
274
+ }
275
+ }
276
+
277
+ async processAnimatedSticker(mediaBuffer, options = {}) {
278
+ const {
279
+ quality = 'high',
280
+ width = 512,
281
+ height = 512,
282
+ duration = 6,
283
+ fps = 15
284
+ } = options;
285
+
286
+ try {
287
+ console.log('🎬 Verarbeite animierten Sticker...');
288
+
289
+ // Für animierte Sticker - vereinfachte Verarbeitung
290
+ // In einer vollständigen Implementation würdest du FFmpeg verwenden
291
+ let sharpImage = sharp(mediaBuffer, { animated: true });
292
+
293
+ const metadata = await sharpImage.metadata();
294
+ console.log(`📊 Animiertes Bild-Info: ${metadata.width}x${metadata.height}, Seiten: ${metadata.pages}`);
295
+
296
+ // Resize für animierte WebP
297
+ const processedBuffer = await sharpImage
298
+ .resize(width, height, {
299
+ fit: 'cover',
300
+ position: 'center'
301
+ })
302
+ .webp({
303
+ quality: quality === 'high' ? 80 : quality === 'medium' ? 60 : 40,
304
+ effort: 4
305
+ })
306
+ .toBuffer();
307
+
308
+ console.log(`✅ Animierter Sticker verarbeitet: ${processedBuffer.length} bytes`);
309
+ return processedBuffer;
310
+
311
+ } catch (error) {
312
+ console.error('❌ Animierter Sticker Fehler:', error);
313
+ // Fallback: Als statisches Bild verarbeiten
314
+ console.log('🔄 Fallback: Verarbeite als statisches Bild...');
315
+ return await this.processStaticSticker(mediaBuffer, options);
316
+ }
317
+ }
318
+
319
+ async createTextImage(text, options = {}) {
320
+ const {
321
+ backgroundColor = '#FFFFFF',
322
+ textColor = '#000000',
323
+ fontSize = 64,
324
+ width = 512,
325
+ height = 512,
326
+ padding = 50
327
+ } = options;
328
+
329
+ try {
330
+ console.log('🎨 Erstelle Text-Bild mit Sharp...');
331
+
332
+ // SVG für Text erstellen (Sharp unterstützt SVG-Text)
333
+ const textSvg = `
334
+ <svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
335
+ <rect width="100%" height="100%" fill="${backgroundColor}"/>
336
+ <text x="50%" y="50%"
337
+ font-family="Arial, sans-serif"
338
+ font-size="${fontSize}"
339
+ fill="${textColor}"
340
+ text-anchor="middle"
341
+ dominant-baseline="middle"
342
+ style="word-wrap: break-word;">
343
+ ${text}
344
+ </text>
345
+ </svg>
346
+ `;
347
+
348
+ // SVG zu Buffer konvertieren
349
+ const textBuffer = await sharp(Buffer.from(textSvg))
350
+ .png()
351
+ .toBuffer();
352
+
353
+ console.log('✅ Text-Bild erstellt');
354
+ return textBuffer;
355
+
356
+ } catch (error) {
357
+ console.error('❌ Text-Bild Fehler:', error);
358
+
359
+ // Fallback: Einfaches farbiges Rechteck
360
+ console.log('🔄 Fallback: Erstelle einfaches Bild...');
361
+ return await sharp({
362
+ create: {
363
+ width: width,
364
+ height: height,
365
+ channels: 4,
366
+ background: backgroundColor
367
+ }
368
+ })
369
+ .png()
370
+ .toBuffer();
371
+ }
372
+ }
373
+
374
+ // Convenience Methods
375
+ async fromMedia(mediaBuffer, options = {}) {
376
+ return await this.sticker.fromMedia(mediaBuffer, options);
377
+ }
378
+
379
+ async fromImage(imagePath, options = {}) {
380
+ return await this.sticker.fromImage(imagePath, options);
381
+ }
382
+
383
+ async fromVideo(videoPath, options = {}) {
384
+ return await this.sticker.fromVideo(videoPath, options);
385
+ }
386
+
387
+ async fromText(text, options = {}) {
388
+ return await this.sticker.fromText(text, options);
389
+ }
390
+
391
+ async fromUrl(url, options = {}) {
392
+ return await this.sticker.fromUrl(url, options);
393
+ }
394
+
395
+ // Cleanup
396
+ cleanup() {
397
+ try {
398
+ // Temp-Dateien löschen falls vorhanden
399
+ if (existsSync(this.tempDir)) {
400
+ const files = readdirSync(this.tempDir);
401
+ files.forEach(file => {
402
+ try {
403
+ unlinkSync(join(this.tempDir, file));
404
+ } catch (error) {
405
+ // Ignoriere Fehler beim Löschen
406
+ }
407
+ });
408
+ }
409
+ } catch (error) {
410
+ // Ignoriere Cleanup-Fehler
411
+ }
412
+ }
413
+ }
package/src/storage.js CHANGED
@@ -16,7 +16,7 @@ export class WAStorage {
16
16
  ensureBaseDir() {
17
17
  if (!fs.existsSync(this.baseDir)) {
18
18
  fs.mkdirSync(this.baseDir, { recursive: true });
19
- console.log(`📁 Storage Directory erstellt: ${this.baseDir}`);
19
+ // Stille Directory-Erstellung
20
20
  }
21
21
  }
22
22
 
@@ -135,7 +135,7 @@ export class WAStorage {
135
135
  const filePath = this.getFilePath(fileName);
136
136
  fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
137
137
  this.cache.set(fileName, data);
138
- console.log(`💾 Daten geschrieben: ${fileName}`);
138
+ // Stille Speicherung - keine Console-Ausgabe
139
139
  return true;
140
140
  } catch (error) {
141
141
  console.error(`❌ Fehler beim Schreiben in ${fileName}:`, error);
@@ -250,7 +250,7 @@ export class WAStorage {
250
250
  if (fs.existsSync(filePath)) {
251
251
  fs.unlinkSync(filePath);
252
252
  this.cache.delete(fileName);
253
- console.log(`🗑️ Datei gelöscht: ${fileName}`);
253
+ // Stille Löschung
254
254
  return true;
255
255
  }
256
256
 
@@ -337,7 +337,7 @@ export class WAStorage {
337
337
  // Cache leeren
338
338
  clearCache() {
339
339
  this.cache.clear();
340
- console.log('🧹 Storage Cache geleert');
340
+ // Stille Cache-Bereinigung
341
341
  }
342
342
 
343
343
  // Backup erstellen
@@ -357,6 +357,7 @@ export class WAStorage {
357
357
  backedUp++;
358
358
  }
359
359
 
360
+ // Nur bei explizitem Backup eine Meldung
360
361
  console.log(`💾 Backup erstellt: ${backedUp} Dateien in ${backupDir}`);
361
362
  return backupDir;
362
363
  } catch (error) {