waengine 1.0.7 → 1.0.9

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
+ }