Reaperxxxx commited on
Commit
a22b105
·
verified ·
1 Parent(s): 34086d9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +77 -107
app.py CHANGED
@@ -10,17 +10,15 @@ 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()
@@ -31,11 +29,11 @@ class TelegramChatGenerator:
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:
@@ -59,22 +57,6 @@ class TelegramChatGenerator:
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()
@@ -101,8 +83,8 @@ class TelegramChatGenerator:
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
  # Ensure coordinates are valid
@@ -114,20 +96,29 @@ class TelegramChatGenerator:
114
  if max_radius < 1:
115
  max_radius = 1
116
 
117
- # Draw shadow first (with bounds check)
118
- if shadow_offset > 0:
119
- shadow_coords = (x1 + shadow_offset, y1 + shadow_offset,
120
- x2 + shadow_offset, y2 + shadow_offset)
121
- self.draw_rounded_rectangle(draw, shadow_coords, max_radius, (0, 0, 0, 30))
 
 
 
 
 
 
 
 
 
122
 
123
  # Draw main bubble
124
  self.draw_rounded_rectangle(draw, coords, max_radius, fill)
125
 
126
- # Add subtle highlight (only if bubble is tall enough)
127
- if y2 - y1 > 4:
128
- highlight_coords = (x1, y1, x2, y1 + 2)
129
- lighter_fill = tuple(min(255, c + 20) for c in fill)
130
- self.draw_rounded_rectangle(draw, highlight_coords, max_radius, lighter_fill)
131
 
132
  def draw_rounded_rectangle(self, draw, coords, radius, fill):
133
  """Draw a perfect rounded rectangle with bounds checking"""
@@ -197,73 +188,52 @@ class TelegramChatGenerator:
197
  draw.ellipse([x, y, x + 2, y + 2], fill=color)
198
 
199
  def generate_chat_bubble(self, message, username, reply_to=None, replied_username=None):
200
- # Create square canvas
201
- img = self.create_gradient_background((self.image_size, self.image_size))
202
-
203
- # Add subtle pattern
204
- self.add_subtle_pattern(img)
205
-
206
- draw = ImageDraw.Draw(img)
207
-
208
- # Calculate bubble dimensions with proper spacing
209
- padding = 50
210
- max_bubble_width = self.image_size - (padding * 2) - 60
211
-
212
- # Ensure minimum dimensions
213
- if max_bubble_width < 200:
214
- max_bubble_width = 200
215
 
216
- # Wrap text intelligently
217
- wrapped_message = self.wrap_text(message, self.font, max_bubble_width - 60)
218
  msg_width, msg_height = self.get_text_dimensions(wrapped_message, self.font)
219
-
220
  username_width, username_height = self.get_text_dimensions(username, self.username_font)
221
 
222
- # Handle reply if present
223
  reply_height = 0
224
  wrapped_reply = ""
225
  if reply_to and replied_username:
226
- # Truncate and wrap reply
227
  truncated_reply = reply_to[:100] + "..." if len(reply_to) > 100 else reply_to
228
- wrapped_reply = self.wrap_text(truncated_reply, self.reply_font, max_bubble_width - 80)
229
  reply_username_w, reply_username_h = self.get_text_dimensions(replied_username, self.reply_font)
230
  reply_msg_w, reply_msg_h = self.get_text_dimensions(wrapped_reply, self.reply_font)
231
  reply_height = reply_username_h + reply_msg_h + 25
232
 
233
- # Calculate total bubble dimensions with minimums
234
- bubble_padding = 25
235
- min_bubble_width = 300
236
- bubble_width = max(min_bubble_width, min(max_bubble_width, msg_width + bubble_padding * 2))
237
- bubble_height = max(80, username_height + msg_height + bubble_padding * 2 + 50)
238
 
239
  if reply_to:
240
  bubble_height += reply_height + 15
241
 
