Как создать бота в телеграмме самому python
Перейти к содержимому

Как создать бота в телеграмме самому python

  • автор:

Пишем Telegram бота на Python + хостинг на Heroku

Приветствую вас, программисты! Сегодня я расскажу вам о том, как написать бота в Telegram с нуля и запустить его на бесплатной платформе по хостингу Heroku.

На просторах интернета похожих статей я не видел, поэтому, то, что вы прочитаете в этой статье будет являться уникальным контентом.

Что мы будем использовать?

  • Библиотеку python-telegram-bot( ссылка на GitHub репозиторий ) с помощью которой мы сможем использовать Telegram Bot API
  • Visual Studio Code(VS Code) в качестве текстового редактора, где мы будем писать код
  • Для хостинга сервис — heroku.com . Вам нужно будет создать там учётную запись
  • Интерпретатор Python, который вы можете скачать здесь
  • Аккаунт в Telegram , для того, чтобы создать бота и тестировать его

ОЧЕНЬ ВАЖНО! В этом туториале я использую операционную систему Windows, поэтому некоторые моменты могут отличаться, в зависимости от вашей ОС.

Что будет делать бот?

Бот, которого мы будем писать будет помогать пользователям с изучением академических слов из теста SAT. Он будет иметь две функции:

  • Отправлять случайные слова из списка из 262 академических слов
  • Отправлять вопросы и варианты ответа для того, чтобы пользователь смог проверить своё знание академических слов

Этот бот будет полезен подписчикам моего телеграм канала @satprepare .

Этап 1. Подготовка к написанию бота

Для начала нам необходимо создать директорию(папку) с нашим проектом. В моём случае она называется SATVocabularyBot и находится на рабочем столе. Поэтому её расположение следующее: C:\Users\HP\Desktop\SATVocabularyBot

Далее заходим в эту папку в Visual Studio Code через File — Open Folder. После этого давайте сразу создадим Python файл с расширением .py в нашем проекте и назовём его main.py. Он у меня уже есть, но выглядеть это будет примерно так:

На этом же скриншоте под окном вы можете увидеть открытый терминал. Вам нужно тоже его открыть через View — Terminal. Далее, через этот терминал вам необходимо установить библиотеку python-telegram-bot. Сделать это можно написав:

pip install python-telegram-bot

Это будет выглядеть так:

И заметьте, что я использую тот интерпретатор Python, который установил сам, а не тот который предлагает VS Code. Это помогло мне избежать кучу ошибок при установке библиотеки.

Лучше использовать Python версии 3.7.0 и выше!

После того, как вы нажмёте Enter, у вас установится библиотека и мы сможем начать писать код.

Но, как писать бота, которого у нас нет?

Этап 2. Создание бота

Теперь нам необходимо создать самого бота в Telegram. Поэтому заходим в Telegram и открываем переписку с «ботом для создания ботов» — @BotFather .

На скриншоте я скрыл от вас TOKEN бота, так как имея доступ к нему можно делать с ботом всё, что угодно. Поэтому и вы никому не показывайте токен своего бота, пока что просто скопируйте его.

Теперь у нас есть бот и можно начать его программировать. Кстати, в этом туториале я не буду показывать, как поставить аватарку или приветственный текст для вашего бота, так как вы можете сделать это сами с помощью команды Edit Bot у @BotFather .

Этап 3. Написание бота

На этом этапе мы уже начнём писать самого бота, то есть его функционал.

В файле main.py пишем следующее:

