# Пути к файлам в контейнере import os # Базовый путь для данных BASE_DIR = os.path.dirname(os.path.abspath(__file__)) # Путь к файлу для хранения resolutions CSV_FILE = os.path.join(BASE_DIR, 'resolutions.csv') # Используем OpenSans шрифт FONT_PATH = os.path.join(BASE_DIR, 'opensans.ttf') # Создание папки для галереи GALLERY_DIR = os.path.join(BASE_DIR, 'gallery') os.makedirs(GALLERY_DIR, exist_ok=True) # Пути к шаблонам TEMPLATES = { "Еж-тян": os.path.join(BASE_DIR, "ejtan.png"), "Ежунья": os.path.join(BASE_DIR, "ejunya.png"), "Оливьешка": os.path.join(BASE_DIR, "olive.png"), "Опер-тян": os.path.join(BASE_DIR, "opertan.png"), "Фортран & Co": os.path.join(BASE_DIR, "fortranco.png"), "Фунтик": os.path.join(BASE_DIR, "funtik.png") } import streamlit as st import pandas as pd import random from datetime import datetime from PIL import Image, ImageDraw, ImageFont import shutil import base64 # Функция для инициализации CSV def initialize_csv(): df = pd.DataFrame(columns=['Resolution', 'Category', 'Mascot', 'Timestamp', 'ImagePath']) df.to_csv(CSV_FILE, index=False) # Проверка и инициализация CSV if not os.path.exists(CSV_FILE) or os.path.getsize(CSV_FILE) == 0: initialize_csv() else: try: pd.read_csv(CSV_FILE) except (pd.errors.EmptyDataError, pd.errors.ParserError): initialize_csv() # Список категорий и маскотов CATEGORIES = ["Новогоднее поздравление", "Ебать ты молодец", "Эпик фейл"] MASCOTS = ["Еж-тян", "Ежунья", "Оливьешка", "Опер-тян", "Фортран & Co", "Фунтик"] # Проверка существования файлов шаблонов def check_templates(): missing_templates = [] for name, path in TEMPLATES.items(): if not os.path.exists(path): missing_templates.append(f"{name}: {path}") return missing_templates # Проверка шрифта def check_font(): if not os.path.exists(FONT_PATH): return False return True # Пул заголовков для каждой категории title_pools = { "Новогоднее поздравление": [ "Заверяющий официальное пожелание анона", "Подтверждает новогоднее настроение", "Вручается в честь наступления очередного витка этой вселенной", "Официально разрешает радоваться Новому году", "Подтверждает наличие оливьешки", "На законное распитие всякого под бой курантов", "За сохранение новогоднего духа в условиях треда", "Выдан без очереди, потому что новый год", "Сертифицирует тёплые пожелания от анона к анону", "На выдачу новогоднего настроения в треде" ], "Ебать ты молодец": [ "Выдаётся охуенному молодцу", "Выдаётся по причине охуеть ты молодец", "Вручается за ультимативную годноту", "Вручается за запредельный уровень молодцовости", "Подтверждает факт: ты реально ебать какой молодец", "Выдан ебать какому молодцу", "За действия, от которых аноны охуели", "Официальное признание: охуенно справился", "За вклад в общее дело и локальный ахуй", "За демонстрацию силы постинга и абсолютной годноты" ], "Эпик фейл": [ "За полнейший пиздец", "Вручается тотальному серуну", "Вручается за эталонный проёб всего и сразу", "О фиксации эпик фейла", "За вклад в фонд коллективного серунства", "Подтверждает: так обосраться - это надо уметь", "За достижение дна и уверенное его пробитие", "О признании фейлом года без права апелляции", "За демонстрацию того, как делать НЕ надо", "Официальное признание постера позором Ежача" ] } # Пул комментариев для каждой категории comment_pools = { "Новогоднее поздравление": [ "Всем Ежачом верим, что так и будет!", "Зафиксировано и заверенно, теперь сбудется, инфа 146%", "Эти пожелания искренны, я гарантирую это!", "Заверено Оператором, так что точно сбудется", "Пожелания занесены в аналлы Ежача", "Ежач видел - значит так тому и быть", "Так и будет, сомневаться запрещено", "Записано, сохранено, пути назад нет", "Теперь это официальная реальность (в разработке)", "С этого момента считается пророчеством" ], "Ебать ты молодец": [ "Так держать!", "Ты заслужил уважение всех анонов", "Это было эпично!", "Такого ещё не видел никто", "Твой подвиг войдет в историю", "Оче годно, одобряемо всем тредом", "Мощно, анон", "Это был просто отвал жопы", "Восхищаемся всем Ежачом", "Это просто вау" ], "Эпик фейл": [ "Ты обосрался на глазах у всего треда", "Господи, зачем ты это сделал", "Аноны запомнят это на долгие годы", "Таких фейлов у нас ещё не было", "Даже Оператору стыдно", "Интернет всё помнит", "Аноны в ахуе", "Поздравляем, ты вошёл не в ту историю", "Ну ты и выдал", "Лучше больше не пиши" ] } # Пул нижнего текста для каждой категории signature_pools = { "Новогоднее поздравление": [ "С Новым 2026 годом, анон!", "Желаем исполнения желаний в 2026!", "До встречи в новом году!", "Пусть все обещания сбудутся!", "С наступающим, анончик!", "Новый год уже здесь!", "2026 начинается прямо сейчас!", "Береги себя в новом году!", "С теплом, от анона к анону!", "Увидимся в 2026!" ], "Ебать ты молодец": [ "Так держать и дальше!", "Продолжай в том же духе!", "Ты - легенда!", "Гордимся тобой, анон!", "За тобой будущее Ежача!", "Это было красиво!", "Ежач одобряе!", "Достойно!", "Не останавливайся!", "Уважение заслужено!" ], "Эпик фейл": [ "Позор зафиксирован", "Это не забудут", "Удалить уже не получится", "С этим тебе жить", "Фейл принят без возражений", "Не пиши больше", "Это просто пиздец", "Невероятный кринж", "Смеялись всем тредом", "Борды - это не твоё" ] } # Определение текстовых областей для каждой позиции # Формат: (x, y, max_width, line_height) text_areas = { "title": (840, 610, 800, 50), # Заголовок "content": (840, 740, 800, 40), # Основной текст "comment": (840, 1120, 800, 40), # Комментарий "signature": (1110, 1260, 600, 35) # Подпись/нижний текст } # Фиксированные размеры шрифтов font_sizes = { "title": 40, "content": 34, "comment": 34, "signature": 28 } # Цвет текста FONT_COLOR = (232, 201, 165) def wrap_text(text, font, max_width): """Разбивает текст на строки, чтобы он помещался в заданную ширину""" lines = [] # Если текст уже содержит переносы строк, обрабатываем каждую часть отдельно text_parts = text.split('\n') for part in text_parts: words = part.split() current_line = "" for word in words: # Проверяем ширину текущей строки с новым словом test_line = f"{current_line} {word}" if current_line else word test_bbox = font.getbbox(test_line) test_width = test_bbox[2] - test_bbox[0] if test_width <= max_width: current_line = test_line else: if current_line: lines.append(current_line) current_line = word if current_line: lines.append(current_line) return lines def draw_wrapped_text(draw, x, y, text, font, max_width, line_height, color): """Рисует текст с автоматическим переносом""" lines = wrap_text(text, font, max_width) for i, line in enumerate(lines): draw.text((x, y + i * line_height), line, font=font, fill=color) return len(lines) def generate_certificate_image(category, mascot, user_data): """Генерация изображения сертификата""" # Проверяем существование шаблона template_path = TEMPLATES.get(mascot) if not template_path or not os.path.exists(template_path): st.error(f"Шаблон для маскота '{mascot}' не найден!") st.info(f"Ожидаемый путь: {template_path}") return None # Проверяем существование шрифта if not os.path.exists(FONT_PATH): st.error(f"Шрифт не найден по пути: {FONT_PATH}") return None try: # Загружаем шаблон img = Image.open(template_path) draw = ImageDraw.Draw(img) # Загружаем шрифт OpenSans title_font = ImageFont.truetype(FONT_PATH, font_sizes["title"]) content_font = ImageFont.truetype(FONT_PATH, font_sizes["content"]) comment_font = ImageFont.truetype(FONT_PATH, font_sizes["comment"]) signature_font = ImageFont.truetype(FONT_PATH, font_sizes["signature"]) except Exception as e: st.error(f"Ошибка при загрузке ресурсов: {str(e)}") return None # Выбираем случайный текст из пулов title = random.choice(title_pools[category]) comment = random.choice(comment_pools[category]) signature_text = random.choice(signature_pools[category]) # Формируем основной текст в зависимости от категории if category == "Новогоднее поздравление": main_text = f"Пожелания: {user_data.get('promise', '')}" else: main_text = f"Для анона: {user_data.get('username', '')}\nПричина вручения: {user_data.get('reason', '')}" # Получаем параметры областей title_x, title_y, title_width, title_line_height = text_areas["title"] content_x, content_y, content_width, content_line_height = text_areas["content"] comment_x, comment_y, comment_width, comment_line_height = text_areas["comment"] signature_x, signature_y, signature_width, signature_line_height = text_areas["signature"] # Рисуем текст на изображении с автоматическим переносом # Заголовок draw_wrapped_text(draw, title_x, title_y, title, title_font, title_width, title_line_height, FONT_COLOR) # Основной текст draw_wrapped_text(draw, content_x, content_y, main_text, content_font, content_width, content_line_height, FONT_COLOR) # Комментарий draw_wrapped_text(draw, comment_x, comment_y, comment, comment_font, comment_width, comment_line_height, FONT_COLOR) # Подпись/нижний текст draw_wrapped_text(draw, signature_x, signature_y, signature_text, signature_font, signature_width, signature_line_height, FONT_COLOR) # Сохранение временного файла temp_file = os.path.join(BASE_DIR, 'certificate.png') try: img.save(temp_file) return temp_file except Exception as e: st.error(f"Ошибка при сохранении сертификата: {str(e)}") return None # Функция для загрузки изображения и конвертации в base64 def get_image_base64(path): if os.path.exists(path): try: with open(path, "rb") as img_file: return base64.b64encode(img_file.read()).decode() except Exception as e: st.error(f"Ошибка при чтении файла: {str(e)}") return None return None # Функция для обновления категории def update_category(new_category): """Обновляет категорию и очищает соответствующие поля""" st.session_state.category = new_category if new_category == "Новогоднее поздравление": st.session_state.username = '' st.session_state.reason = '' else: st.session_state.promise = '' # Настройка страницы st.set_page_config( page_title="Glintwine for ejchan", page_icon="🍷", layout="wide" ) # Кастомный CSS для оформления st.markdown(""" """, unsafe_allow_html=True) # Шапка с логотипом и названием сервиса logo_path = None for ext in ['.svg', '.png', '.jpg', '.jpeg', '.webp']: test_path = os.path.join(BASE_DIR, f'logo{ext}') if os.path.exists(test_path): logo_path = test_path break # Отображаем шапку st.markdown("""
Glintwine for ejchan
', unsafe_allow_html=True) st.markdown('Drug for dreams does not exists, but you can drink some glintwine. Cause why not?
', unsafe_allow_html=True) with col3: pass st.markdown("