242
- # Ensure bubble fits in canvas
243
- if bubble_width > self.image_size - padding:
244
- bubble_width = self.image_size - padding
245
- if bubble_height > self.image_size - padding:
246
- bubble_height = self.image_size - padding
247
-
248
- # Center the bubble vertically in the square
249
- bubble_x = max(padding, self.image_size - bubble_width - padding)
250
- bubble_y = max(padding, (self.image_size - bubble_height) // 2)
251
-
252
- # Ensure bubble coordinates are valid
253
- if bubble_x + bubble_width > self.image_size:
254
- bubble_x = self.image_size - bubble_width - 10
255
- if bubble_y + bubble_height > self.image_size:
256
- bubble_y = self.image_size - bubble_height - 10
257
-
258
- # Draw main bubble with shadow
259
- if bubble_width > 0 and bubble_height > 0:
260
- self.draw_advanced_bubble(
261
- draw,
262
- [bubble_x, bubble_y, bubble_x + bubble_width, bubble_y + bubble_height],
263
- radius=20,
264
- fill=self.bubble_color,
265
- shadow_offset=4
266
- )
267
 
268
  # Current position for content
269
  content_y = bubble_y + bubble_padding
@@ -273,7 +243,7 @@ class TelegramChatGenerator:
273
  reply_y = content_y
274
  reply_bubble_height = max(30, reply_height + 10)
275
 
276
- # Reply background (with bounds checking)
277
  reply_bubble_x1 = bubble_x + 15
278
  reply_bubble_x2 = bubble_x + bubble_width - 15
279
  reply_bubble_y1 = reply_y
@@ -287,27 +257,27 @@ class TelegramChatGenerator:
287
  fill=self.reply_bubble_color
288
  )
289
 
290
- # Blue accent line
291
  if reply_bubble_height > 10:
292
- draw.rectangle([bubble_x + 20, reply_y + 5, bubble_x + 24, reply_y + reply_bubble_height - 5],
293
  fill=self.reply_line_color)
294
 
295
  # Reply content
296
- reply_content_x = bubble_x + 35
297
- reply_content_y = reply_y + 8
298
 
299
  # Reply username
300
  if reply_content_x < bubble_x + bubble_width - 20:
301
  draw.text((reply_content_x, reply_content_y), replied_username,
302
  fill=self.reply_line_color, font=self.reply_font)
303
- reply_content_y += 20
304
 
305
  # Reply message
306
  for line in wrapped_reply.split('\n'):
307
  if reply_content_y < bubble_y + bubble_height - 30:
308
  draw.text((reply_content_x, reply_content_y), line,
309
  fill=self.reply_text_color, font=self.reply_font)
310
- reply_content_y += 20
311
 
312
  content_y += reply_bubble_height + 15
313
 
@@ -315,30 +285,30 @@ class TelegramChatGenerator:
315
  if content_y < bubble_y + bubble_height - 50:
316
  draw.text((bubble_x + bubble_padding, content_y), username,
317
  fill=self.username_color, font=self.username_font)
318
- content_y += username_height + 10
319
 
320
  # Main message text with better spacing
321
  for line in wrapped_message.split('\n'):
322
  if content_y < bubble_y + bubble_height - 40:
323
  draw.text((bubble_x + bubble_padding, content_y), line,
324
  fill=self.text_color, font=self.font)
325
- content_y += 28
326
 
327
- # Add timestamp
328
  current_time = datetime.now().strftime("%H:%M")
329
  time_bbox = self.time_font.getbbox(current_time)
330
  time_width = time_bbox[2] - time_bbox[0]
331
 
332
- timestamp_x = bubble_x + bubble_width - time_width - bubble_padding
333
- timestamp_y = bubble_y + bubble_height - 25
334
 
335
  if timestamp_x > bubble_x and timestamp_y > bubble_y:
336
  draw.text((timestamp_x, timestamp_y),
337
  current_time, fill=self.time_color, font=self.time_font)
338
 
339
- # Add checkmark (delivered status)
340
  check_x = bubble_x + bubble_width - 25
341
- check_y = bubble_y + bubble_height - 22
342
 
343
  if check_x > bubble_x and check_y > bubble_y:
344
  self.draw_checkmarks(draw, check_x, check_y)
 
10
 
11
  class TelegramChatGenerator:
12
  def __init__(self):
13
+ # Custom bubble colors
14
+ self.bubble_color = (31, 39, 69) # #1f2745
15
+ self.bubble_shadow = (20, 25, 45) # Darker shadow
16
+ self.reply_bubble_color = (25, 31, 55) # Darker for contrast
17
+ self.reply_line_color = (70, 130, 255) # Blue accent line
 
 
18
  self.text_color = (255, 255, 255)