import logging import telegram from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, ConversationHandler, RegexHandler logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO) logger = logging.getLogger(__name__) def main(): updater = Updater(token='Токен вашего бота') dispatcher = updater.dispatcher conv_handler = ConversationHandler( entry_points = [CommandHandler('start', start)], states = < ACTION: [RegexHandler('^(Learn new words|Check yourself) #39;, action)], ANSWER: [MessageHandler(Filters.text, answer_check)] >, fallbacks=[CommandHandler('cancel', cancel)] ) dispatcher.add_handler(conv_handler) dispatcher.add_error_handler(error) updater.start_polling() updater.idle() if __name__ == '__main__': main()

Давайте разберёмся с тем, что это значит.

В самом начале мы импортируем различные библиотеки:

  • logging — это библиотека для логирования, с помощью которой мы создадим логгер и сможем выявлять ошибки в коде нашего бота.
  • telegram и telegram.ext — библиотека для работы с ботом Telegram, которую мы установили в первом этапе нашего туториала.

Далее, мы создаём логгер, который будет выводить ошибки в терминале.

После этого мы создаём метод main, который будет всё время вызываться первым благодаря этим строчкам кода, которые мы записали в конце предыдущего блока кода:

if __name__ == '__main__': main()

Коротко об объектах и обработчиках

В самом же методе main мы создаём объекты:

  • updater — с его помощью мы соединимся нашим ботом в телеграме, поэтому в одинарные кавычки вам нужно скопировать и вставить токен вашего бота.
  • dispatcher — все обновления будут идти через него, то есть он будет отвечать за обновления в непосредственно самом Telegram, текстовые команды и обработчики событий(handlers).
  • conv_handler — является объектом Conversation Handler, который является сложным обработчиком событий. Он включает в себя 4 разных коллекций обработчиков событий: список entry_points, словарь states, список fallbacks и список timed_out_behavior. В нашем боте мы будем использовать только первые три.

Если говорить подробнее о коллекциях обработчиков, то можно сказать, что entry_points используется для того, чтобы начать переписку с ботом. Поэтому мы вносим в неё CommandHandler — обработчик команд на команду ‘start‘. Таким образом, когда пользователь напишет ‘/start‘ боту, у нас вызовется метод start, который мы напишем чуть позже.

States может содержать несколько обработчиков событий, которые отвечают за различные состояния переписки. Например, в нашем случае мы имеем два состояния: ACTION и ANSWER. Первое отвечает за то, какое действие выберет пользователь, а второе за то, какой ответ напишет пользователь при выполнении теста. RegexHandler в первом состоянии обрабатывает строки ‘Learn new words’ или ‘Check yourself’ и далее передаёт один из них в метод action. MessageHandler во втором состоянии обрабатывает любой текст, поэтому мы написали Filters.text, но он также может обрабатывать и другие типы сообщений. В конце статьи я дам ссылку на документацию, где всё это есть.

Поэтому за пределами метода main(можно после создания логгера)пишем следующие строки кода:

ACTION, ANSWER = range(2)

Fallbacks используется для того, чтобы выйти из текущего состояния, поэтому при нажатии на ‘/cancel‘ пользователь сможет выйти на предыдущее состояние.

Далее, через эти строки мы добавляем обработчик событий conv_handler и error_handler, отвечающий за ошибки в dispatcher:

dispatcher.add_handler(conv_handler) dispatcher.add_error_handler(error)

Следующие строки кода начинают принимать обновления с нашего Telegram бота:

updater.start_polling() updater.idle() #незн зачем это

Но я не совсем уверен в последнем (просто увидел, как это используется в примерах в официальном репозитории библиотеки)

Polling и Webhooks

Небольшое отступление от темы:

Наш бот использует polling, то есть периодически отправляет запросы на сервера Telegram. Можно использовать webhook-и, чтобы бот отправлял запросы на определённый url, но опыта работы с ними у меня еще нет, да и используются они только для крупных проектов, которые используются огромным количеством пользователей и там нужна производительность.

Продолжаем писать нашего бота.

Метод start

В первую очередь напишем метод (функцию) start, который, как я и говорил будет вызываться командой ‘/start‘:

def start(bot, update): bot.send_chat_action(chat_id=update.message.chat_id, action = telegram.ChatAction.TYPING) time.sleep(1) custom_keyboard = [['Learn new words'], ['Check yourself']] reply_markup = telegram.ReplyKeyboardMarkup(custom_keyboard, one_time_keyboard=False) bot.send_message(chat_id=update.message.chat_id, text="What do you want to do?", reply_markup=reply_markup) return ACTION

Здесь мы используем time, поэтому не забудьте импортировать библиотеку time в самом начале:

import time
from telegram import ChatAction

bot.send_chat_action — предназначен для того, чтобы бот показывал действие, как здесь:

time.sleep(1) — для того, чтобы он делал это на протяжении 1 секунды. Без этого вы не увидите этой надписи на скриншоте.

Далее, мы создаём кастомную клавиатуру (custom_keyboard), состоящую из двух кнопок на выбор.

reply_markup — это разметка, которая будет отправляться в ответ на сообщение пользователя. В неё мы добавляем нашу кастомную клавиатуру.

one_time_keyboard = False — означает то, что клавиатура не исчезнет после того, как пользователь отправит сообщение.

bot.send_message — для того, чтобы бот отправил соответствующее сообщение пользователю в чат. В нашем случае принимает параметры (может принимать больше):

  • chat_id — уникальный идентификатор чата в формате @username
  • text — определённый текст в одинарных кавычках
  • reply_markup — разметка, которую должен отправить бот, то есть клавиатуру

Последняя строка в методе отправляет состояние ACTION, которое далее перехватывает RegexHandler, о котором говорилось раньше.

Кстати, не забудьте добавить этот метод в код, так как возможно без него у вас не будет работать bot.send_action (взял этот код в Wiki Pages репозитория, поэтому понятия не имею, что он делает):

def send_action(action): def decorator(func): @wraps(func) def command_func(*args, **kwargs): bot, update = args bot.send_chat_action(chat_id=update.effective_message.chat_id, action=action) return func(bot, update, **kwargs) return command_func return decorator

Поэтому нужно еще импортировать следующее:

from functools import wraps

Файл dictionaries.py

Перед тем, как начать писать метод action необходимо создать 3 списка, которые будут источником информации для бота. Нужно создать список слов, описаний слов и типов слов, то есть words, description и type.

Я решил занести эти списки в новый файл dictionaries.py, чтобы эти данные не мешали в нашем главном файле. По скриншоту ниже вы поймёте почему��:

Говнокод? Возможно. Просто мне лень париться насчёт различных баз данных для хранения всей информации.

Не забудьте написать это, чтобы мы смогли пользоваться этими списками в файле main.py:

import dictionaries words = dictionaries.words type = dictionaries.type description = dictionaries.description

Методы action и learn

Следующий шаг — это написание метода action, который вызывается RegexHandler-ом. Вот как он будет выглядеть:

def action(bot, update): if(update.message.text == 'Learn new words'): learn(bot, update) elif(update.message.text == 'Check yourself'): num = generate_correct_answer() global correct_word correct_word = num correct_num = random.randint(1, 4) first_incorrect = words[random.randint(1, len(words) - 1)] second_incorrect = words[random.randint(1, len(words) - 1)] third_incorrect = words[random.randint(1, len(words) - 1)] if(correct_num == 1): custom_keyboard=[[words[correct_word]], [first_incorrect], [second_incorrect], [third_incorrect]] elif(correct_num == 2): custom_keyboard=[[first_incorrect], [words[correct_word]], [second_incorrect], [third_incorrect]] elif(correct_num == 3): custom_keyboard=[[first_incorrect], [second_incorrect], [words[correct_word]], [third_incorrect]] elif(correct_num == 4): custom_keyboard=[[first_incorrect], [second_incorrect], [third_incorrect], [words[correct_word]]] reply_markup = telegram.ReplyKeyboardMarkup(custom_keyboard, one_time_keyboard=False) bot.send_message(chat_id=update.message.chat_id, text=description[correct_word], reply_markup=reply_markup) return ANSWER

При первом условии вызывается метод learn, который будет отправлять случайные слова из списка:

def learn(bot, update): num = random.randint(1, len(words) - 1) bot.send_chat_action(chat_id=update.message.chat_id , action = telegram.ChatAction.TYPING) time.sleep(1) bot.send_message(chat_id=update.message.chat_id, text="*"+ words[num]+"* - "+description[num]+"\n"+"\n_"+type[num]+"_", parse_mode=telegram.ParseMode.MARKDOWN) bot.send_message(chat_id=update.message.chat_id, text="What is next?")

Здесь всё понятно — создаётся рандомное число и бот отправляет всю информацию из списка по индексу этого числа о нём в виде одного сообщения.

Рандом? Нужно импортировать следующее:

import random

parse_mode — нужен для того, чтобы бот превратил нужные слова в кавычках в bold или italic. Для этого используется Markdown mode.

Возвращаемся к методу action.

Здесь также создаётся случайное число через метод generate_correct_answer:

def generate_correct_answer(): num = random.randint(1, len(words) - 1) return num

Также в этом методе будет использоваться глобальная переменная correct_word, поэтому добавьте её вне всех методов:

correct_word = 0

Для доступа к ней вне метода action я написал метод get_correct_word:

def get_correct_word(): return correct_word

Далее в методе action мы создаём три неправильных случайных ответа и распределяем все ответы тоже в случайном порядке. Думаю здесь не нужно объяснений — всё и так предельно ясно.

Ну и в конце метода возвращается состояние ANSWER, которое перехватывается MessageHandler-ом, после чего вызывается метод answer_check.

Метод answer_check

Этот метод будет отвечать за то, чтобы проверять ответы введённые пользователем. Он выглядит так:

def answer_check(bot, update): correct_word = get_correct_word() custom_keyboard = [['Learn new words'], ['Check yourself']] reply_markup = telegram.ReplyKeyboardMarkup(custom_keyboard, one_time_keyboard=False) if(update.message.text == words[correct_word]): bot.send_chat_action(chat_id=update.message.chat_id , action = telegram.ChatAction.TYPING) bot.send_message(chat_id=update.message.chat_id, text="*Correct!*", parse_mode=telegram.ParseMode.MARKDOWN) bot.send_message(chat_id=update.message.chat_id, text="What do you want to do?", reply_markup=reply_markup) return ACTION else: bot.send_chat_action(chat_id=update.message.chat_id , action = telegram.ChatAction.TYPING) bot.send_message(chat_id=update.message.chat_id, text="*Incorrect!*" + " Correct answer is: " + words[correct_word], parse_mode=telegram.ParseMode.MARKDOWN) bot.send_message(chat_id=update.message.chat_id, text="What do you want to do?", reply_markup=reply_markup) return ACTION

Здесь есть два условия: если ответ правильный и если нет. В соответствии с этим будут отображаться соответствующие сообщения от бота.

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

Еще есть два метода cancel и error, которые вызываются из метода main и играют незначительную часть нашего кода (комментировать их не буду):

def cancel(bot, update): return ConversationHandler.END def error(bot, update, error): logger.warning('Update "%s" caused error "%s"', update, error)

Итак, мы написали нашего бота но он работает только тогда, когда мы нажимаем на кнопку run. Но как сделать так, чтобы он работал постоянно?

Часть 4. Хостинг бота

Надеюсь вы уже создали аккаунт в Heroku. Всё, что вам требуется сейчас сделать это войти в свой аккаунт и увидеть этот экран:

Не обращайте внимание на мои проекты, вместо них у вас ничего не будет.

Далее, вам необходимо скачать Heroku Command Line Interface (CLI) по этой ссылке и Git по этой ссылке .

После того, как вы всё установили, перейдите в терминал в VS Code. Напишите туда следующее:

heroku login

Нажмите на любую кнопку (кроме q) и вас перекинет в браузер, где вам нужно нажать на кнопку Log in:

Далее можно вернуться обратно в VS Code. Перед тем, как загрузить нашего бота на Heroku, нам следует добавить еще два файла: requirements.txt и Procfile. Заметьте, что у Procfile нет никакого расширения.

В Procfile напишем эту строку:

worker: python main.py $PORT

Это означает, что тип нашего dyno (так называется что-то на подобии сервера в Heroku) — worker будет работать с файлом main.py. Он будет работать всегда и без передышки. Web dyno будет иногда выключаться, но нам этого не надо.

В файл requirements.txt пишет эти строки:

appdirs==1.4.3 certifi==2018.1.18 Cython==0.23 Django==1.10.6 docutils==0.13.1 packaging==16.8 pipenv==11.8.0 psutil==5.0.1 pyowm==2.8.0 Pygments==2.2.0 pyparsing==2.2.0 pyTelegramBotAPI==3.6.1 python-telegram-bot==7.0.1 requests==2.13.0 six==1.10.0 virtualenv==15.1.0 virtualenv-clone==0.3.0

Это различные требования для работы нашего бота на Heroku. Возможно некоторые из них вовсе не нужны, попробуйте проверить :D.

Теперь у нас всё готово для загрузки бота на Heroku!

Возвращаемся в терминал, где мы залогинились на Heroku, помните? Пишем туда:

heroku create

После этого у нас создаётся проект на Heroku. Зайдите на сайт и посмотрите его имя. Например, у меня создался проект и он называется agile-refuge-53805.

Далее, в терминале следует написать:

git add .

Потом делаете свой первый коммит:

git commit -am "make it better"

И затем делаете деплой своего кода на Heroku:

git push heroku master

Вам нужно будет подождать немного (обычно от 1 до 3 минут) и потом написать следующее в терминал, чтобы запустить вашего worker dyno:

heroku ps:scale worker=1

Готово! Проверьте своего бота — теперь он должен работать постоянно.

Кстати, Heroku не полностью бесплатный сервис, поэтому иногда они будут присылать вам сообщения о том, что ваши dyno перестанут работать надо заплатить. Я обычно игнорю такие сообщения, а боты работают как ни в чем не бывало 🙂

Я надеюсь вам понравился мой туториал и вы написали своего бота в Telegram! Если вам понравилась статья, то не поленитесь поставить лайк или написать мне «Спасибо!». Это будет мотивировать меня писать для вас статьи!

Если вы немного запутались, то вот GitHub репозиторий на этого бота.

Футбольный телеграм бот на Python (1/4): Подготовка и настройка бота

В этой серии статей мы напишем телеграм бота на python. Он работает с внешним API, запрашивает результаты футбольных матчей и выводить их в сообщении.

Когда локальная версия будет готова, разместим бота на сервере. Вместо Heroku, я выбрал отдельную виртуальную машину, что бы бот не засыпал. Это ближе к реальности.

Вся разработка разбита на этапы:

  1. Локальная установка библиотек и Redis.
  2. Регистрация и получение токена.
  3. Настройка , подключение к базам данных.
  4. Написание основной функциональности бота.
  5. Регистрации, выбор и настройка внешнего апи футбольных матчей.
  6. Добавление сбора результатов матчей и интеграция в бота.
  7. Деплой, публикация на сервере:
    1. Регистрация дешевого или бесплатного VPS.
    2. Запуск Редис-клиента.
    3. Запуск и настройка бота на сервере.

    Рабочая версия бота запущена в телеграме до конца февраля @FonlineBOT. Бот отключен.

    Вводные данные

    Материал рассчитан на уровень Начинающий+, нужно понимать как работают классы и функции, знать основы базы данных и async/await. Если знаний мало, крайне желательно писать код в Pycharm, бесплатная версия подходит.

    Используйте указанные версии библиотек, что бы проект работал без изменений. При установке иных версий вы можете получать ошибки, связанные с совместимостью.

    Версия Python - 3.8+ aiogram==2.11.2 emoji==1.1.0 redis==3.5.3 ujson==4.0.1 uvloop==0.14.0 # не работает и не требуется на Windows

    Локальная установка библиотек для бота и Redis

    Для начала нужно создать проект «fonlinebot» с виртуальным окружение. В Pycharm это делается так:

    pycharm new project

    Затем установить библиотеки в виртуальном окружении. Сразу понадобятся 4: для бота, работы с redis, ускорения и emoji в сообщениях.

    pip install aiogram==2.11.2 redis==3.5.3 ujson==4.0.1 emoji==1.1.0

    Установка Redis локально

    Redis — это резидентная база данных (такая, которая хранит записи прямо в оперативной памяти) в виде пар ключ-значение. Чтение и запись в память происходит намного быстрее, чем в случае с дисками, поэтому такой подход отлично подходит для хранения второстепенных данных.

    Из недавней статьи — Redis для приложений на Python

    Для установки Redis на Linux/Mac следуйте этим инструкциям: https://redis.io/download#from-source-code. Для запуска достаточно ввести src/redis-server .

    Что бы установить на Windows скачайте и распакуйте архив отсюда. Для запуска откройте «redis-server.exe».

    Теперь нужно убедиться, что все работает. Создайте файл «main.py» в корне проекта и выполните этот код:

     
    # fonlinebot/main.py import redis r = redis.StrictRedis() print(r.ping())

    Вывод будет True , в другом случае ошибка.

    Регистрация бота и получение токена

    Для регистрации напишем https://t.me/botfather команду /newbot . Далее он просит ввести имя и адрес бота. Если данные корректны, выдает токен. Учтите, что адрес должен быть уникальным, нельзя использовать «fonlinebot» снова.

    Футбольный телеграм бот на Python (1/4): Подготовка и настройка бота

    На время разработки сохраним токен в файл. Создайте «config.py» в папке проекта для хранения настроек и запишите токен TOKEN = "ВАШ ТОКЕН"

    Настройка бота

    Теперь нужно связать бота с redis и базой данных, проверить работоспособность.

    Создадим необходимые модули и файлы. В папке «fonlinebot» к созданным ранее «main.py» и «config.py» добавим: «database.py», «requirements.txt» и папку «app». В папку «app» добавьте: «bot.py», «dialogs.py», «service.py». Вот такая структура получится:

    Футбольный телеграм бот на Python (1/4): Подготовка и настройка бота

    Разделив бот на модули, его удобнее поддерживать и дорабатывать.

    • «main.py» — для запуска бота.
    • «config.py» — хранит настройки, ключи доступов и другую статическую информацию.
    • «database.py» — для работы с базой данных и кешем(redis).
    • «requirements.txt» — хранит зависимости проекта, для запуска на сервере.
    • «app» — папка самого бота.
      • «bot.py» — для взаимодействия бота с юзерами, ответы на сообщения.
      • «dialogs.py» — все текстовые ответы бота.
      • «service.py» — бизнес логика, получение и обработка данных о матчах.

      Пришло время перейти к программированию. Запишем в «requirements.txt» наши зависимости:

      aiogram==2.11.2 emoji==1.1.0 redis==3.5.3 ujson==4.0.1 uvloop==0.14.0

      Так как большая часть программирует на Windows, uvloop мы не устанавливали локально. Установим его на сервере.

      В «config.py» к токену добавим данные бота и подключения к redis.

       
      # fonlinebot/config.py import ujson import logging logging.basicConfig(level=logging.INFO) TOKEN = "здесь должен быть токен" BOT_VERSION = 0.1 # База данных хранит выбранные юзером лиги BOT_DB_NAME = "users_leagues" # Тестовые данные поддерживаемых лиг BOT_LEAGUES = < "1": "Бундеслига", "2": "Серия А", "3": "Ла Лига", "4": "Турецкая Суперлига", "5": "Чемпионат Нидерландов", "6": "Про-лига Бельгии", "7": "Английская Премьер-лига", "8": "Лига 1", ># Флаги для сообщений, emoji-код BOT_LEAGUE_FLAGS = < "1": ":Germany:", "2": ":Italy:", "3": ":Spain:", "4": ":Turkey:", "5": ":Netherlands:", "6": ":Belgium:", "7": ":England:", "8": ":France:", ># Данные redis-клиента REDIS_HOST = 'localhost' REDIS_PORT = 6379 # По умолчанию пароля нет. Он будет на сервере REDIS_PASSWORD = None

      Информацию о лигах в будущем можно будет вынести в отдельный json файл. Эта версия бота будет поддерживать не более 10 вариантов, я явно их записал.

      Добавление базы данных

      Теперь добавим классы для работы с базой данных sqlite и redis. База данных нужна для сохранения предпочтений по лигам юзеров.

      Юзер будет выбирать 3 чемпионата для отслеживания, бот сохранит их в БД и использует для запроса результатов.

      Кеш(redis) будет сохранять результаты матчей, что бы уменьшить количество запросов к API и ускорить время ответов. Как правило, бесплатные API лимитирует запросы.

       
      # fonlinebot/database.py import os import logging import sqlite3 import redis import ujson import config # класс наследуется от redis.StrictRedis class Cache(redis.StrictRedis): def __init__(self, host, port, password, charset="utf-8", decode_responses=True): super(Cache, self).__init__(host, port, password=password, charset=charset, decode_responses=decode_responses) logging.info("Redis start") def jset(self, name, value, ex=0): """функция конвертирует python-объект в Json и сохранит""" r = self.get(name) if r is None: return r return ujson.loads(r) def jget(self, name): """функция возвращает Json и конвертирует в python-объект""" return ujson.loads(self.get(name))

      Класс Cache наследуется от StrictRedis . Мы добавляем 2 метода jset , jget для сохранения списков и словарей python в хранилище redis. Изначально он не работает с ними.

      Теперь добавим класс, который будет создавать базы данных и выполнять функции CRUD.

       
      # fonlinebot/database.py #. class Database: """ Класс работы с базой данных """ def __init__(self, name): self.name = name self._conn = self.connection() logging.info("Database connection established") def create_db(self): connection = sqlite3.connect(f".db") logging.info("Database created") cursor = connection.cursor() cursor.execute('''CREATE TABLE users (id INTEGER PRIMARY KEY, leagues VARCHAR NOT NULL);''') connection.commit() cursor.close() def connection(self): db_path = os.path.join(os.getcwd(), f".db") if not os.path.exists(db_path): self.create_db() return sqlite3.connect(f".db") def _execute_query(self, query, select=False): cursor = self._conn.cursor() cursor.execute(query) if select: records = cursor.fetchone() cursor.close() return records else: self._conn.commit() cursor.close() async def insert_users(self, user_id: int, leagues: str): insert_query = f"""INSERT INTO users (id, leagues) VALUES (, "")""" self._execute_query(insert_query) logging.info(f"Leagues for user added") async def select_users(self, user_id: int): select_query = f"""SELECT leagues from leagues where record = self._execute_query(select_query, select=True) return record async def update_users(self, user_id: int, leagues: str): update_query = f"""Update leagues set leagues = "" where self._execute_query(update_query) logging.info(f"Leagues for user updated") async def delete_users(self, user_id: int): delete_query = f"""DELETE FROM users WHERE self._execute_query(delete_query) logging.info(f"User deleted")

      Sqlite подходит для тестовых проектов. В будущем потребуется переход на внешнюю базу данных и асинхронная работа. Что бы не переписывать всю логику работы с базой, я сразу добавил асинхронный синтаксис.

      Файл базы данных будет создаваться один раз, автоматически. Теперь нужно создать экземпляры классов:

      Введение, простой echo-бот

      Приветствую тебя, читатель! Telegram Bot API – это мощный инструмент для вообще чего угодно. Автоматизация действий, работа с пользователями, онлайн-магазины, игры и много чего ещё. В этом учебнике мы научимся писать ботов для Telegram на языке Python.

      Сразу оговорюсь: тот бот, который получится в итоге - это лишь прототип, цель всех этих постов - рассказать об основах ботостроения, показать, как можно за короткое время написать простого бота для своих нужд.

      Язык программирования будет Python 3, но это не означает, что любители PHP, Ruby и т.д. в пролёте; все основные принципы совпадают. Я не буду особо останавливаться на описании самого языка, желающие могут ознакомиться с документацией по Python здесь.

      Подготовка к запуску

      Взаимодействие ботов с людьми основано на HTTP-запросах. Чтобы не мучаться с обработкой «сырых» данных, воспользуемся библиотекой pyTelegramBotAPI, которая берет на себя все нюансы отправки и получения запросов, позволяя сконцентрироваться непосредственно на логике. Установка библиотеки предельно простая:

      pip install pytelegrambotapi python3 

      Скриншот из терминала с запущенным интерпретатором Python

      Обратите внимание: библиотека называется pyTelegramBotAPI , а не telebot . Последнюю ставить не нужно!

      Теперь можно выйти из режима Python-консоли (Ctrl+Z или Ctrl+D, или exit() )

      Пишем простого echo-бота

      Ну, довольно слов, перейдем к делу. В качестве практики к первому уроку, напишем бота, повторяющего присланное текстовое сообщение. Создадим каталог, а внутри него 2 файла: bot.py и config.py . Я рекомендую выносить различные константы и настройки в файл config.py , дабы не загромождать другие. В файл config.py впишем:

      # Токен ненастоящий :) Подставьте свой token = '1234567890:AAE_abCDEFghijKLmNOpqRsTuVWxyz' 

      Теперь надо научить бота реагировать на сообщения. Напишем обработчик, который будет реагировать на все текстовые сообщения.

      @bot.message_handler(content_types=["text"]) def repeat_all_messages(message): # Название функции не играет никакой роли bot.send_message(message.chat.id, message.text) 

      У читателя возникнет вопрос: зачем там символ “@”? Что это вообще за message_handler ? Дело в том, что после приёма сообщения от Telegram его надо обработать по-разному в зависимости от того, что это за сообщение: текст “привет” или текст “пока”, может быть, вообще стикер или музыка. Первое, что придёт в голову – написать множество конструкций if-then-else , но такой подход некрасивый и позволяет быстро запутаться.
      Для решения этой проблемы автор библиотеки pyTelegramBotAPI реализовал механизм хэндлеров, которые используют питоновские декораторы (пока просто запомним это слово). В хэндлере описывается, в каком случае необходимо выполнять ту или иную функцию. Например, хэндлер @bot.message_handler(content_types=["text"]) выполнит нижестоящую функцию, если от Telegram придёт текстовое сообщение, а хэндлер @bot.message_handler(commands=["start"]) сработает при получении команды /start.

      Теперь запустим бесконечный цикл получения новых записей со стороны Telegram:

      if __name__ == '__main__': bot.infinity_polling() 

      Функция infinity_polling запускает т.н. Long Polling, бот должен стараться не прекращать работу при возникновении каких-либо ошибок. При этом, само собой, за ботом нужно следить, ибо сервера Telegram периодически перестают отвечать на запросы или делают это с большой задержкой приводя к ошибкам 5xx)

      Итак, полный код файла bot.py выглядит следующим образом:

      import config import telebot bot = telebot.TeleBot(config.token) @bot.message_handler(content_types=["text"]) def repeat_all_messages(message): # Название функции не играет никакой роли bot.send_message(message.chat.id, message.text) if __name__ == '__main__': bot.infinity_polling() 

      Готово! Осталось запустить бота: python3 bot.py

      Бот работает

      На этом первый урок окончен.

      Как написать Telegram бота: практическое руководство

      6 Апрель 2016 , Python, 457123 просмотров, How To Create a Telegram Bot Using Python

      В последнее время Telegram у всех на слуху. Нужно отдать должное отделу маркетинга этого приложения, шумиху подняли на славу. Одной из основных "фишек" Telegram является его якобы защищённость - по словам Павла Дурова вся переписка между пользователями шифруется. Более того, ни одна спец.служба мира не будет иметь доступ к вашим сообщениям. Но в данной статье речь не об этом. Сегодня хотелось бы поговорить о не менее крутой фишке в Telegram, а именно о ботах. Помимо того, что в сети уже полно информации о различного рода Telegram ботах (github бот, например), мессенджер открыл своё API для разработчиков, и теперь каждый может создать своего собственного бота с блэкджеком и плюшками.

      В статье я приведу пример написания онлайн бота с использованием Python и Django фреймворка. То есть мы "запилим" полноценное веб-приложение, которое будет крутиться на удалённом хосте и принимать команды от пользователей. Весь исходный текст доступен в моём github репозитории.

      Документация, описывающая процесс взаимодействия с ботами Telegram находится тут. Чтобы не изобретать велосипед, я нашел неплохую Python библиотеку, реализующую все основные функции ботов - telepot. Как я уже упоминал ранее, для того, чтобы обслуживать пользователей нашего бота мы будет разрабатывать веб-приложение, используя Django фреймворк.

      Как создать Telegram бота?

      Для начала нам необходимо зарегистрировать в Telegram нашего будущего бота. Это делается следующим образом:

      • Необходимо установить приложение Telegram на телефон или компьютер. Скачать приложение можно тут
      • Добавляем к себе в контакт-лист бота с именем BotFather
      • Запускаем процедуру "общения" с ботом нажатием кнопки Start. Далее перед нами предстанет список команд точно как на скриншоте.
      • Для того, чтобы создать нового бота необходимо выполнить команду /newbot и следовать инструкциям. Обратите внимание, что username для бота должен всегда содержать в конце слово bot. Например, DjangoBot или Django_bot.

      Telegram bot

      • Для нашего бота я выбрал имя PythonPlanetBot, так как его основная функция заключается в парсинге RSS feed сайта Python Planet и выдача информации о последних постах пользователю 🙂

      Python Planet бот

      После создания бота, обратите внимание на строку с текстом:

      Use this token to access the HTTP API:

      За которой следует т.н. token по которому мы будем манипулировать нашим ботом. Помимо функции создания telegram бота, BotFather также имеет ряд других возможностей:

      • Присвоить боту описание
      • Установить аватар
      • Поменять token

      и так далее. Полное описание доступных команд можно увидеть на первом скриншоте.

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

      Как я ранее уже упоминал, мы будем писать веб-приложение на Django. Но стоит отметить, что это делать необязательно. Можно обойтись и обычным Python скриптом, правда в этом случае необходимо будет периодически опрашивать Telegram на предмет новых запросов от пользователей бота (используя метод getUpdates) и увеличивая offset для получения самых последних данных без повторений. В Telegram существует два взаимоисключающих метода получения команд/сообщений для вашего бота.

      • Использование вызова API метода getUpdates
      • Установка Webhook

      Установка Webhook заключается в передаче боту специального URL адреса на который будет поступать POST запрос каждый раз, когда кто-то начнёт посылать сообщения боту. Именно этот вариант мы и будем использовать для взаимодействия между ботом и его пользователем. Для того, чтобы задать URL, необходимо использовать API метод setWebhook. Отмечу, что URL должен начинаться с https, то есть иметь защищённое SSL соединение с валидным сертификатом. Telegram разрешает использовать самоподписанный сертификат, правда для этого необходимо в методе setWebhook передавать также публичный ключ в PEM формате (ASCII base64). Либо же можно получить валидный бесплатный SSL сертификат от Let's Encrypt.

      Подробнее о getUpdates и setWebhook можно почитать соответственно здесь и тут.

      Итак, вернёмся к python библиотеке для работы с Telegram - telepot. На текущий момент самой последней её версий является 6.7. Устанавливаем её в виртуальное окружение python virtualenv:

      pip install telepot

      Самый простой вариант взаимодействия с Telegram ботом на Python выглядит следующим образом:

      import telepot token = '123456' TelegramBot = telepot.Bot(token) print TelegramBot.getMe() 

      Переменной token присваиваем значение токена, полученного при создании бота через BotFather. В итоге после выполнения этих команд мы получим:

      Поздравляю! Мы вызывали самый простой API запрос getMe, который возвращает информацию о боте: username, id, first_name.

      Добавим нашего бота к себе в контакт-лист и пошлём ему первую стандартную команду /start

      Telegram Bot

      TelegramBot.getUpdates() [, u'message_id': 1, u'chat': >, u'update_id': 649179764>] 

      Процесс общения с telegram ботом происходит по HTTPS; для передачи данных используется JSON. Метод getUpdates возвращает список/массив из объектов типа Update. Внутри Update находится объект Message. Для стандартного взаимодействия с ботом нас фактически интересует именно объект Message, у которого мы считываем атрибут text, хранящий в себе текст, переданный боту и объект chat, в котором лежит информация о пользователе, инициировавшем общение с нашим Telegram ботом. Также имеется параметр update_id, который служит в качестве offset параметра при вызове метода getUpdates. То есть update_id+1 вернёт все сообщения, поступившие после последнего update_id, при этом все предыдущие сообщения будут удалены.

      TelegramBot.getUpdates(649179764+1) [, u'message_id': 13, u'chat': >, u'update_id': 649179765>] 

      На этапе написания простейшего Telegram бота нам этих вызовов достаточно. Приступим к написанию Django приложения для обслуживания наших пользователей.

      Простая функция парсинга RSS фида Planet Python выглядит вот так:

      # -*- coding: utf8 -*- from xml.etree import cElementTree import requests def parse_planetpy_rss(): """Parses first 10 items from http://planetpython.org/rss20.xml """ response = requests.get('http://planetpython.org/rss20.xml') parsed_xml = cElementTree.fromstring(response.content) items = [] for node in parsed_xml.iter(): if node.tag == 'item': item = <> for item_node in list(node): if item_node.tag == 'title': item['title'] = item_node.text if item_node.tag == 'link': item['link'] = item_node.text items.append(item) return items[:10] 

      Здесь я использую python библиотеку requests для работы с HTTP в самом простейшем варианте без обработки ошибок. Django "вьюшка" выглядит следующим образом:

      TOKEN = '' TelegramBot = telepot.Bot(TOKEN) def _display_help(): return render_to_string('help.md') def _display_planetpy_feed(): return render_to_string('feed.md', ) class CommandReceiveView(View): def post(self, request, bot_token): if bot_token != TOKEN: return HttpResponseForbidden('Invalid token') commands = < '/start': _display_help, 'help': _display_help, 'feed': _display_planetpy_feed, >try: payload = json.loads(request.body.decode('utf-8')) except ValueError: return HttpResponseBadRequest('Invalid request body') else: chat_id = payload['message']['chat']['id'] cmd = payload['message'].get('text') # command func = commands.get(cmd.split()[0].lower()) if func: TelegramBot.sendMessage(chat_id, func(), parse_mode='Markdown') else: TelegramBot.sendMessage(chat_id, 'I do not understand you, Sir!') return JsonResponse(<>, status=200) @method_decorator(csrf_exempt) def dispatch(self, request, *args, **kwargs): return super(CommandReceiveView, self).dispatch(request, *args, **kwargs) 

      CommandReceiveView ждёт POST запрос на себя, парсит его и отвечает исходя из заданной команды. Полноценное Django приложение можно найти по этой ссылке. Стоит отметить в коде использование ещё одного API вызова - sendMessage. Этот метод отправляет сообщение заданному пользователю, используя при этом chat_id и сам текст сообщения. Chat_id - это уникальный идентификатор чата между пользователем и ботом (его идентификатор есть в ответе на запрос getUpdates). У Telegram ботов есть одно ограничение, они не могут посылать сообщения пользователям, которые предварительно не инициировали общение с ним. По-видимому это сделано дабы избежать массового создания спам-ботов.

      Я предполагаю, что вы уже клонировали мой репозиторий, настроили окружение и установили все необходимые зависимости: Django, requests, telepot. Если же вы не знаете как это сделать, то совсем скоро я напишу цикл статей о разработке веб-приложений на Python, включая разбор экосистемы: разработка, настройка, деплой. Если вам это интересно, то отпишитесь, пожалуйста, в комментариях к этой статье. Хочется получить обратную связь 🙂

      Итак, веб-приложение на Django запущено. Как же начать тестировать бота? А всё очень просто - необходимо симулировать действия Telegram сервиса. Для этого нам понадобится HTTP клиент и тело запроса. В качестве HTTP клиента я часто использую Chrome плагин под названием Postman, а тело запроса мы возьмём напрямую из данных, полученных с помощью API вызова getUpdates.

      После запуска runserver, URL на который необходимо посылать запрос выглядит следующим образом:

      http://127.0.0.1:8000/planet/b. BOT_TOKEN/

      где BOT_TOKEN - это токен нашего бота. Смотрим скриншот:

      Postman REST Client

      telegram-bot-postman

      А давайте-ка отправим команду feed для получения списка новостей из Planet Python:

      Postman и TelegramPostman и Telegram

      На скриншотах видно, что бот адекватно отреагировал на нашу команду вывести список последних 10 постов.

      Следующим шагом является деплой нашего Django приложения на удалённый хост и последующий вызов метода setWebhook для передачи URL на который будет посылаться POST запрос от сервиса Telegram каждый раз при поступлении команд боту от пользователей. Об этом мы поговорим в следующей заметке.

      Интересные записи:

      • Django Channels: работа с WebSocket и не только
      • Введение в logging на Python
      • Работа с MySQL в Python
      • FastAPI, asyncio и multiprocessing
      • Что нового появилось в Django Channels?
      • Pyenv: удобный менеджер версий python
      • Обзор Python 3.9
      • Почему Python?
      • Руководство по работе с HTTP в Python. Библиотека requests
      • Celery: начинаем правильно
      • Разворачиваем Django приложение в production на примере Telegram бота
      • Авторизация через Telegram в Django и Python
      • Python-RQ: очередь задач на базе Redis
      • Введение в pandas: анализ данных на Python
      • Работа с PostgreSQL в Python
      • Django, RQ и FakeRedis
      • Итоги первой встречи Python программистов в Алматы
      • Обзор Python 3.8
      • Участие в подкасте TalkPython
      • Интеграция Trix editor в Django
      • Строим Data Pipeline на Python и Luigi
      • Видео презентации ETL на Python
      • Авторизация через Telegram в Django приложении

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *