Создание простого Telegram-бота с помощью PythonAnywhere

2024-06-18 пользователь FizzFanatic написал на нашем форуме, что решение webhook работает на бесплатных аккаунтах — и похоже, что это так, спасибо!

Исходное сообщение¶

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

В этом сообщении блога показано, как вы можете создать и запустить простого бота с помощью Telegram. Telegram не так популярна как платформа для обмена сообщениями, как WhatsApp или Skype, но для нее гораздо проще создавать ботов. Вам понадобится обычный компьютер, а также телефон, на который можно установить приложение Telegram. Когда вы закончите прорабатывать описанные здесь шаги, у вас будет бот, который сможет вести с вами почти правдоподобный разговор.

Он использует PythonAnywhere, что, вероятно, не очень удивительно, учитывая название этого блога 😉 Вы можете делать здесь все, используя бесплатную учетную запись PythonAnywhere, и бот, с которым вы попадете, будет полностью функциональным. Платный аккаунт понадобится вам только в том случае, если ваш бот начнет набирать много пользователей — порядка тысяч сообщений в день.

Итак, без лишних слов, давайте начнем!

Создание бота¶

Первое, что вам нужно сделать, это сообщить Telegram, что вы хотите создать бот. Для этого вам понадобится аккаунт в Telegram — установите их приложение на телефоне и настройте его.

Далее начните разговор с «BotFather». Это бот, которым управляет сам Telegram, и он контролирует создание и регистрацию ботов на своей платформе. В версии их приложения для Android вот что вы делаете (другие платформы похожи)

  • Нажмите на кнопку «Начать разговор» в правом нижнем углу, чтобы начать новый разговор.
  • Нажмите на значок лупы «Поиск» в правом верхнем углу.
  • Введите «botfather».
  • Тапните по появившемуся «@BotFather». Убедитесь, что рядом с ним есть синяя галочка
  • Отобразится приветственное сообщение.
  • Нажмите кнопку «Начать».
  • Отправьте ему сообщение «/newbot»
  • Он запросит имя для вашего бота. Я собираюсь назвать свой «GTDemo», но вы должны выбрать имя, которое уникально для вас.
  • Далее он запросит имя пользователя; Я буду использовать «GTDemoBot»
  • Если все прошло успешно, он распечатает сообщение о том, что бот был создан. Там есть одна важная информация, которая вам понадобится позже: токен для доступа к HTTP API. Это будет длинная строка буквенно-цифровых символов, возможно, с двоеточием внутри. Чтобы сохранить это на потом, я скопировал сообщение со своего телефона, а затем отправил его себе по электронной почте — не очень безопасно, но, вероятно, достаточно безопасно, если вы не собираетесь вкладывать ничего секретного в своего бота.

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

В этом нет ничего удивительного. Давайте заставим его что-то сделать.

Первый простой бот¶

На компьютере:

  • Я рекомендую вам создать новую учетную запись PythonAnywhere для этого урока — мы создадим сайт позже, и если у вас уже есть сайт, мы не хотим путать эти два понятия. У вас может быть несколько бесплатных учетных записей, даже используя один и тот же адрес электронной почты, поэтому нет ничего плохого в том, чтобы зарегистрировать новую. Просто создайте бесплатный «Beginner».
  • Запустите консоль «Bash»
  • Туда бегиpip3.6 install --user telepot это установит (для вашей собственной учетной записи PythonAnywhere) отличную библиотеку Python для телепота, которая скрывает некоторые сложности общения с API Telegram. Дождитесь завершения процесса.
  • Затем нажмите на логотип PythonAnywhere в левом верхнем углу, чтобы вернуться на панель управления PythonAnywhere.
  • Перейдите во вкладку «Файлы».
  • В поле «Введите имя нового файла» введите имя файла, заканчивающееся на «.py» для кода вашего бота — скажем, firstsimplebot.py — и нажмите кнопку «Новый файл».
  • Введите следующий код, заменив «YOUR_AUTHORIZATION_TOKEN» на токен, который BotFather дал вам ранее:#!/usr/bin/python3.6 import telepot import time import urllib3 # You can leave this bit out if you're using a paid PythonAnywhere account proxy_url = "http://proxy.server:3128" telepot.api._pools = { 'default': urllib3.ProxyManager(proxy_url=proxy_url, num_pools=3, maxsize=10, retries=False, timeout=30), } telepot.api._onetime_pool_spec = (urllib3.ProxyManager, dict(proxy_url=proxy_url, num_pools=1, maxsize=1, retries=False, timeout=30)) # end of the stuff that's only needed for free accounts bot = telepot.Bot('YOUR_AUTHORIZATION_TOKEN') def handle(msg): content_type, chat_type, chat_id = telepot.glance(msg) print(content_type, chat_type, chat_id) if content_type == 'text': bot.sendMessage(chat_id, "You said '{}'".format(msg["text"])) bot.message_loop(handle) print ('Listening ...') # Keep the program running. while 1: time.sleep(10)
  • Нажмите кнопку «> Запустить этот файл» внизу страницы.
  • Консоль появится внизу страницы. Через некоторое время на дисплее должно появиться сообщение «Прослушивание…»

