Бот для создания стикеров и мемов на Python

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Сегодня мы развлечемся и напишем бота в Telegram, который будет делать мемы и стикеры из изображений, используя библиотеку Pillow.

Пример работы бота
Пример работы бота

Наш бот будет накладывать текст на фото или трансформировать фото в подобие стикера. Да, функционал самый простой, если нужно что-то более продвинутое, можно воспользоваться фотошопом или нейронками. Или разобраться глубже в том, как написать дополнительную функциональность.

Лично я использую бота, создающего стикеры и мемы, когда надо отправить что-то в чате как реакцию на происходящее с юмором, не тратя время на поиск подходящего мема.

В завершении статьи, мы развернем проект в облаке Amvera, используя git push, буквально за четыре команды в IDE.

Исходный код бота на GitHub.

Ссылка на демо работающего бота.

Создание бота, создающего стикеры и мемы

Для создания бота нам нужно перейти в BotFather и получить токен для API. Он нам потребуется позже.

Написание бота с использованием библиотеки aiogram 3

Перед тем как приступить к написанию бота, мы установим необходимые библиотеки для работы бота.

Переходим в командную строку (cmd) и прописываем команду ниже.

pip install aiogram pillow

Приступим к написанию кода.

  1. Создаём и открываем файл main.py.

  2. Импортируем библиотеки.

import logging
import asyncio
import random
import os
from aiogram import Bot, Dispatcher, Router, types
from aiogram.filters import CommandStart, StateFilter
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton
from aiogram.fsm.state import StatesGroup, State
from aiogram.fsm.context import FSMContext

from stickers import create_sticker
from mem import create_meme

Экземпляр бота выглядит следующим образом.

logging.basicConfig(level=logging.INFO)
bot = Bot(token=os.environ["TOKEN"]) # токен, который будет в качестве переменной на хостинге
dp = Dispatcher() # переменная диспетчера
router = Router() # переменная роутера


async def main():
    dp.include_router(router) # инициализация роутера
    await dp.start_polling(bot) # запуск бота

if __name__ == '__main__':
    logging.basicConfig(level=logging.INFO) # инициализация логгера
    try:
        asyncio.run(main()) # запускаем функцию main в асинхронном режиме
    except KeyboardInterrupt:
        print('Бот выключен')

Дополнительно создадим файлы mem.py, stickers.py а также шрифт для мемов, который можно будет скачать в исходнике бота на Github в конце статьи.

Мы сделали основу бота, теперь создадим команды

  1. Переходим в файл mem.py и вставляем код ниже.

import io
from PIL import Image, ImageDraw, ImageFont
from aiogram.types import BufferedInputFile


async def create_meme(photo_bytes: bytes, text: str) -> BufferedInputFile:
    try:
        image = Image.open(io.BytesIO(photo_bytes)).convert("RGB") # открываем файл изображения в байтах
    except Exception as e:
        print(f"Ошибка открытия изображения: {e}")
        return None


    draw = ImageDraw.Draw(image)
   
    # размер шрифта
    font_size = int(image.width / 10)
    try:
        font = ImageFont.truetype("fonts/font.ttf", size=font_size) # выбираем шрифт из папки fonts, присваиваем размер шрифта
    except IOError: # если шрифт не был найден
        print("Шрифт не найден. Используем шрифт по умолчанию.")
        font = ImageFont.load_default() # используем стандартный шрифт


    bbox = draw.textbbox((0, 0), text, font=font)
    text_width = bbox[2] - bbox[0]
    text_height = bbox[3] - bbox[1]


    # проверка, чтобы текст не выходил за пределы изображения
    if text_width > image.width:
        font_size = int(font_size * (image.width / text_width))
        font = ImageFont.truetype("fonts/font.ttf", size=font_size)
        bbox = draw.textbbox((0, 0), text, font=font)
        text_width = bbox[2] - bbox[0]
        text_height = bbox[3] - bbox[1]


    x = (image.width - text_width) // 2
    y = image.height - text_height - 20


    # проверка, чтобы текст не выходил за нижнюю границу изображения
    if y < 0:
        y = 10  # отступ сверху, если текст слишком большой


    draw.text((x, y), text, fill="white", font=font) # "пишем" текст на картинке в белом цвете


    meme_bytes = io.BytesIO()
    image.save(meme_bytes, format='JPEG') # сохарняем фото
    meme_bytes.seek(0)


    # конвертируем фото в байты
    meme_bytes_data = meme_bytes.getvalue()


    return BufferedInputFile(meme_bytes_data, filename='meme.jpg')

Переходим в файл stickers.py и вставляем код ниже.

from aiogram import types
from aiogram.types import BufferedInputFile
from PIL import Image
import io


async def create_sticker(photo: types.PhotoSize, bot) -> BufferedInputFile:
    photo_file = await bot.download(photo.file_id) # скачиваем фото
   
    image = Image.open(photo_file) # открываем фото


    image = image.resize((512, 512))  # размер стикера Telegram
    sticker_bytes = io.BytesIO()
    image.save(sticker_bytes, format='PNG') # сохраняем фото
    sticker_bytes.seek(0)


    sticker = BufferedInputFile(sticker_bytes.getvalue(), filename='sticker.png') # создаем стикер
    return sticker

Напишем состояния FSM

from stickers import create_sticker
from mem import create_meme


class Form(StatesGroup): # Состояния
    waiting_memphoto = State()
    waiting_stickphoto = State()
    waiting_text_for_mem = State()

Напишем переменные

choicestart = InlineKeyboardMarkup(inline_keyboard=[
    [InlineKeyboardButton(text="1️⃣ Мем из фотографии", callback_data="mem")],
    [InlineKeyboardButton(text="2️⃣ Стикер из фотографии", callback_data="sticker")],
]) # Кнопки


cancel = InlineKeyboardMarkup(inline_keyboard=[
    [InlineKeyboardButton(text="◀️ Назад", callback_data="cancel")]
])
errors_in_text = {
    "❌ Я не умею добавить текст в виде ФАЙЛА 						
Источник: https://habr.com/ru/companies/amvera/articles/884904/


Интересные статьи

Интересные статьи

Попадание внутренней корпоративной информации в чужие руки — страшный сон для любого бизнеса. Последствия таких инцидентов могут быть непредсказуемыми, от утечек данных клиентов и сопутствующих штрафо...
12 декабря 2023 года организаторы из ReedPop официально закрыли культовую выставку видеоигр Electronic Entertainment Expo (E3), которая была главным игровым мероприятием в мире больше 20 лет
Если вы никогда раньше не работали с JavaScript и читаете этот текст из десктопной версии одного из современных браузеров, то вы можете прямо сейчас написать свой первый HelloWorld-скрипт. Нужно нажат...
Есть несколько способов добавить водяной знак в Битрикс. Рассмотрим два способа.
Этот пост будет из серии, об инструментах безопасности, которые доступны в Битриксе сразу «из коробки». Перечислю их все, скажу какой инструмент в какой редакции Битрикса доступен, кратко и не очень р...