19
+ self.reply_text_color = (160, 170, 185)
20
  self.username_color = (255, 255, 255)
21
+ self.time_color = (160, 170, 185)
22
 
23
  # Load fonts with fallbacks
24
  self.load_fonts()
 
29
  def load_fonts(self):
30
  """Load fonts with multiple fallbacks"""
31
  font_paths = [
32
+ "Roboto-Regular.ttf",
33
+ "Arial.ttf",
34
+ "Arial.ttf",
35
+ "arial.ttf",
36
+ "DejaVuSans.ttf"
37
  ]
38
 
39
  try:
 
57
  self.reply_font = ImageFont.load_default()
58
  self.time_font = ImageFont.load_default()
59
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  def wrap_text(self, text, font, max_width):
61
  """Smart text wrapping based on pixel width"""
62
  words = text.split()
 
83
 
84
  return '\n'.join(lines)
85
 
86
+ def draw_telegram_bubble(self, draw, coords, radius, fill, shadow_offset=6):
87
+ """Draw Telegram-style bubble with enhanced shadow"""
88
  x1, y1, x2, y2 = coords
89
 
90
  # Ensure coordinates are valid
 
96
  if max_radius < 1:
97
  max_radius = 1
98
 
99
+ # Draw multiple shadow layers for depth
100
+ for i in range(shadow_offset, 0, -1):
101
+ shadow_alpha = max(10, 40 - i * 5) # Varying opacity
102
+ shadow_coords = (x1 + i, y1 + i, x2 + i, y2 + i)
103
+ shadow_color = (0, 0, 0, shadow_alpha)
104
+
105
+ # Create shadow layer
106
+ shadow_layer = Image.new('RGBA', draw.im.size, (0, 0, 0, 0))
107
+ shadow_draw = ImageDraw.Draw(shadow_layer)
108
+ self.draw_rounded_rectangle(shadow_draw, shadow_coords, max_radius, shadow_color)
109
+
110
+ # Blur the shadow
111
+ shadow_layer = shadow_layer.filter(ImageFilter.GaussianBlur(radius=1))
112
+ draw.im.paste(shadow_layer, (0, 0), shadow_layer)
113
 
114
  # Draw main bubble
115
  self.draw_rounded_rectangle(draw, coords, max_radius, fill)
116
 
117
+ # Add subtle inner highlight
118
+ if y2 - y1 > 6:
119
+ highlight_coords = (x1 + 1, y1 + 1, x2 - 1, y1 + 3)
120
+ lighter_fill = tuple(min(255, c + 15) for c in fill)
121
+ self.draw_rounded_rectangle(draw, highlight_coords, max_radius - 1, lighter_fill)
122
 
123
  def draw_rounded_rectangle(self, draw, coords, radius, fill):
124
  """Draw a perfect rounded rectangle with bounds checking"""
 
188
  draw.ellipse([x, y, x + 2, y + 2], fill=color)
189
 
190
  def generate_chat_bubble(self, message, username, reply_to=None, replied_username=None):
191
+ # Calculate bubble dimensions first to determine canvas size
192
+ padding = 40
193
+ bubble_padding = 25
 
 
 
 
 
 
 
 
 
 
 
 
194
 
195
+ # Estimate text dimensions
196
+ wrapped_message = self.wrap_text(message, self.font, 500) # Max width estimate
197
  msg_width, msg_height = self.get_text_dimensions(wrapped_message, self.font)
 
198
  username_width, username_height = self.get_text_dimensions(username, self.username_font)
199
 
200
+ # Handle reply dimensions
201
  reply_height = 0
202
  wrapped_reply = ""
203
  if reply_to and replied_username:
 
204
  truncated_reply = reply_to[:100] + "..." if len(reply_to) > 100 else reply_to
205
+ wrapped_reply = self.wrap_text(truncated_reply, self.reply_font, 450)
206
  reply_username_w, reply_username_h = self.get_text_dimensions(replied_username, self.reply_font)
207
  reply_msg_w, reply_msg_h = self.get_text_dimensions(wrapped_reply, self.reply_font)