Теперь вернитесь к своему телефону. В чате с вашим ботом введите «Здравствуйте». Он должен почти немедленно ответить: «Вы сказали «Здравствуйте».

Если вы еще раз взглянете на консоль на PythonAnywhere, то увидите, что она распечатала некоторую информацию о сообщении — вероятно, что-то вроде

text private 321518746

Ухаживать! Работающий бот 🙂

Давайте проработаем этот код шаг за шагом.

#!/usr/bin/python3.6

Это сообщает PythonAnywhere, что вы хотите запустить код с использованием Python 3.6, который является версией Python, для которой мы установили telepot.

import telepot
import time
import urllib3

Этот бит просто импортирует модули Python, которые мы собираемся использовать.

# You can leave this bit out if you're using a paid PythonAnywhere account
proxy_url = "http://proxy.server:3128"
telepot.api._pools = {
    'default': urllib3.ProxyManager(proxy_url=proxy_url, num_pools=3, maxsize=10, retries=False, timeout=30),
}
telepot.api._onetime_pool_spec = (urllib3.ProxyManager, dict(proxy_url=proxy_url, num_pools=1, maxsize=1, retries=False, timeout=30))
# end of the stuff that's only needed for free accounts

Как говорится в комментариях, этот материал нужен только в том случае, если вы используете бесплатную учетную запись PythonAnywhere для начинающих — мы, конечно, для этого руководства, но вы можете удалить его, если захотите повторно использовать код в платной учетной записи позже. Это связано с тем, что бесплатные учетные записи могут подключаться только к определенным внешним веб-сайтам, и эти соединения должны проходить через прокси-сервер. Многие API автоматически получают сведения о прокси-сервере из своей системной среды во время работы, но telepot этого не делает. Это не проблема, это просто означает, что мы должны быть немного более явными и сказать «используйте этот прокси здесь».

bot = telepot.Bot('YOUR_AUTHORIZATION_TOKEN')

Теперь мы добрались до сути кода. Эта линия использует телепот для подключения к серверу Telegram.

Далее мы определяем функцию, которая умеет обрабатывать сообщения от Telepot.

def handle(msg):
    content_type, chat_type, chat_id = telepot.glance(msg)

Первое, что мы делаем, это извлекаем полезную информацию из сообщения, используя функцию утилиты glance от telepot.

    print(content_type, chat_type, chat_id)

… Мы распечатываем некоторую информацию, просто в целях отладки.

    if content_type == 'text':
    	bot.sendMessage(chat_id, "You said '{}'".format(msg["text"]))

На данный момент мы обрабатываем только текстовые сообщения; Распознавание речи немного выходит за рамки этого урока… Когда мы получаем текстовое сообщение, мы просто отвечаем человеку, рассказывая о том, что он сказал.

Вот и все, что связано с функцией обработчика сообщений. Вернемся к основному коду:

bot.message_loop(handle)

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

print ('Listening ...')

Поэтому мы выводим сообщение на нашу собственную консоль, чтобы показать, что мы готовы к работе…

# Keep the program running.
while 1:
    time.sleep(10)

И тогда мы ждем вечность. Как я уже сказал, цикл телепотового сообщения будет продолжаться только до тех пор, пока наша программа не завершится, поэтому мы хотим остановить его завершение.

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

