Skip to content

Commit

Permalink
feat: generate avatar; cleanup bot messages
Browse files Browse the repository at this point in the history
  • Loading branch information
dantetemplar committed Nov 13, 2024
1 parent 27c7cba commit f243730
Show file tree
Hide file tree
Showing 15 changed files with 1,331 additions and 562 deletions.
9 changes: 5 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
FROM python:3.11-slim
FROM python:3.12-slim

WORKDIR /usr/src/app

COPY pyproject.toml .
COPY pyproject.toml poetry.lock ./

RUN pip install poetry && poetry config virtualenvs.create false && poetry install --no-root

COPY . .
COPY static static
COPY src src

CMD ["python", "./bot.py"]
CMD ["python", "-m", "src.bot"]
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Simplest bot for removing join/left notifications in Telegram chats. Built with

## Prerequisites

- Python 3.9 or newer
- Python 3.12 or newer
- Docker and Docker Compose (for Docker deployment)
- A Dokku server (for Dokku deployment)

Expand All @@ -26,8 +26,8 @@ Simplest bot for removing join/left notifications in Telegram chats. Built with

1. Clone this repository:

`git clone https://github.com/IgorDuino/inno-chat-cleaner.git`
`cd inno-chat-cleaner`
`git clone https://github.com/one-zero-eight/chat-helper.git`
`cd chat-helper`

2. Create a `.env` file in the root directory with your Telegram bot token:

Expand Down
38 changes: 0 additions & 38 deletions bot.py

This file was deleted.

6 changes: 6 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
services:
bot:
build: .
restart: always
environment:
TELEGRAM_API_TOKEN: $TELEGRAM_API_TOKEN
8 changes: 0 additions & 8 deletions docker-compose.yml

This file was deleted.

1,399 changes: 894 additions & 505 deletions poetry.lock

Large diffs are not rendered by default.

16 changes: 12 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
[tool.poetry]
name = "telegram-bot"
name = "chat-helper"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]
authors = ["one-zero-eight <https://t.me/one_zero_eight>"]
package-mode = false

[tool.poetry.dependencies]
python = "^3.9"
aiogram = "^2.13"
python = "^3.12"
aiogram = "^3.14.0"
numpy = "^2.1.3"
pillow = "^11.0.0"
pytest = "^8.3.3"

[tool.ruff]
line-length = 120
lint.extend-select = ["I", "UP", "PL"]

[build-system]
requires = ["poetry-core>=1.0.0"]
Expand Down
Empty file added src/__init__.py
Empty file.
84 changes: 84 additions & 0 deletions src/avatar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import numpy as np
from PIL import Image, ImageDraw, ImageFont

LOGO_IMG = np.array(Image.open("static/logo.png"))
font_big = ImageFont.truetype("static/Rubik-Bold.ttf", 124)
font_small = ImageFont.truetype("static/Rubik-Bold.ttf", 74)


def print_text(img: Image, pos: tuple[int, int], text: str, font: ImageFont, max_width: int, max_height: int) -> Image:
font_size = font.size
draw = ImageDraw.Draw(img)
current_font = ImageFont.truetype(font.path, font_size)

# Wrap text to fit within max_width and max_height
wrapped_text = text
trials = 1000
while trials:
trials -= 1
lines = []
words = wrapped_text.split()
current_line = []

for word in words:
current_line.append(word)
test_line = " ".join(current_line)
_, _, w, _ = current_font.getbbox(test_line)
if w > max_width:
# If line width exceeds max_width, start a new line
current_line.pop() # remove word causing overflow
lines.append(" ".join(current_line))
current_line = [word] # start new line with current word
# Add any remaining words in the current line to lines
if current_line:
lines.append(" ".join(current_line))

max_width_in_lines = 0

for line in lines:
_, _, w, _ = current_font.getbbox(line)
max_width_in_lines = max(w, max_width_in_lines)

# Calculate total height of the text block
_, _, _, line_height = current_font.getbbox("A")
text_height = line_height * len(lines)

if (text_height <= max_height) and (max_width_in_lines <= max_width):
# Draw the wrapped text
y_text = pos[1] - text_height // 2
for line in lines:
_, _, text_width, _ = current_font.getbbox(line)
x_text = pos[0] - text_width // 2
draw.text((x_text, y_text), line, font=current_font, fill=(255, 255, 255))
y_text += line_height
break
else:
# Reduce font size if text block is too tall
font_size -= 1
current_font = ImageFont.truetype(font.path, font_size)

return img


def generate_avatar(title: str, subtitle: str | None, color: tuple[int, int, int]) -> Image:
img = np.zeros((640, 640, 3), np.uint32) + np.array(color)

h = LOGO_IMG.shape[0]
w = LOGO_IMG.shape[1]
y0 = 105 - h // 2
x0 = (img.shape[1] - w) // 2
roi = img[y0 : y0 + h, x0 : x0 + w, :]
img[y0 : y0 + h, x0 : x0 + w, :] = roi * (255 - LOGO_IMG[:, :, :3]) // 255 + (LOGO_IMG[:, :, :3])

img = img.clip(0, 255).astype(np.uint8)

img = Image.fromarray(img)
max_width = 600
max_height = 300
if subtitle is not None:
img = print_text(img, (640 // 2, 640 // 2), title, font_big, max_width, max_height)
img = print_text(img, (640 // 2, 640 // 2 + 200), subtitle, font_small, max_width, 74)
else:
img = print_text(img, (640 // 2, 640 // 2), title, font_big, max_width, max_height)

return img
Loading

0 comments on commit f243730

Please sign in to comment.