208
  reply_height = reply_username_h + reply_msg_h + 25
209
 
210
+ # Calculate bubble dimensions
211
+ bubble_width = max(300, min(600, msg_width + bubble_padding * 2 + 50))
212
+ bubble_height = max(100, username_height + msg_height + bubble_padding * 2 + 50)
 
 
213
 
214
  if reply_to:
215
  bubble_height += reply_height + 15
216
 
217
+ # Create canvas with transparent background, sized to fit bubble + padding
218
+ canvas_width = bubble_width + padding * 2
219
+ canvas_height = bubble_height + padding * 2
220
+
221
+ # Create image with transparent background
222
+ img = Image.new('RGBA', (canvas_width, canvas_height), (0, 0, 0, 0))
223
+ draw = ImageDraw.Draw(img)
224
+
225
+ # Position bubble in center of canvas
226
+ bubble_x = padding
227
+ bubble_y = padding
228
+
229
+ # Draw main bubble with enhanced shadow
230
+ self.draw_telegram_bubble(
231
+ draw,
232
+ [bubble_x, bubble_y, bubble_x + bubble_width, bubble_y + bubble_height],
233
+ radius=18,
234
+ fill=self.bubble_color,
235
+ shadow_offset=6
236
+ )
 
 
 
 
 
237
 
238
  # Current position for content
239
  content_y = bubble_y + bubble_padding
 
243
  reply_y = content_y
244
  reply_bubble_height = max(30, reply_height + 10)
245
 
246
+ # Reply background
247
  reply_bubble_x1 = bubble_x + 15
248
  reply_bubble_x2 = bubble_x + bubble_width - 15
249
  reply_bubble_y1 = reply_y
 
257
  fill=self.reply_bubble_color
258
  )
259
 
260
+ # Blue accent line (thicker and more prominent)
261
  if reply_bubble_height > 10:
262
+ draw.rectangle([bubble_x + 18, reply_y + 8, bubble_x + 22, reply_y + reply_bubble_height - 8],
263
  fill=self.reply_line_color)
264
 
265
  # Reply content
266
+ reply_content_x = bubble_x + 30
267
+ reply_content_y = reply_y + 12
268
 
269
  # Reply username
270
  if reply_content_x < bubble_x + bubble_width - 20:
271
  draw.text((reply_content_x, reply_content_y), replied_username,
272
  fill=self.reply_line_color, font=self.reply_font)
273
+ reply_content_y += 22
274
 
275
  # Reply message
276
  for line in wrapped_reply.split('\n'):
277
  if reply_content_y < bubble_y + bubble_height - 30:
278
  draw.text((reply_content_x, reply_content_y), line,
279
  fill=self.reply_text_color, font=self.reply_font)
280
+ reply_content_y += 18
281
 
282
  content_y += reply_bubble_height + 15
283
 
 
285
  if content_y < bubble_y + bubble_height - 50:
286
  draw.text((bubble_x + bubble_padding, content_y), username,
287
  fill=self.username_color, font=self.username_font)
288
+ content_y += username_height + 12
289
 
290
  # Main message text with better spacing
291
  for line in wrapped_message.split('\n'):
292
  if content_y < bubble_y + bubble_height - 40:
293
  draw.text((bubble_x + bubble_padding, content_y), line,
294
  fill=self.text_color, font=self.font)
295
+ content_y += 30
296
 
297
+ # Add timestamp in bottom right
298
  current_time = datetime.now().strftime("%H:%M")
299
  time_bbox = self.time_font.getbbox(current_time)
300
  time_width = time_bbox[2] - time_bbox[0]
301
 
302
+ timestamp_x = bubble_x + bubble_width - time_width - bubble_padding - 30
303
+ timestamp_y = bubble_y + bubble_height - 28
304
 
305
  if timestamp_x > bubble_x and timestamp_y > bubble_y:
306
  draw.text((timestamp_x, timestamp_y),
307
  current_time, fill=self.time_color, font=self.time_font)
308
 
309
+ # Add double checkmarks
310
  check_x = bubble_x + bubble_width - 25
311
+ check_y = bubble_y + bubble_height - 25
312
 
313
  if check_x > bubble_x and check_y > bubble_y:
314
  self.draw_checkmarks(draw, check_x, check_y)