Переход к вебхукам¶

Бот, который у вас есть сейчас, просто работает внутри консоли под вашим редактором. На самом деле он будет работать довольно долго, но если PythonAnywhere выполнит какие-либо работы по обслуживанию системы, требующие перезагрузки сервера, на котором он находится, он остановится и не перезагрузится. Очевидно, что это не очень хорошо для бота, так что давайте исправим это.

Мы будем использовать API «вебхуков» Telegram. Вебхуки — это другой способ подключения к Telegram. Наш предыдущий код устанавливал исходящее соединение из PythonAnywhere в Telegram, а затем полагался на то, что Telegram отправляет сообщения по этому соединению для обработки. С вебхуками все наоборот. По сути, мы говорим Telegram: «Когда мой бот получает сообщение, подключается к PythonAnywhere и передает сообщение». А для того, чтобы подключиться к PythonAnywhere, нужно создать веб-приложение, которое будет работать внутри вашей учетной записи PythonAnywhere, и которое будет работать с очень простым API.

Если что-то из этого звучит пугающе, не волнуйтесь. На самом деле все довольно просто, а инструкции подробно описаны 🙂

  • Нажмите на логотип PythonAnywhere, чтобы вернуться на панель управления PythonAnywhere.
  • На вкладке «Консоли» нажмите на маленький «X» рядом с консолью «firstsimplebot.py». Это важно — это убьет запущенного бота, которого мы уже создали, чтобы он не мешал новому, которого мы собираемся создать.
  • Перейдите на вкладку «Web».
  • Нажмите кнопку «Добавить новое веб-приложение».
  • На первой странице будет просто указано, что веб-приложение будет размещено по адресу your-pythonanywhere-username.pythonanywhere.com. Нажмите далее.
  • На следующей странице выберите веб-фреймворк «Flask». Flask — отличный выбор для простых веб-сайтов, разработанных для API.
  • На следующем шаге выберите «Python 3.6». Именно для этой версии мы установили телепот.
  • На следующей странице просто примите местоположение по умолчанию для вашего приложения Flask. Это будет что-то вроде /home/your-pythonanywhere-username/mysite/flask_app.py
  • После короткого ожидания вы увидите сообщение «Все готово!», и ваш сайт будет настроен. На него будет ссылка – перейдите по ссылке и вы должны увидеть сообщение «Привет от Flask!».

Итак, теперь у вас есть простой веб-сайт, на котором отображается только одно сообщение. Что нам нужно сделать дальше, так это настроить его так, чтобы вместо этого он работал с API, к которому может подключиться Telegram. И нам также нужно сообщить Telegram, что он там есть, и с каким ботом он должен работать.

  • Нажмите кнопку «Назад» в вашем браузере, чтобы вернуться на вкладку «Веб».
  • Посмотрите немного вниз на страницу, и вы увидите раздел «Код».
  • В этом разделе откройте ссылку «Перейти к каталогу» для «Исходного кода» в новой вкладке браузера. (Будет полезно оставить вкладку «Веб» на потом.)
  • В новой вкладке вы увидите страницу «Файлы». Один файл будет называться «flask_app.py»; Нажмите на него, чтобы перейти в редактор.

Введите следующий код. Пока не беспокойтесь о том, что он делает, мы рассмотрим это через секунду. Но не забудьте заменить YOUR_AUTHORIZATION_TOKEN токеном Telegram HTTP API, а YOUR_PYTHONANYWHERE_USERNAME — именем пользователя PythonAnywhere. Также замените A_SECRET_NUMBER номером, который знаете только вы; Хороший способ получить по-настоящему случайный — обратиться к этому онлайн-генератору GUID, который сгенерирует уникальное число, например, «c04a4995-a7e2-4bf5-b8ab-d7599105d1d1».

from flask import Flask, request
import telepot
import urllib3

proxy_url = "http://proxy.server:3128"
telepot.api._pools = {
    'default': urllib3.ProxyManager(proxy_url=proxy_url, num_pools=3, maxsize=10, retries=False, timeout=30),
}
telepot.api._onetime_pool_spec = (urllib3.ProxyManager, dict(proxy_url=proxy_url, num_pools=1, maxsize=1, retries=False, timeout=30))

