Arrow
Arrow2. August 2019 07:06

Деплой Telegram бота на Django с исползованием webhook

Bot, Django, Telegram

Добрый день.

Помогите пожалуйста никак не могу найти информации о деплое Telegram бота на pythonenywhere.

Написал бота используя pyTelegramBotAPI (база данных, админка и еще некоторые вещи на Django). Столкнулся с проблемой, что на локальной машине с использованием polling() все работает, а когда выкладываю на pythonenywhere, чтобы не мучать сервер нужно использовать webhook. Только нигде в интернете нет вменяемого объяснения как сделать это для Django. Для Flask куча описаний. Пытался переделать то, что удалось найти, но бот никак не заводится. Вернее само приложение Django работает прекрасно, как и админка бота, только бот ника не реагирует на мои сообщения в телеграм. По логам все ок и теграм говорит, что webhook установлен успешно.

Вот мой код:

Файл "urls.py" проекта:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('bot.urls')),
    ..................
]

Файл "urls.py" бота:

urlpatterns = [
    path('', UpdateBot.as_view(), name='update'),
]

Файл "views.py" бота:

from telebot import TeleBot, types
from rest_framework.response import Response
from rest_framework.views import APIView
from TelegramBot.settings import TOKEN


bot = TeleBot(TOKEN)


class UpdateBot(APIView):
    def post(self, request):
        # Сюда должны получать сообщения от телеграм и далее обрабатываться ботом
        json_str = request.body.decode('UTF-8')
        update = types.Update.de_json(json_str)
        bot.process_new_updates([update])

        return Response({'code': 200})


@bot.message_handler(commands=['start'])
def start_message(message):
    # User написал /start в диалоге с ботом
    text = '<b>Настройка бота!</b>\n\n'
    text += 'Чтобы пначать использовать бата и настроить его по Вашим предпочтениям ответьте на следующие вопросы.\n\n'
    text += '......................'

    keyboard = types.InlineKeyboardMarkup()
    key_begin = types.InlineKeyboardButton(text='🖊️ Начать', callback_data='begin')
    keyboard.add(key_begin)

    bot.send_message(message.chat.id, text=text, reply_markup=keyboard, parse_mode='HTML')

..............................

# Webhook
bot.set_webhook(url="https://myapp.pythonanywhere.com/" + TOKEN)

Для Flask (с ним не работал) есть такой пример:

import telebot
import os
from flask import Flask, request

bot = telebot.TeleBot(токен вашего бота)
server = Flask(__name__)


@bot.message_handler(commands=['start'])
def start(message):
    bot.reply_to(message, 'Hello, ' + message.from_user.first_name)

@bot.message_handler(func=lambda message: True, content_types=['text'])
def echo_message(message):
    bot.reply_to(message, message.text)


@server.route("/токен бота", methods=['POST'])
def getMessage():
    bot.process_new_updates([telebot.types.Update.de_json(request.stream.read().decode("utf-8"))])
    return "!", 200

@server.route("/")
def webhook():
    bot.remove_webhook()
    bot.set_webhook(url="https://ссылка на приложение/токен вашего бота")
    return "!", 200

server.run(host="0.0.0.0", port=os.environ.get('PORT', 5000))

Буду благодарен за помощь.

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Stabiles Hosting des sozialen Netzwerks EVILEG. Wir empfehlen VDS-Hosting für Django-Projekte.

Magst du es? In sozialen Netzwerken teilen!

11
nayk1982
  • 3. August 2019 08:25

Добрый день.
Посмотрите для начала статус установленного WebHook, нет ли ошибок с сертификатом безопасности. Это можно сделать из командной строки:

