Как заставить работать nginx+gunicorn+telebot (telegram webhooks)?

Здравствуйте!

Дано:
1. Бот, написанный на flask+python3+telebot.
2. Дроплет на DigitalOcean

Хочу запустить бота на вебхуках. В идеале хотелось бы запускать два и больше ботов на одном дроплете.

Локально, с помощью long polling бот работает. На дроплете он тоже работал c самоподписными сертификатами просто на flaks. Хотелось бы использовать Nginx и Gunicorn, так как это выглядит верным решением (так ведь?)

Проблема:
В логах telebot'a вижу, что вебхук установлен, установлен куда надо. Nginx работает, gunicorn крутиться, но бот не отвечает. Что не так? Может где-то в корне что-то не так?

Я руководствовался этим https://www.digitalocean.com/community/tutorials/h... и немного этим https://groosha.gitbooks.io/telegram-bot-lessons/c... Прикладываю ниже исходники.

run.py
from test_bot import app

if __name__ == '__main__':
    app.run()


test_bot/__init__.py
import flask
import telebot
import config

app = flask.Flask(__name__)
app.config.from_object(config.DefaultConfig)
from . import bot_core

@app.route('/bot', methods=['POST'])
def webhook():
    if flask.request.headers.get('content-type') == 'application/json':
        json_string = flask.request.get_data().decode('utf-8')
        update = telebot.types.Update.de_json(json_string)
        bot_core.bot.process_new_updates([update])
        return ''
    else:
        flask.abort(403)


test_bot/bot_core.py
import telebot

bot = telebot.TeleBot(app.config['BOT_API_TOKEN'])

# Handle '/start'
@bot.message_handler(commands=['start'])
def start_message(message):
    bot.send_message(message.chat.id, 'Hello!')
# Remove webhook, it fails sometimes the set if there is a previous webhook
bot.remove_webhook()
sleep(1)
# Set webhook
bot.set_webhook('https://droplet_ip/bot')


/etc/systemd/system/test_bot.service
[Unit]
Description=Gunicorn instance to serve test_bot
After=network.target

[Service]
User=netolyrg
Group=www-data
WorkingDirectory=/home/netolyrg/test_bot
Environment="PATH=/home/netolyrg/test_bot/flask_venv/bin"
ExecStart=/home/netolyrg/bmstubot_test/test_bot/bin/gunicorn --workers 1 --bind unix:test_bot.sock -m 007 run:app

[Install]
WantedBy=multi-user.target


/etc/nginx/sites-available/test_bot
server {
    server_name droplet_ip;

    listen 443 ssl;

    access_log /home/netolyrg/logs/access.log;
    error_log  /home/netolyrg/logs/error.log;

    ssl_certificate /etc/nginx/ssl/nginx.crt;
    ssl_certificate_key /etc/nginx/ssl/nginx.key;

    location /bot {
        include proxy_params;
        proxy_pass http://unix:/home/netolyrg/test_bot/test_bot.sock;
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Host $server_name;
    }
}
  • Вопрос задан
  • 4072 просмотра
Решения вопроса 1
@netolyrg Автор вопроса
Так, я нашел ошибку.
Я почитал https://core.telegram.org/bots/webhooks и понял, что при самоподписном сертификате нужно в bot.set_webhook('https://droplet_ip/bot') указывать путь до .pem публичного ключа.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
fzfx
@fzfx
18,5 дм
что с правами на unix-сокет? ls -la /home/netolyrg/test_bot/test_bot.sock
вообще навскидку выглядит всё нормально, но проблема в том, что я не настоящий сварщик. единственно, я не в курсе, чем именно у вас занимается bot_core.bot.process_new_updates([update]).
в качестве примера могу кинуть код бота на D для сравнения:
spoiler
import vibe.vibe;
import vibe.stream.tls;
import vibe.data.json;
import std.string;
import std.typecons;

immutable string botName = "somebotname";
immutable string tgWebhookUrl = "/urlforwebhook";
immutable string tApiUrl = "https://api.telegram.org/bot123456789:TOKENISHERE";

void main()
{
        auto settings = new HTTPServerSettings;
	// на каком порту слушать вебхуки, проксируемые сюда nginx'ом
        settings.port = 8080;
	// на каких интерфейсах хоста слушать вебхуки
        settings.bindAddresses = ["::1", "127.0.0.1"];

        auto router = new URLRouter;
	// установка обработчика вебхуков
        router.post(tgWebhookUrl, &tgWebhookDispatcher);

        listenHTTP(settings, router);
        runApplication();
}

// обработчик вебхуков
void tgWebhookDispatcher(HTTPServerRequest req, HTTPServerResponse res)
{
        res.contentType("application/json");
        string resJson;
	// проверка на то, что пришедшее сообщение является replay'ем
        if("reply_to_message" in req.json["message"]) {
		// проверка на то, что в пришедшем сообщении команда для бота
                if("entities" in req.json["message"] &&
                "type" in req.json["message"]["entities"][0] &&
                req.json["message"]["entities"][0]["type"].get!string == "bot_command")
                {
			// диспетчеризация команд для бота и ответ на них
                        switch(req.json["message"]["text"].get!string)
                        {
                                case "/somecommand1@" ~ botName:
                                resJson = q"[{"method":"sendMessage","chat_id":]"
                                ~ to!string(req.json["message"]["chat"]["id"])
                                ~ q"[,"text":"Answer1"}]";
                                break;
                                case "/somecommand2@" ~ botName:
                                resJson = q"[{"method":"sendMessage","chat_id":]"
                                ~ to!string(req.json["message"]["chat"]["id"])
                                ~ q"[,"text":"Answer2"}]";
                                break;
                                default:
                                break;
                        }
                }
        }
        res.writeBody(resJson);
}
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы