2021-12-28 в 13:13, admin , рубрики: DIY, python, telegram, Блог компании Selectel, мессенджеры, Программирование, стикеры
Все началось с одной из учебных групп в Telegram. Студенты там очень любят делать стикеры из сообщений своего преподавателя. Я выяснил, что делаются они в полуавтоматическом режиме: сообщение пересылается в бота, который рисует «пузырек» сообщения, а результат пересылается в официального стикер-бота.
Схема рабочая, но напрашивается идея минимизировать количество пересылок. Тем более, что в Telegram существуют боты, создающие пользовательские стикерпаки. Рассказываю, как сделать такого бота без лишних телодвижений, и даю свое творение на тест. Если не хотите запариваться с созданием бота, но не против запечатлеть парочку своих золотых цитат для потомков, — прошу под кат.
Концепт идеи прост: пользователь пересылает сообщение в диалог с ботом, бот создает стикер.
- никаких баз данных, даже встроенных;
- никаких промежуточных файлов, стараемся делать все в памяти.
Такой подход усложняет разработку бота, но значительно упрощает его эксплуатацию:
- вся информация хранится в Telegram, у бота нет данных — не нужно думать о резервном копировании;
- для запуска бота нужен только код и файл конфигурации;
- бот может быть запущен даже на Raspberry Pi (кстати, сервер с этим одноплатником можно получить в Selectel в течение часа).
Для разработки я выбрал язык Python версии 3.8. Сперва сделаем основу бота, которая получает сообщения и выводит доступную информацию.
Основа
Итак, регистрируем нового бота или используем старого. Все операции с ними производятся через официального BotFather. Для начала хватит идентификатора бота (username) и токена для API.
Представленный в статье код адаптирован для объяснения в контексте статьи. Ссылка на оригинальный исходный код будет в конце.
Для Bot API уже есть обертка, названная python-telegram-bot. В статье используется версия 13.4.1. Создаем простой обработчик текстовых сообщений:
def on_message_received(update: Update, context: CallbackContext): # Игнорируем все события, кроме получения сообщения if not update.message: return # Если идентификатор чата не равен идентификатору отправителя, # то бота включили в группу. Игнорируем. if update.message.chat_id != update.message.from_user.id: return # Синтетическое ограничение: хотим работать только с пересланными сбщ if not update.message.forward_from: update.message.reply_text(«Only forwarded messages supported!») return print(update.message)
Создаем бота и регистрируем обработчик.
import toml from telegram.ext import Updater, MessageHandler, Filters config = toml.load(‘dsb.toml’) bot = Updater( token=config[«telegram»][«token»] ) bot.dispatcher.add_handler( MessageHandler(Filters.update.message, on_message_received) ) bot.start_polling() bot.idle()
Теперь боту можно переслать любое сообщение, и он выведет в stdout данные, которые ему доступны.
Вывод обработчика сообщений без чувствительных данных
< ‘message_id’: 391, ‘date’: 1640260315, ‘chat’: < ‘id’: 00000001, ‘type’: ‘private’, ‘username’: ‘someone-s-username’, ‘first_name’: ‘Пример’, ‘last_name’: ‘Примерыч’ >, ‘forward_from’: < ‘id’: 0000002, ‘first_name’: ‘Иван’, ‘is_bot’: False, ‘last_name’: ‘Иваныч’, ‘username’: ‘totally-not-a-bot’, ‘language_code’: ‘en’ >, ‘forward_date’: 1640259241, ‘text’: ‘пример!’, ‘entities’: [], ‘caption_entities’: [], ‘photo’: [], ‘new_chat_members’: [], ‘new_chat_photo’: [], ‘delete_chat_photo’: False, ‘group_chat_created’: False, ‘supergroup_chat_created’: False, ‘channel_chat_created’: False, ‘from’: < ‘id’: 00000001, ‘type’: ‘private’, ‘username’: ‘someone-s-username’, ‘first_name’: ‘Пример’, ‘last_name’: ‘Примерыч’, ‘language_code’: ‘ru’ >>
В представленном выводе доступна следующая информация:
- forward_from — информация об авторе пересланного сообщения;
- text — текст пересланного сообщения.
Для того, чтобы нарисовать «пузырек» сообщения, не хватает лишь аватарки. Получаем ее парой отдельных вызовов:
# получаем первую (текущую) аватарку пользователя result = context.bot.get_user_profile_photos( update.message.forward_from.id, limit=1 ) # type: UserProfilePhotos # Обрабатываем ситуацию, когда аватарки нет, или она скрыта настройками приватности if result.total_count > 0: file = context.bot.get_file(result.photos[0][0].file_id) # type: File
Вызов get_user_profile_photos() возвращает двумерный массив записей типа File. Первое измерение задает количество аватарок у пользователя, но не больше limit. Второе измерение задает аватарку разных размеров. В нашем случае достаточно забрать первую попавшуюся картинку, но для оптимизации стоит сразу выбирать картинку подходящего разрешения.
Объект file имеет метод download_as_bytearray(), что позволяет загрузить аватарку в память без использования промежуточных файлов.
Теперь, когда есть необходимая информация, можно нарисовать «пузырек».
Рисуем стикер
Пример созданного изображения
Для рисования используем библиотеку Pillow версии 8.4.0. Шрифт — OpenSans, такой же используется в официальных приложениях Telegram.
Мессенджер накладывает ограничение на стикеры: как минимум одна сторона должна быть размером 512 пикселей. Так как мы генерируем сообщение, то можно зафиксировать ширину, а высоту рассчитывать в зависимости от количества текста.
# Импортируем шрифт, кегль 26 OPEN_SANS = ImageFont.truetype(‘OpenSans.ttf’, 26) # Разбиваем сообщение на строки из расчета, # что в одной строке не больше 30 символов text = textwrap.wrap(update.message[«text»], width=30) # Получаем высоту шрифта font_height = OPEN_SANS.getsize(text[0])[1] # Рассчитываем высоту картинки height = font_height * (len(text) + 1) + 2*BUBBLE_PADDING if height > 512: raise OverflowError(«Image too big»)
Функция textwrap.wrap() разбивает строку на массив строк, пытаясь сделать перенос по пробелам. Расчет высоты картинки прост:
- отступ от начала — BUBBLE_PADDING, в моем случае 10px;
- имя отправителя — font_height;
- сообщение — font_height * len(text);
- отступ до конца — BUBBLE_PADDING.
Если сообщение большое, то высота картинки может получиться больше 512 пикселей. В этом случае наши полномочия — лапки, выбрасываем исключение. Если размер меньше, то можем продолжать. Проверяем наличие аватарки у пользователя и адаптируем ее к нашему стикеру.
# Скачиваем аватарку как массив байт data = file.download_as_bytearray() # Класс Image из Pillow умеет читать только из потоков, # создаем виртуальный байтовый поток avatar = Image.open(BytesIO(data)) # type: Image.Image # Аватарки в Телеграме квадратные, поэтому просто масштабируем # до желаемого размера size = (AVATAR_SIZE, AVATAR_SIZE) avatar = avatar.resize(size, Image.ANTIALIAS) # Создаем круглую маску mask = Image.new(‘L’, size, 0) draw = ImageDraw.Draw(mask) draw.ellipse((0, 0) + size, fill=255) # Заполняем прозрачным по маске avatar = ImageOps.fit(avatar, mask.size, centering=(0.5, 0.5)) avatar.putalpha(mask)
Теперь у нас есть сообщение и аватарка. Создаем «холст» и начинаем рисовать. Обязательно выбираем цветовой режим RGBA и делаем прозрачный (alpha = 0) основным цветом «холста».
# Создаем изображение img = Image.new(‘RGBA’, (width, height), color=(255, 255, 255, 0)) # Создаем холст, на котором рисуем d = ImageDraw.Draw(img) # Если есть аватарка – вставляем, если нет – рисуем синий круг if avatar: img.paste(self.avatar, (0, 0)) else: d.ellipse((0, 0, AVATAR_SIZE, AVATAR_SIZE), fill=»blue») # Рисуем черный пузырек d.rounded_rectangle((BUBBLE_X_START, 0, width, height), fill=»black», radius=BUBBLE_RADIUS) # Первая строка – розовый заголовок, имя d.text( (TEXT_X_START, BUBBLE_PADDING), update.message.forward_from.first_name, fill=»pink», font=OPEN_SANS ) # Вторая и последующие строки – текст сообщения offset = BUBBLE_PADDING + font_height for line in self._text: d.text((TEXT_X_START, offset), line, fill=»white», font=OPEN_SANS) offset += font_height
Финальный штрих — сохранить изображение. Так как мы все держим в памяти, то сохраняем также в виртуальный байтовый поток.
sticker = BytesIO() # Для прозрачности сохраняем в PNG img.save(sticker, ‘PNG’) # Отматываем поток на начало, чтобы из него можно было считать sticker.seek(0);
Осталось совсем немного: загрузить стикер в Telegram и передать его пользователю.
Заполнение набора стикеров
Те, кто создавал собственные наборы, знают, что для всех операций со стикерами необходимо обращаться к боту Stickers. Однако, в Bot API есть набор вызовов для взаимодействия со стикерами, в том числе функция создания набора. Созданный ботом набор стикеров имеет следующие особенности:
- уникальное имя набора (используется в ссылках вида https://t.me/addstickers/ ) обязательно должно заканчиваться на _by_%BOT_USERNAME%;
- набор стикеров принадлежит пользователю и может быть отредактирован через бота Stickers;
- для управления набором стикеров через бота требуется его уникальное имя и идентификатор пользователя.
Как упоминалось ранее, бот должен работать без базы данных. Таким образом, уникальное имя набора должно быть вычисляемым. Самый простой способ — использовать идентификатор пользователя в имени набора. Однако это некорректно: любой пользователь набора стикеров может «вычислить» автора.
Имя бота в уникальном имени набора неявно используется для аутентификации действий бота. Так, зная идентификатор пользователя, произвести деструктивные действия с набором не получится.
Эта «особенность» исправляется хэшированием. Мне показалось подходящим использовать UUIDv5, который использует SHA-1 для хэширования. Правда, UUIDv5 не соответствует сразу двум ограничениям Telegram:
- может начинаться с цифры;
- имеет запрещенные символы — дефисы.
Первая проблема решается префиксом, а вторая — удалением запрещенных символов. Таким образом, UUIDv5 от идентификатора пользователя — отличное вычисляемое решение. А чтобы усложнить угадывание автора, можно добавить «соль» к идентификатору.
# id пользователя + соль sid = f»-» # Генерируем uuidv5 и конвертируем в строку uid = str(uuid.uuid5(uuid.NAMESPACE_X500, sid)) # Удаляем дефисы uid = uid.replace(«-«, «») # В качестве буквенного префикса используем s sticker_set_name = f»s_by_»
Теперь у нас все есть, создаем набор с первым стикером.
context.bot.add_sticker_to_set( user_id=update.message.from_user.id, name=sticker_set_name, emojis=DEFAULT_EMOJI, png_sticker=bio )
Если функция вернула True, то стикерпак создан. Если мы хотим добавить еще один стикер, то сперва набор нужно найти.
# get_sticker_set выбросит исключение, если набора нет. # Это можно использовать для определения, когда нужно создать набор. sticker_set = context.bot.get_sticker_set(sticker_set_name) # type: StickerSet # Наборы ограничены по 120 стикеров if len(sticker_set.stickers) >= 120: update.message.reply_text(«Sticker set is full») return # Добавляем! context.bot.add_sticker_to_set( user_id=update.message.from_user.id, name=sticker_set_name, emojis=DEFAULT_EMOJI, png_sticker=bio )
Стикеры добавляются в набор мгновенно, но у пользователей отображаются в течение нескольких часов. Наиболее оперативный способ обновить набор — удалить из сохраненных наборов и добавить заново.
В качестве ответа бот будет отправлять только что добавленный стикер, подтверждая, что он действительно загружен.
sticker_set = context.bot.get_sticker_set(sticker_set_name) # type: StickerSet update.message.reply_sticker(sticker_set.stickers[-1])
Вот и все, бот готов.
Конечно, это далеко не продуктовый вариант, так как Emoji не поддерживаются, существует ограничение на 120 стикеров на человека и совершенно нет кастомизации сообщений. Но для начала сойдет.
Заключение
Еще один маленький шажок для автоматизации рутинных процессов. Генерация стикеров — не самый популярный случай, но, если вдруг захочется автоматизировать, теперь вы знаете как.
Для быстрого тестирования можете использовать моего бота: ohmyquotebot (если что, он не будет жить вечно). Бот не отвечает на команду /start, так что не волнуйтесь и просто пересылайте ему сообщение, из которого хотите сделать стикер.
Исходный код доступен на GitHub.
Источник: www.pvsm.ru
Какой бот делает мемы в Телеграме? Как его найти?
Какой бот делает мемы в Телеграме? В мессенджере появился некий бот, который вставляет рандомные надписи к картинкам. Вот так и получаются мемы. Как его найти?
Ты меня не тролль
Разработчики во главе с программистом Александром Чезгановым хотят сохранить “частичку постироничной культуры ушедших 2010-х годов”.
Как уверяют создатели, довольно часто созданные мемы могут быть глупыми и странными. Ой, да у нас с вами вся жизнь такая. Так что этот бот должен зайти большинству любителей трешёвого юмора!
В конце концов… Мы деньги за интернет платим, чтобы тут деградировать и орать над мемасами и тупыми видосиками!
Функции
Что умеет бот? Давайте посмотрим.
- Бот делает мемы в Телеграме. Нужно нажать на вкладку “Создать свой мем”. А затем прислать нужное фото и надпись. И через несколько секунд бот пришлёт готовый мем.
- Также вы можете предложить боту свою подпись.
- А еще можете пообщаться в чате с другими пользователями.
Сорви овации
Будьте самыми стильными! Используйте суперэффекты:
- Фильтры и маски для ТикТока.
- Бьюти приложения для видео.
- Красивые шрифты.
- Видеоредакторы.
- Убрать предмет с фото.
- Flat Lay раскладка.
- Оживить фото.
- Соединить 2 фото в 1 лицо.
- Акварельные мазки.
- Надпись в круге на фото.
- Изменить цвет предмета на фото.
- Топ-20 лучших фильтров VSCO.
- Рамки (большая коллекция).
- Двойная экспозиция.
- Снимки в стиле Полароид.
Еще куча лайфхаков, секретов обработки, лучших фильтров и необычных масок. А также о том, как сделать радугу на лице, наложить текст, правильно разложить раскладку на фото, описано в сборной большой статье. Кликайте на картинку!
Теперь вы знаете, что за бот делает мемы в Телеграме. Пользуйтесь этим классным ботом для того, чтобы выразить всю абсурдность произошедшей ситуации. Ну или просто поугарать.
Спасибо за ваше потраченное время
Источник: kikimoraki.ru
Как создать бота для Telegram | Бот отправляющий изображения котов
Боты для телеграмма сейчас весьма популярны, давайте и мы научимся делать своих.
ЯП который мы будем использовать, как всегда Python. Напишем простого бота, который по заданной команде будет присылать нам котиков, а еще добавим кнопку, чтобы не вводить команды каждый раз. Вот рабочий пример (надеюсь к моменту твоего прочтения, я его еще буду хостить).
И итог того, что должно получиться:
Подготовка
Для начала мы будем исходить из того, что Python3 уже установлен и из командной строки прекрасно работает >_ python и >_ pip. Сидим мы из под Linux или Windows неважно.
Для работы с телеграмом будем использовать эту библиотеку, для установки необходима командная строка с правами администратора.
И ввод одной команды.
pip install python-telegram-bot —upgrade
pip install requests
Не забудем и про добавление команды
Пишем код
В принципе, многое уже и так есть в примерах в репозитории и заново ничего придумывать не надо, мы лишь изменим и дополним то, что уже есть.
И так поехали, берем из примеров простого бота echobot2, который уже умеет отвечать на все сообщения и знает команды /help /start. Отвечалку на все сообщения мы изменим, команды тоже немного поменяем и добавим свою /cat, которая собственно и будет постить котиков :3.
Для начала подставляем ранее полученный токен, необходимый для работы.
Смотрим дальше и видим, что все обработчики в боте (команды, текст и тд) находятся в функции main и добавляются как dp.add_handler
Как вы можете заметить, событие использования команды в чате состоит из двух частей: текст команд и функции, которая вызывается при ее использовании.
По тому же принципу добавим свою команду строкой
dp.add_handler(CommandHandler(«cat», sendcat)), где cat — это команда , sendcat — вызываемая функция при получение данной команды.
Остальные хендлеры трогать не будем, а поменяем лишь вызываемые ими функции.
Так например по умолчанию функция echo отправляет пользователю тот же самый текст что он и прислал.
Изменим отправляемый текст на сообщение об ошибке
Также изменим отправляемый текст на /start и /help
Украшательства закончены. Приступим к котикам :).
Котики у нас будут состоять из двух функций. Первую вы уже видели в хендлере — sendcat, она будет отвечать за отправку, но встает вопрос, а что же нам собственно отправлять? Тут мы будем использовать вторую функцию getcat которая будет отвечает за предоставление ссылок на картинки с котиками, брать которые мы будет с сайта thecatapi. Его апи позволяет получать ссылки на случайные картинки с котами, чем мы и воспользуемся.
Подключаем библиотеку requests и пишим простенькую функцию выдающию ссылки на картинки с котиками. Потом просто вызываем ее в sendcat.
Проверим что получилос. Запускаем наш скрипт, ищем в телеграмме бота по нику, который вы задали ему при создании и. Котики .
Уже неплохо, но что-то все равно не то. Мы отправляем котов не как картинки, а как ссылки, телеграм конечно их подгружает и показывает, но решение так себе. Исправим же это, открываем документацию по работе с апи телеграма для ботов и ищем метод отправки картинок.
Возвращаемся обратно и в функции sendcat заменяем всю строку отправки сообщения на bot.sendPhoto(chat_id=update.message.chat_id, photo=getcat())
Почти все готово, осталось сделать кнопку в чате, чтобы не вписывать постоянно команду вручную.
Использовать мы будем Inline клавиатуру (прямо в чате которая), она состоит из двух функций: первая отвечает за отрисовку (markup), вторая за обработку нажатий (callback).
Снова идем в примеры, которые даются авторами библиотеки, вытаскиваем вот это и переделываем под себя.
Кое-что доподключаем:
В итоге получается
Исходный код
import requests from telegram import InlineKeyboardButton, InlineKeyboardMarkup, ReplyKeyboardRemove, ReplyKeyboardMarkup, KeyboardButton from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, CallbackQueryHandler import logging # Enable logging logging.basicConfig(format=’%(asctime)s — %(name)s — %(levelname)s — %(message)s’, level=logging.INFO) logger = logging.getLogger(__name__) # Define a few command handlers. These usually take the two arguments bot and # update. Error handlers also receive the raised TelegramError object in error. def start(bot, update): «»»Приветствие»»» update.message.reply_text(‘Привет, я бот, который очень любит котиков :3nНапиши мне /cat и я поделюсь ими с тобой’) def help(bot, update): «»»Сообщение для помощи с командами»»» update.message.reply_text(‘Чтобы получить котика напиши /cat’) def echo(bot, update): «»»На любой текст отвечаем ошибкой»»» update.message.reply_text(«Неизвестная команда :(«) def error(bot, update, error): «»»Log Errors caused by Updates.»»» logger.warning(‘Update «%s» caused error «%s»‘, update, error) def getcat(): »’Получение ссылки на картинку с котиком»’ try: r = requests.get(‘http://thecatapi.com/api/images/get?format=src’) url = r.url except: url = get_cat() print(‘Error with cat parsing’) pass return url def sendcat(bot, update): «»»Отправка котиков»»» bot.sendPhoto(chat_id=update.message.chat_id, photo=getcat(), reply_markup=draw_button()) def draw_button(): keys =[[InlineKeyboardButton(‘?Еще котика. ‘, callback_data=’1’)]] return InlineKeyboardMarkup(inline_keyboard=keys) def get_callback_from_button(bot, update): query = update.callback_query username = update.effective_user.username chat_id = query.message.chat.id message_id = query.message.message_id if int(query.data) == 1: bot.sendPhoto(photo=getcat(), chat_id=chat_id, message_id=message_id, reply_markup=draw_button()) def main(): «»»Start the bot.»»» # Create the EventHandler and pass it your bot’s token. updater = Updater(«СЮДА ВАШ ТОКЕН») # Get the dispatcher to register handlers dp = updater.dispatcher dp.add_handler(CallbackQueryHandler(get_callback_from_button)) dp.add_handler(CommandHandler(«start», start)) dp.add_handler(CommandHandler(«help», help)) dp.add_handler(CommandHandler(«cat», sendcat)) # on noncommand i.e message — echo the message on Telegram dp.add_handler(MessageHandler(Filters.text, echo)) # log all errors dp.add_error_handler(error) # Start the Bot updater.start_polling() # Run the bot until you press Ctrl-C or the process receives SIGINT, # SIGTERM or SIGABRT. This should be used most of the time, since # start_polling() is non-blocking and will stop the bot gracefully. updater.idle() if __name__ == ‘__main__’: main()
F.A.Q
- Q: Т.к бот каждый раз запрашивает картинку, а потом еще и загружает ее в мессенджер, уходит масса времени. Как ускорить выдачу ответов?
- A: Решение — использовать сервера Telegram, как кеш. Сохраняйте при отправке фото их media_id (куда нибудь в бд), чтобы потом можно было быстренько его оттуда вытянуть и отправить.
- Q: А как сделать ребут, админку, оформить тексты т.д ?
- A: Здесь есть отличные сниппеты
- Q: Как осуществить . ?
- A: Документация Telegram bot api, Вики библиотеки
Источник: liberbear.com