secret = "A_SECRET_NUMBER"
bot = telepot.Bot('YOUR_AUTHORIZATION_TOKEN')
bot.setWebhook("https://YOUR_PYTHONANYWHERE_USERNAME.pythonanywhere.com/{}".format(secret), max_connections=1)

app = Flask(__name__)

@app.route('/{}'.format(secret), methods=["POST"])
def telegram_webhook():
    update = request.get_json()
    if "message" in update:
        chat_id = update["message"]["chat"]["id"]
        if "text" in update["message"]:
            text = update["message"]["text"]
            bot.sendMessage(chat_id, "From the web: you said '{}'".format(text))
        else:
            bot.sendMessage(chat_id, "From the web: sorry, I didn't understand that kind of message")
    return "OK"

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

  • Сохраните файл
  • Переключитесь на вкладку браузера с настроенным веб-приложением.
  • Нажмите зеленую кнопку «Перезагрузить» вверху.
  • Дождитесь окончания работы «спиннера».

Вернитесь в телефон и отправьте еще одно сообщение. На этот раз вы должны получить ответное сообщение, в котором четко указано, что оно пришло из Интернета. Итак, теперь у нас есть бот, использующий вебхуки!

Давайте теперь проработаем код:

from flask import Flask, request
import telepot
import urllib3

Итак, опять же, мы импортируем некоторые модули Python. На этот раз, помимо телепота и urllib3, которые нам нужны для общения с Telegram, мы используем некоторые материалы из Flask.

proxy_url = "http://proxy.server:3128"
telepot.api._pools = {
    'default': urllib3.ProxyManager(proxy_url=proxy_url, num_pools=3, maxsize=10, retries=False, timeout=30),
}
telepot.api._onetime_pool_spec = (urllib3.ProxyManager, dict(proxy_url=proxy_url, num_pools=1, maxsize=1, retries=False, timeout=30))

Опять же, то, что нам нужно, чтобы получить доступ к Telegram с бесплатной учетной записи PythonAnywhere.

secret = "A_SECRET_NUMBER"

Теперь это немного лучшей практики для ботов Telegram, использующих вебхуки. Ваш бот работает как общедоступный веб-сайт. Любой человек в мире мог подключиться к нему. И, конечно, мы очень не хотим, чтобы случайные люди могли подключаться, притворяясь Telegram, и заставлять его говорить неуместные вещи… Итак, мы собираемся сказать, что веб-сайт обслуживает только одну страницу, и URL-адрес этой страницы невозможно угадать. Это должно сделать вещи достаточно безопасными. Код для этого вы увидите через мгновение.

bot = telepot.Bot('YOUR_AUTHORIZATION_TOKEN')

Подключаемся к Telegram с помощью телепота, как и раньше.

bot.setWebhook("https://YOUR_PYTHONANYWHERE_USERNAME.pythonanywhere.com/{}".format(secret), max_connections=1)

Мы используем telepot для отправки сообщения в Telegram со словами «когда мой бот получает сообщение, это URL-адрес для отправки материала». Это, конечно, не только содержит имя хоста для вашего сайта с вашим именем пользователя PythonAnywhere, но также включает в себя неразгадаемый секрет, который мы определили ранее. Также стоит отметить, что он использует безопасный HTTPS, а не HTTP — все сайты на PythonAnywhere, даже бесплатные, получают HTTPS по умолчанию, а Telegram (вполне разумно) будет отправлять вебхуки только по HTTPS.

app = Flask(__name__)

Теперь мы создаем приложение Flask для обработки запросов.

@app.route('/{}'.format(secret), methods=["POST"])
def telegram_webhook():

Это какой-то код Flask, чтобы сказать: «Когда вы получаете запрос POST по секретному URL, запустите следующую функцию». Если вы хотите узнать больше о том, как работает Flask, у нас есть руководство по этому вопросу.

    update = request.get_json()

Telegram отправляет материалы ботам с использованием кодировки JSON, поэтому мы декодируем их, чтобы получить словарь Python.

    if "message" in update:

Если то, что мы получили от Телеграна, было сообщением…

        chat_id = update["message"]["chat"]["id"]

… получить идентификатор сеанса чата, частью которого он является…

        if "text" in update["message"]:

