Reaperxxxx commited on
Commit
1912621
Β·
verified Β·
1 Parent(s): fc08b4e

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +399 -0
app.py ADDED
@@ -0,0 +1,399 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, send_file
2
+ from PIL import Image, ImageDraw, ImageFont, ImageFilter
3
+ import textwrap
4
+ import io
5
+ import os
6
+ import math
7
+ from datetime import datetime
8
+
9
+ app = Flask(__name__)
10
+
11
+ class TelegramChatGenerator:
12
+ def __init__(self):
13
+ # Modern Telegram colors with gradients
14
+ self.bg_gradient_start = (15, 20, 28)
15
+ self.bg_gradient_end = (22, 32, 45)
16
+ self.bubble_color = (55, 125, 255) # Telegram blue
17
+ self.bubble_shadow = (35, 90, 200) # Darker shadow
18
+ self.reply_bubble_color = (45, 48, 55)
19
+ self.reply_line_color = (55, 125, 255) # Blue accent line
20
+ self.text_color = (255, 255, 255)
21
+ self.reply_text_color = (140, 150, 165)
22
+ self.username_color = (255, 255, 255)
23
+ self.time_color = (140, 150, 165)
24
+
25
+ # Load fonts with fallbacks
26
+ self.load_fonts()
27
+
28
+ # Standard square size
29
+ self.image_size = 800
30
+
31
+ def load_fonts(self):
32
+ """Load fonts with multiple fallbacks"""
33
+ font_paths = [
34
+ "fonts/Roboto-Regular.ttf",
35
+ "fonts/Arial.ttf",
36
+ "/System/Library/Fonts/Arial.ttf",
37
+ "C:/Windows/Fonts/arial.ttf",
38
+ "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
39
+ ]
40
+
41
+ try:
42
+ # Try to load a modern font
43
+ for path in font_paths:
44
+ if os.path.exists(path):
45
+ self.font = ImageFont.truetype(path, 22)
46
+ self.username_font = ImageFont.truetype(path, 18)
47
+ self.reply_font = ImageFont.truetype(path, 16)
48
+ self.time_font = ImageFont.truetype(path, 14)
49
+ return
50
+
51
+ # Fallback to default
52
+ self.font = ImageFont.load_default()
53
+ self.username_font = ImageFont.load_default()
54
+ self.reply_font = ImageFont.load_default()
55
+ self.time_font = ImageFont.load_default()
56
+ except:
57
+ self.font = ImageFont.load_default()
58
+ self.username_font = ImageFont.load_default()
59
+ self.reply_font = ImageFont.load_default()
60
+ self.time_font = ImageFont.load_default()
61
+
62
+ def create_gradient_background(self, size):
63
+ """Create a gradient background"""
64
+ img = Image.new('RGB', size, self.bg_gradient_start)
65
+ draw = ImageDraw.Draw(img)
66
+
67
+ # Create vertical gradient
68
+ for y in range(size[1]):
69
+ ratio = y / size[1]
70
+ r = int(self.bg_gradient_start[0] * (1 - ratio) + self.bg_gradient_end[0] * ratio)
71
+ g = int(self.bg_gradient_start[1] * (1 - ratio) + self.bg_gradient_end[1] * ratio)
72
+ b = int(self.bg_gradient_start[2] * (1 - ratio) + self.bg_gradient_end[2] * ratio)
73
+
74
+ draw.line([(0, y), (size[0], y)], fill=(r, g, b))
75
+
76
+ return img
77
+
78
+ def wrap_text(self, text, font, max_width):
79
+ """Smart text wrapping based on pixel width"""
80
+ words = text.split()
81
+ lines = []
82
+ current_line = []
83
+
84
+ for word in words:
85
+ test_line = ' '.join(current_line + [word])
86
+ bbox = font.getbbox(test_line)
87
+ width = bbox[2] - bbox[0]
88
+
89
+ if width <= max_width:
90
+ current_line.append(word)
91
+ else:
92
+ if current_line:
93
+ lines.append(' '.join(current_line))
94
+ current_line = [word]
95
+ else:
96
+ # Word is too long, force break
97
+ lines.append(word)
98
+
99
+ if current_line:
100
+ lines.append(' '.join(current_line))
101
+
102
+ return '\n'.join(lines)
103
+
104
+ def draw_advanced_bubble(self, draw, coords, radius, fill, shadow_offset=3):
105
+ """Draw bubble with shadow and gradient effect"""
106
+ x1, y1, x2, y2 = coords
107
+
108
+ # Draw shadow first
109
+ shadow_coords = (x1 + shadow_offset, y1 + shadow_offset,
110
+ x2 + shadow_offset, y2 + shadow_offset)
111
+ self.draw_rounded_rectangle(draw, shadow_coords, radius, (0, 0, 0, 30))
112
+
113
+ # Draw main bubble
114
+ self.draw_rounded_rectangle(draw, coords, radius, fill)
115
+
116
+ # Add subtle highlight
117
+ highlight_coords = (x1, y1, x2, y1 + 2)
118
+ lighter_fill = tuple(min(255, c + 20) for c in fill)
119
+ self.draw_rounded_rectangle(draw, highlight_coords, radius, lighter_fill)
120
+
121
+ def draw_rounded_rectangle(self, draw, coords, radius, fill):
122
+ """Draw a perfect rounded rectangle"""
123
+ x1, y1, x2, y2 = coords
124
+
125
+ # Main rectangles
126
+ draw.rectangle([x1 + radius, y1, x2 - radius, y2], fill=fill)
127
+ draw.rectangle([x1, y1 + radius, x2, y2 - radius], fill=fill)
128
+
129
+ # Corner circles
130
+ draw.ellipse([x1, y1, x1 + 2 * radius, y1 + 2 * radius], fill=fill)
131
+ draw.ellipse([x2 - 2 * radius, y1, x2, y1 + 2 * radius], fill=fill)
132
+ draw.ellipse([x1, y2 - 2 * radius, x1 + 2 * radius, y2], fill=fill)
133
+ draw.ellipse([x2 - 2 * radius, y2 - 2 * radius, x2, y2], fill=fill)
134
+
135
+ def get_text_dimensions(self, text, font):
136
+ """Get accurate text dimensions"""
137
+ lines = text.split('\n')
138
+ max_width = 0
139
+ total_height = 0
140
+
141
+ for i, line in enumerate(lines):
142
+ bbox = font.getbbox(line)
143
+ width = bbox[2] - bbox[0]
144
+ height = bbox[3] - bbox[1]
145
+ max_width = max(max_width, width)
146
+ total_height += height
147
+
148
+ # Add line spacing except for last line
149
+ if i < len(lines) - 1:
150
+ total_height += 6
151
+
152
+ return max_width, total_height
153
+
154
+ def add_subtle_pattern(self, img):
155
+ """Add a subtle dot pattern to the background"""
156
+ draw = ImageDraw.Draw(img)
157
+ width, height = img.size
158
+
159
+ # Create subtle dot pattern
160
+ for x in range(0, width, 40):
161
+ for y in range(0, height, 40):
162
+ # Vary opacity based on position
163
+ opacity = max(5, min(15, abs(x - width//2) // 20 + abs(y - height//2) // 20))
164
+ color = (255, 255, 255, opacity)
165
+ draw.ellipse([x, y, x + 2, y + 2], fill=color)
166
+
167
+ def generate_chat_bubble(self, message, username, reply_to=None, replied_username=None):
168
+ # Create square canvas
169
+ img = self.create_gradient_background((self.image_size, self.image_size))
170
+
171
+ # Add subtle pattern
172
+ self.add_subtle_pattern(img)
173
+
174
+ draw = ImageDraw.Draw(img)
175
+
176
+ # Calculate bubble dimensions with proper spacing
177
+ padding = 50
178
+ max_bubble_width = self.image_size - (padding * 2) - 60
179
+
180
+ # Wrap text intelligently
181
+ wrapped_message = self.wrap_text(message, self.font, max_bubble_width - 60)
182
+ msg_width, msg_height = self.get_text_dimensions(wrapped_message, self.font)
183
+
184
+ username_width, username_height = self.get_text_dimensions(username, self.username_font)
185
+
186
+ # Handle reply if present
187
+ reply_height = 0
188
+ wrapped_reply = ""
189
+ if reply_to and replied_username:
190
+ # Truncate and wrap reply
191
+ truncated_reply = reply_to[:100] + "..." if len(reply_to) > 100 else reply_to
192
+ wrapped_reply = self.wrap_text(truncated_reply, self.reply_font, max_bubble_width - 80)
193
+ reply_username_w, reply_username_h = self.get_text_dimensions(replied_username, self.reply_font)
194
+ reply_msg_w, reply_msg_h = self.get_text_dimensions(wrapped_reply, self.reply_font)
195
+ reply_height = reply_username_h + reply_msg_h + 25
196
+
197
+ # Calculate total bubble dimensions
198
+ bubble_padding = 25
199
+ bubble_width = min(max_bubble_width, max(350, msg_width + bubble_padding * 2))
200
+ bubble_height = username_height + msg_height + bubble_padding * 2 + 30
201
+
202
+ if reply_to:
203
+ bubble_height += reply_height + 15
204
+
205
+ # Center the bubble vertically in the square
206
+ bubble_x = self.image_size - bubble_width - padding
207
+ bubble_y = (self.image_size - bubble_height) // 2
208
+
209
+ # Draw main bubble with shadow
210
+ self.draw_advanced_bubble(
211
+ draw,
212
+ [bubble_x, bubble_y, bubble_x + bubble_width, bubble_y + bubble_height],
213
+ radius=20,
214
+ fill=self.bubble_color,
215
+ shadow_offset=4
216
+ )
217
+
218
+ # Current position for content
219
+ content_y = bubble_y + bubble_padding
220
+
221
+ # Draw reply section if present
222
+ if reply_to and replied_username:
223
+ reply_y = content_y
224
+ reply_bubble_height = reply_height + 10
225
+
226
+ # Reply background
227
+ self.draw_rounded_rectangle(
228
+ draw,
229
+ [bubble_x + 15, reply_y, bubble_x + bubble_width - 15, reply_y + reply_bubble_height],
230
+ radius=12,
231
+ fill=self.reply_bubble_color
232
+ )
233
+
234
+ # Blue accent line
235
+ draw.rectangle([bubble_x + 20, reply_y + 5, bubble_x + 24, reply_y + reply_bubble_height - 5],
236
+ fill=self.reply_line_color)
237
+
238
+ # Reply content
239
+ reply_content_x = bubble_x + 35
240
+ reply_content_y = reply_y + 8
241
+
242
+ # Reply username
243
+ draw.text((reply_content_x, reply_content_y), replied_username,
244
+ fill=self.reply_line_color, font=self.reply_font)
245
+ reply_content_y += 20
246
+
247
+ # Reply message
248
+ for line in wrapped_reply.split('\n'):
249
+ draw.text((reply_content_x, reply_content_y), line,
250
+ fill=self.reply_text_color, font=self.reply_font)
251
+ reply_content_y += 20
252
+
253
+ content_y += reply_bubble_height + 15
254
+
255
+ # Main message username
256
+ draw.text((bubble_x + bubble_padding, content_y), username,
257
+ fill=self.username_color, font=self.username_font)
258
+ content_y += username_height + 10
259
+
260
+ # Main message text with better spacing
261
+ for line in wrapped_message.split('\n'):
262
+ draw.text((bubble_x + bubble_padding, content_y), line,
263
+ fill=self.text_color, font=self.font)
264
+ content_y += 28
265
+
266
+ # Add timestamp
267
+ current_time = datetime.now().strftime("%H:%M")
268
+ time_bbox = self.time_font.getbbox(current_time)
269
+ time_width = time_bbox[2] - time_bbox[0]
270
+
271
+ draw.text((bubble_x + bubble_width - time_width - bubble_padding,
272
+ bubble_y + bubble_height - 25),
273
+ current_time, fill=self.time_color, font=self.time_font)
274
+
275
+ # Add checkmark (delivered status)
276
+ check_x = bubble_x + bubble_width - 25
277
+ check_y = bubble_y + bubble_height - 22
278
+ self.draw_checkmarks(draw, check_x, check_y)
279
+
280
+ return img
281
+
282
+ def draw_checkmarks(self, draw, x, y):
283
+ """Draw double checkmarks for message status"""
284
+ # First checkmark
285
+ points1 = [(x-8, y), (x-5, y+3), (x-2, y-2)]
286
+ self.draw_polyline(draw, points1, self.time_color, width=2)
287
+
288
+ # Second checkmark (overlapping)
289
+ points2 = [(x-5, y), (x-2, y+3), (x+2, y-2)]
290
+ self.draw_polyline(draw, points2, self.time_color, width=2)
291
+
292
+ def draw_polyline(self, draw, points, fill, width=1):
293
+ """Draw a polyline (series of connected lines)"""
294
+ for i in range(len(points) - 1):
295
+ draw.line([points[i], points[i + 1]], fill=fill, width=width)
296
+
297
+ # Initialize generator
298
+ chat_gen = TelegramChatGenerator()
299
+
300
+ @app.route('/chat')
301
+ def generate_chat():
302
+ # Get parameters
303
+ message = request.args.get('message', 'Hello World! πŸ‘‹')
304
+ username = request.args.get('username', 'User')
305
+ reply_to = request.args.get('replyto')
306
+ replied_username = request.args.get('repliedusername')
307
+
308
+ # Generate image
309
+ img = chat_gen.generate_chat_bubble(
310
+ message=message,
311
+ username=username,
312
+ reply_to=reply_to,
313
+ replied_username=replied_username
314
+ )
315
+
316
+ # Return as PNG
317
+ img_io = io.BytesIO()
318
+ img.save(img_io, 'PNG', quality=95, optimize=True)
319
+ img_io.seek(0)
320
+
321
+ return send_file(img_io, mimetype='image/png', as_attachment=False)
322
+
323
+ @app.route('/')
324
+ def index():
325
+ return '''
326
+ <!DOCTYPE html>
327
+ <html>
328
+ <head>
329
+ <title>Telegram Chat Generator</title>
330
+ <style>
331
+ body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; background: #f5f5f5; }
332
+ .container { background: white; padding: 30px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
333
+ h1 { color: #333; text-align: center; margin-bottom: 30px; }
334
+ .example { margin: 15px 0; }
335
+ .example img { max-width: 300px; border-radius: 10px; box-shadow: 0 2px 8px rgba(0,0,0,0.2); }
336
+ .params { background: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px 0; }
337
+ a { color: #007bff; text-decoration: none; }
338
+ a:hover { text-decoration: underline; }
339
+ </style>
340
+ </head>
341
+ <body>
342
+ <div class="container">
343
+ <h1>🎨 Premium Telegram Chat Generator</h1>
344
+
345
+ <div class="params">
346
+ <h3>✨ Features:</h3>
347
+ <ul>
348
+ <li>🎨 Beautiful gradient backgrounds</li>
349
+ <li>πŸ’¬ Authentic Telegram bubble design</li>
350
+ <li>↩️ Reply message support</li>
351
+ <li>πŸ“ Perfect square format (800x800)</li>
352
+ <li>πŸ•’ Automatic timestamps</li>
353
+ <li>βœ… Read status indicators</li>
354
+ <li>πŸ“± Smart text wrapping</li>
355
+ </ul>
356
+ </div>
357
+
358
+ <h3>πŸš€ Try These Examples:</h3>
359
+
360
+ <div class="example">
361
+ <h4>Simple Message:</h4>
362
+ <a href="/chat?message=Hey! How are you doing today? 😊&username=Alice">
363
+ <img src="/chat?message=Hey! How are you doing today? 😊&username=Alice" alt="Simple message example">
364
+ </a>
365
+ </div>
366
+
367
+ <div class="example">
368
+ <h4>Long Message:</h4>
369
+ <a href="/chat?message=I just wanted to let you know that the meeting has been rescheduled to tomorrow at 3 PM. Please make sure to bring all the documents we discussed earlier. Thanks!&username=John">
370
+ <img src="/chat?message=I just wanted to let you know that the meeting has been rescheduled to tomorrow at 3 PM. Please make sure to bring all the documents we discussed earlier. Thanks!&username=John" alt="Long message example">
371
+ </a>
372
+ </div>
373
+
374
+ <div class="example">
375
+ <h4>Reply Message:</h4>
376
+ <a href="/chat?message=Sounds great! I'll be there πŸ‘&username=Mike&replyto=Want to grab coffee tomorrow morning?&repliedusername=Sarah">
377
+ <img src="/chat?message=Sounds great! I'll be there πŸ‘&username=Mike&replyto=Want to grab coffee tomorrow morning?&repliedusername=Sarah" alt="Reply message example">
378
+ </a>
379
+ </div>
380
+
381
+ <div class="params">
382
+ <h3>πŸ”§ API Parameters:</h3>
383
+ <ul>
384
+ <li><code>message</code> - Your message text (supports emojis!)</li>
385
+ <li><code>username</code> - Sender's display name</li>
386
+ <li><code>replyto</code> - (Optional) Message being replied to</li>
387
+ <li><code>repliedusername</code> - (Optional) Original sender's name</li>
388
+ </ul>
389
+
390
+ <h4>Example URL:</h4>
391
+ <code>/chat?message=Hello World!&username=YourName&replyto=How are you?&repliedusername=Friend</code>
392
+ </div>
393
+ </div>
394
+ </body>
395
+ </html>
396
+ '''
397
+
398
+ if __name__ == '__main__':
399
+ app.run(debug=True, host='0.0.0.0', port=7860)