curl https://api.telegram.org/bot<TOKEN>/getWebhookInfo
    Arrow
    • 4. August 2019 09:06

    Добрый день.

    Попробовал реализовать через сертефикат и изменил код:

    from telebot import TeleBot, types
    from os.path import join
    from django.conf import settings
    from rest_framework.response import Response
    from rest_framework.views import APIView
    from TelegramBot.settings import TOKEN
    
    
    bot = TeleBot(settings.TOKEN, threaded=False)
    
    
    class UpdateBot(APIView):
        def post(self, request):
            # Сюда должны получать сообщения от телеграм и далее обрабатываться ботом
            json_str = request.body.decode('UTF-8')
            update = types.Update.de_json(json_str)
            bot.process_new_updates([update])
    
            return Response({'code': 200})
    
    
    @bot.message_handler(commands=['start'])
    def start_message(message):
        # User написал /start в диалоге с ботом
        text = '<b>Настройка бота!</b>\n\n'
        text += 'Чтобы пначать использовать бата и настроить его по Вашим предпочтениям ответьте на следующие вопросы.\n\n'
        text += '......................'
    
        keyboard = types.InlineKeyboardMarkup()
        key_begin = types.InlineKeyboardButton(text='🖊️ Начать', callback_data='begin')
        keyboard.add(key_begin)
    
        bot.send_message(message.chat.id, text=text, reply_markup=keyboard, parse_mode='HTML')
    
    ..............................
    
    # Webhook
    CERTIFICATE = join(settings.BASE_DIR, 'PUBLIC.pem')
    bot.set_webhook(url="https://myapp.pythonanywhere.com/", certificate=open(CERTIFICATE),
                    max_connections=100000)
    

    Выполнил команду:

    curl https://api.telegram.org/bot<TOKEN>/getWebhookInfo
    

    Результат:

    {"ok":true,"result":{"url":"https://myapp.pythonanywhere.com/","has_custom_certificate":true,"pending_update_count":6,"last_error_date":1564923410,"last_error_message":"SSL error {error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed}","max_connections":100}}
    
      Arrow
      • 4. August 2019 10:04

      В логах pythonenywhere:

      Request: method=get url=https://api.telegram.org/bot<KEY>/setWebhook params={'url': 'https://myapp.pythonanywhere.com/', 'max_connections': 100000} files={'certificate': <_io.TextIOWrapper name='/home/admin/Bot/PUBLIC.pem' mode='r' encoding='UTF-8'>}
      (apihelper.py:55 uWSGIWorker1Core0) DEBUG - TeleBot: "The server returned: 'b'{"ok":true,"result":true,"description":"Webhook was set"}''"
      The server returned: 'b'{"ok":true,"result":true,"description":"Webhook was set"}'
      

      Проверка:

      curl https://api.telegram.org/bot<KEY>/getWebhookInfo
      {"ok":true,"result":{"url":"https://myapp.pythonanywhere.com/","has_custom_certificate":true,"pending_update_count":7,"last_error_date":1564927364,"last_error_message":"SSL error {error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed}","max_connections":100}}
      
        nayk1982
        • 4. August 2019 11:31

        Сертификат не проходит проверку. У меня такое было, когда пытался использовать самоподписанный сертификат. Решилось установкой сертификата Lets Encrypt. После этого WebHook заработал без проблем и сообщения стали приходить.

          Arrow
          • 4. August 2019 13:07
          • (bearbeitet)

          Попробовал создать сертефикат при помощи acme-tiny (Let's Encrypt certificates):

          openssl genrsa 4096 > account.key
          

          В коде изменил:

          # Webhook
          CERTIFICATE = join(settings.BASE_DIR, 'account.key')
          bot.set_webhook(url="https://myapp.pythonanywhere.com/", certificate=open(CERTIFICATE),
                          max_connections=100000)
          

          И получил в логах:

          Request: method=get url=https://api.telegram.org/bot<KEY>/setWebhook params={'url': 'https://myapp.pythonanywhere.com/', 'max_connections': 100000} files={'certificate': <_io.TextIOWrapper name='/home/admin/Bot/account.key' mode='r' encoding='UTF-8'>}
          The server returned: 'b'{"ok":false,"error_code":400,"description":"Bad Request: bad webhook: Failed to set custom certificate file"}''
          

          Сервер его вообще не принимает. C рекомендуемым certbot то же самое.

            Arrow
            • 5. August 2019 08:16

            Решил уйти от сертификата к простому токену.

            Изменил код на:

            Файл urls.py приложения бота:

            from django.urls import path
            from bot.views import UpdateBot
            from django.conf import settings
            from django.views.decorators.csrf import csrf_exempt
            
            
            urlpatterns = [
                path('{}'.format(settings.TOKEN), csrf_exempt(UpdateBot.as_view()), name='update'),
            ]
            
            

            Код views.py приложения бота:

            from rest_framework.response import Response
            from django.http import HttpResponse
            from django.views import View
            from telebot import TeleBot, types, logger
            from bot.models import User, Category, Content, Country, Link, User_category, User_content, SentLinks
            
            
            bot = TeleBot(settings.TOKEN, threaded=False)
            
            
            class UpdateBot(View):
                def get(self, request, *args, **kwargs):
                    return HttpResponse("Бот запусчен и работает.")
            
                def post(self, request, *args, **kwargs):
                    json_str = request.body.decode('UTF-8')
                    update = types.Update.de_json(json_str)
                    bot.process_new_updates([update])
            
                    return Response({'code': 200})
            
            
            @bot.message_handler(commands=['start'])
            def start_message(message):
                # User написал /start в диалоге с ботом
                text = '<b>Настройка бота!</b>\n\n'
                text += 'Чтобы пначать использовать бата и настроить его по Вашим предпочтениям ответьте на следующие вопросы.\n\n'
                text += '......................'
            
                keyboard = types.InlineKeyboardMarkup()
                key_begin = types.InlineKeyboardButton(text='🖊️ Начать', callback_data='begin')
                keyboard.add(key_begin)
            
                bot.send_message(message.chat.id, text=text, reply_markup=keyboard, parse_mode='HTML')
            
            ..............................
            
            # Webhook
            bot.remove_webhook()
            bot.set_webhook(url=f"{settings.DOMAIN}/{settings.TOKEN}")
            # DOMAIN = "https://myapp.pythonanywhere.com"
            # 
            

            Сразу, когда перезагрузил веб приложение в pythonenywhere пришло куча сообщений от бота (реакция на мои сообщения /start), а затем опять тишина.

            При проверке через терминал получаю:

            $ curl https://api.telegram.org/bot<KEY>/getWebhookInfo
            {"ok":true,"result":{"url":"https://myapp.pythonanywhere.com/<KEY>","has_custom_certificate":false,"pending_update_count":19,"last_error_date":1565006606,"last_err
            or_message":"Wrong response from the webhook: 500 Internal Server Error","max_connections":40}}
            

            В логах тишина. Немогу понять, что не верно. Особенно если по началу все вроде бы заработало.

              Arrow
              • 5. August 2019 09:44

              Все заработало.

              Только теперь не знаю как быть. Толи я, что-то не то сделал, то ли телеграм тупит.
              Постоянно сробатывает Одна и таже процедура в коде, а именно часть 'Рассказать о боте':

              def menu(message):
                  try:
                      if message.text == 'Рассказать о боте':
                      """ Эта часть отрабатывает """
                          markup = types.InlineKeyboardMarkup()
                          forward_btn = types.InlineKeyboardButton(
                              text='...............',
                              url='https://t.me/share/url?url=https://t.me/' + settings.BOT_NAME)
                          markup.add(forward_btn)
                          text = '.............. '
                          bot.send_message(message.chat.id, text=text,
                                           reply_markup=markup, parse_mode='html')
                          bot.register_next_step_handler(message, menu)
              
                      elif message.text == 'Поиск':
                          bot.delete_message(message.chat.id, message.message_id)
                          markup = types.InlineKeyboardMarkup()
                          search_channel = types.InlineKeyboardButton(
                              text='.....', callback_data='.....')
                          search_bot = types.InlineKeyboardButton(
                              text='....', callback_data='.....')
                          search_chat = types.InlineKeyboardButton(
                              text='....', callback_data='....')
                          markup.row(search_channel)
                          markup.row(search_bot)
                          markup.row(search_chat)
                          text = '.......'
                          bot.send_message(message.chat.id, text=text,
                                           parse_mode='html', reply_markup=markup)
                          bot.register_next_step_handler(message, owner_menu)
              
                      elif message.text == 'Изменить настройки':
                          bot.register_next_step_handler(message, owner_menu)
                          manage_interests(message)
              
              
                      else:
                          raise Exception()
                  except:
                      bot.register_next_step_handler(message, owner_menu)
              

              Подскажите пожалуйста, где я туплю. Спасибо.

                nayk1982
                • 5. August 2019 12:05

                Возможно телеграм повторяет одно и то же сообщение, это бывает, если в ответ на сообщение от телеграм ваш бот ничего не отвечает. Нужно на сообщения посылать ответ:

                {"ok": true}
                

                И еще, в каждом сообщении есть счетчик update_id, для каждого чата. Можно его запоминать и сравнивать, чтоб не дублировать ответы.

                  Arrow
                  • 6. August 2019 07:07
                  • Die Antwort wurde als Lösung markiert.

                  Спасибо.

                  Что-то не особо помогает.

                  Похоже что у pythonanywhere какие-то проблемы с передачей обновлений боту или у телеграм. То приходят данные, то нет. Сейчас проверка показывает:

                  {"ok":true,"result":{"url":"https://myapp.pythonanywhere.com/<Key>","has_custom_certificate":false,"pending_update_count":0,"max_connections":40}}
                  

                  А сообщения вообще перестали приходить.

                  Можете привести пример реализации ответа {"ok": true} или подредактировать мое, может я уже туплю на ровном месте.

                  Мой код:

                  urls.py приложения бота:

                  from django.urls import path
                  from django.conf import settings
                  from django.views.decorators.csrf import csrf_exempt
                  from bot.views import UpdateBot, Check
                  
                  app_name = 'PlayMarketBot'
                  
                  urlpatterns = [
                      path('{}'.format(settings.TOKEN), csrf_exempt(UpdateBot.as_view()), name='update'),
                      path('/', csrf_exempt(Check.as_view()), name='check'),
                  ]
                  

                  views.py приложения бота:

                  from django.conf import settings
                  from random import randint
                  from django.http import HttpResponse
                  from django.views import View
                  from telebot import TeleBot, types, logger
                  from bot.models import User, Category, Content, Country, Link, User_category, User_content, SentLinks
                  from threading import Timer
                  import logging
                  
                  logger.setLevel(logging.DEBUG)
                  
                  __author__ = '@masterarrow'
                  
                  bot = TeleBot(settings.TOKEN)
                  
                  update_id = None
                  
                  
                  class Check(View):
                      def get(self, request, *args, **kwargs):
                          return HttpResponse("Бот запусчен и работает.")
                  
                  
                  class UpdateBot(View):
                      def post(self, request, *args, **kwargs):
                          global update_id
                          json_str = request.body.decode('UTF-8')
                          update = types.Update.de_json(json_str)
                          if update_id != update.update_id:
                              bot.process_new_updates([update])
                              update_id = update.update_id
                  
                          return Response(b'{"ok":true,"result":[]}')
                  
                  
                  @bot.message_handler(commands=['start'])
                  def start_message(message):
                      bot.delete_message(message.chat.id, message.message_id)
                      text = '/////////////////////////'
                  
                      keyboard = types.InlineKeyboardMarkup()
                      key_begin = types.InlineKeyboardButton(text='🖊️ Начать', callback_data='begin')
                      keyboard.add(key_begin)
                  
                      bot.send_message(message.chat.id, text=text, reply_markup=keyboard, parse_mode='HTML')
                  
                  .............................................
                  
                  def menu(message):
                      try:
                          # Always working ???
                          if message.text == 'Рассказать':
                              markup = types.InlineKeyboardMarkup()
                              forward_btn = types.InlineKeyboardButton(
                                  text='Пригласить',
                                  url='https://t.me/share/url?url=https://t.me/' + settings.BOT_NAME)
                              markup.add(forward_btn)
                              text = '......................................'
                              bot.send_message(message.chat.id, text=text,
                                               reply_markup=markup, parse_mode='html')
                              bot.register_next_step_handler(message, menu)
                  
                          elif message.text == 'Поиск':
                              bot.delete_message(message.chat.id, message.message_id)
                              markup = types.InlineKeyboardMarkup()
                              search_channel = types.InlineKeyboardButton(
                                  text='......', callback_data='.............')
                              search_bot = types.InlineKeyboardButton(
                                  text='.......', callback_data='.........')
                              search_chat = types.InlineKeyboardButton(
                                  text='.....', callback_data='...........')
                              markup.row(search_channel)
                              markup.row(search_bot)
                              markup.row(search_chat)
                              text = '....................'
                              bot.send_message(message.chat.id, text=text,
                                               parse_mode='html', reply_markup=markup)
                              bot.register_next_step_handler(message, menu)
                  
                          elif message.text == 'Настроить':
                              manage_interests(message)
                              bot.register_next_step_handler(message, menu)
                  
                          else:
                              raise Exception()
                      except:
                          bot.register_next_step_handler(message, menu)
                  
                  
                  bot.remove_webhook()
                  bot.set_webhook(url=f"{settings.DOMAIN}/{settings.TOKEN}")
                  
                  

                  Большое спасибо за помощь.

                    nayk1982
                    • 6. August 2019 22:04

                    На python не делал, поэтому не подскажу, как правильно.
                    Бросаются в глаза последние строчки из views.py:

                    bot.remove_webhook()
                    bot.set_webhook(url=f"{settings.DOMAIN}/{settings.TOKEN}")
                    

                    Они что, выполняются каждый раз? Если да, то это не правильно. WebHook устанавливается один раз и после этого ничего с ним делать не нужно (пока адрес вашего сервера не изменится или токен бота). Я бы вообще управление вэбхуком из кода убрал, это можно из консоли сделать одной строчкой, когда понадобится.

                    Могу рассказать, как делал я своего бота. На сервере для начала создал простой скрипт для приема сообщений и вывода в лог-файлы всего, что принял. Это поможет разобраться, как работает Telegram API. WebHook поставил на этот скрипт, добавил бота в тестовый чат Telegram и просто изучал, как работает API, параллельно читая документацию. Потом начал потихоньку наполнять этот скрипт и делать дополнительные если нужно. По началу лучше логировать все, что только можно, легче разобраться.

                      Arrow
                      • 7. August 2019 02:24

                      Спасибо за подсказку.
                      Да я и не подумал, что код установки webhook можно вообще убрать или вынести.

                      Большое спасибо за помощь.

                        Kommentare

                        Nur autorisierte Benutzer können Kommentare posten.
                        Bitte Anmelden oder Registrieren
                        Letzte Kommentare
                        A
                        ALO1ZE19. Oktober 2024 08:19
                        Fb3-Dateileser auf Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
                        ИМ
                        Игорь Максимов5. Oktober 2024 07:51
                        Django – Lektion 064. So schreiben Sie eine Python-Markdown-Erweiterung Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
                        d
                        dblas55. Juli 2024 11:02
                        QML - Lektion 016. SQLite-Datenbank und das Arbeiten damit in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
                        k
                        kmssr8. Februar 2024 18:43
                        Qt Linux - Lektion 001. Autorun Qt-Anwendung unter Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
                        Qt WinAPI - Lektion 007. Arbeiten mit ICMP-Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
                        Jetzt im Forum diskutieren
                        J
                        JacobFib17. Oktober 2024 03:27
                        добавить qlineseries в функции Пользователь может получить любые разъяснения по интересующим вопросам, касающимся обработки его персональных данных, обратившись к Оператору с помощью электронной почты https://topdecorpro.ru…
                        JW
                        Jhon Wick1. Oktober 2024 15:52
                        Indian Food Restaurant In Columbus OH| Layla’s Kitchen Indian Restaurant If you're looking for a truly authentic https://www.laylaskitchenrestaurantohio.com/ , Layla’s Kitchen Indian Restaurant is your go-to destination. Located at 6152 Cleveland Ave, Colu…
                        КГ
                        Кирилл Гусарев27. September 2024 09:09
                        Не запускается программа на Qt: точка входа в процедуру не найдена в библиотеке DLL Написал программу на C++ Qt в Qt Creator, сбилдил Release с помощью MinGW 64-bit, бинарнику напихал dll-ки с помощью windeployqt.exe. При попытке запуска моей сбилженной программы выдаёт три оши…
                        F
                        Fynjy22. Juli 2024 04:15
                        при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …

                        Folgen Sie uns in sozialen Netzwerken