… Проверьте, есть ли на нем текст (чего не будет, если кто-то, скажем, отправит ему фотографию)…

            text = update["message"]["text"]
            bot.sendMessage(chat_id, "From the web: you said '{}'".format(text))

… Затем отправьте ответ обратно с помощью телепота…

        else:
            bot.sendMessage(chat_id, "From the web: sorry, I didn't understand that kind of message")

… Если это был не текст, просто скажите пользователю, что мы его не поняли…

    return "OK"

… и, наконец, как только какое-то сообщение было отправлено, верните что-то в Telegram, чтобы сообщить, что все в порядке.

Итак, теперь у нас есть, и, надеюсь, мы понимаем, простой Telegram-бот, который будет работать практически вечно! Сайты на бесплатных аккаунтах PythonAnywhere длятся три месяца, а затем вы можете продлить их еще на три месяца — а через три месяца вы можете продлить снова, и так далее, сколько угодно раз. Так что, если вы готовы заходить на PythonAnywhere четыре раза в год, все готово 🙂

Но на данный момент бот довольно скучный. Давайте сделаем его немного интереснее.

Представляем Элизу¶

Извините, фанаты Гамильтона, а не сестра Анжелики и Пегги. Элиза — это ранняя система обработки естественного языка, и ее нормальная реализация имитирует психотерапевта Рожера — своего рода терапевта, который просто обращает каждый вопрос обратно на пациента. Это делает его простым в реализации и использовании в таком боте, как этот.

Вдвойне удобно, что пакет Python nltk предоставляет реализацию Eliza, поэтому нам даже не нужно писать его самостоятельно.

Давайте разберемся, как это работает. Перейдите на панель управления PythonAnywhere и запустите новую консоль Bash. В нем попробуйте Eliza в интерпретаторе Python 3.6 следующим образом (ответы, которые он вам даст, могут отличаться):

19:20 ~ $ python3.6
Python 3.6.0 (default, Jan 13 2017, 00:00:00)
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from nltk.chat.eliza import eliza_chatbot
>>> eliza_chatbot.respond("Hello")
'Hi there... how are you today?'
>>> eliza_chatbot.respond("I'm well")
"Why do you think you're well?"
>>> eliza_chatbot.respond("I'm not sure")
'How does being not sure make you feel?'
>>> eliza_chatbot.respond("A little confused, to be honest")
'Very interesting.'
>>> eliza_chatbot.respond("Is it?")
'Why do you ask that?'

Хорошо, это должно дать вам представление о том, как это работает. Давайте запрограммируем это.

  • Вернитесь на панель управления, на вкладку «Файлы», затем к коду вашего приложения Flask (вероятно, внутри mysite/flask_app.py).
  • Добавьте новый импорт в начало:from nltk.chat.eliza import eliza_chatbot
  • Внутри функции telegram_webhook заменим эту строку:bot.sendMessage(chat_id, "From the web: you said '{}'".format(text)) С этим:if text == "/start": bot.sendMessage(chat_id, "Hello, I'm the therapist. How can I help?") else: bot.sendMessage(chat_id, eliza_chatbot.respond(text)) Когда кто-то впервые подключается к боту Telegram, приложение отправляет вам текстовое сообщение с текстом «/start», поэтому у нас есть особый случай для этого, чтобы Элиза не сказала что-то странное, например, «Почему вы говорите это /start?». А вот все остальные сообщения мы просто отправляем Элизе на обработку, после чего возвращаем.
  • Сохраните файл, затем перейдите на вкладку «Web» и нажмите зеленую кнопку «Reload».

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

  • В старой сессии нажмите меню «Гамбургер» справа вверху и выберите «Удалить чат».
  • Нажмите на кнопку, чтобы снова начать чат, затем снова найдите своего бота, нажмите его.
  • Начните чат, нажав кнопку «Начать».

И теперь у вас должна быть возможность разговаривать со своим чат-ботом! Много счастливых часов не очень полезной терапии 🙂

Вот и все!¶

Вот и все для этого урока. Если у вас возникли проблемы, оставьте комментарий ниже. И если у вас есть какие-либо мысли о том, как мы могли бы его продлить, просто дайте нам знать. Получайте удовольствие и удачного ботинга!