虽然每天都在用Telegram Bot,但一直没深入了解过,最近折腾了一下,发现TG机器人能实现的功能太多了,非常实用,API也很完善,然而还是遇到了不少问题,特别是用Nginx配合Webhook的时候,搞了一晚上都没成功。因此简单的记录一下,排一下坑。

准备工作

这里跳过Bot的申请过程。网上有多种语言的集成API,PHP、Node.js、Rust、Python等等,虽然没学过Python,但看了看文档相对简单,因此就决定是它了。API则选择了pyTelegramBotAPI,pyTelegramBotAPI推荐使用Python3.6-3.9版本,我使用的是Python3.7.3

安装Virtual Environment

apt install python3-pip
pip3 install virtualenv

创建一个工程目录:

mkdir /home/tg_bot
cd /home/tg_bot
virtualenv first_tg_bot

激活virtualenv:

source first_tg_bot/bin/activate

接下来的依赖就会安装到虚拟环境中,要退出环境的话使用deactivate命令。

如果以后要将项目转移到别的地方,先进入虚拟环境中导出所有包到requirements.txt

pip3 freeze > requirements.txt

然后在新的环境中安装:

pip3 install -r requirements.txt

安装所需依赖

pip3 install pyTelegramBotAPI
pip3 install uwsgi
pip3 install flask

设置Webhook

获取Bot的更新可以通过Polling或者Webhook,这里选择更适合生产环境的Webhook,设置Webhook很简单,只需要访问一下下面的链接就行了:

https://api.telegram.org/bot{bot_token}/setWebhook?url={your_link}

例如:

https://api.telegram.org/bot123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11/setWebhook?url=https://www.example.com

之后可以通过下面的链接查看设置的Webhook信息:

https://api.telegram.org/bot{bot_token}/getWebhookInfo

配置uWSGI

uWSGI是一个Web Server,在这里主要是为了用uwsgi协议充当网关作用,连接Nginx和Python应用程序。uwsgi执行可以通过命令行或者文件配置,这里使用.ini文件配置,创建一个uwsgi.ini文件:

[uwsgi]
project = first_tg_bot
path = /home/tg_bot
socket = %(path)/%(project).sock
chmod-socket = 666
wsgi-file = %(path)/first_tg_bot.py
callable = app
master = true
vacuum = true
processes = 4
threads = 2

创建一个脚本first_tg_bot.sh,方便执行:

#!/usr/bin/env bash
cd /home/tg_bot
source first_tg_bot/bin/activate
uwsgi uwsgi.ini

记得chmod +x first_tg_bot.sh,之后可以用screen启动命令保持运行或者创建守护进程:

vim /lib/systemd/system/tg_bot.service
[Unit]
Description=Telegram Bot
Wants=network.target
After=syslog.target network-online.target

[Service]
Type=simple
PIDFile=/home/tg_bot/tg_bot.pid
WorkingDirectory=/home/tg_bot
ExecStart=/home/tg_bot/first_tg_bot.sh
Restart=on-failure
RestartSec=10
KillMode=process

[Install]
WantedBy=multi-user.target

配置Nginx

Webhook只能使用https,因此需要签发证书,反代部分配置如下:

    location /BOT_TOKEN {
        include uwsgi_params;
        uwsgi_pass unix:/home/tg_bot/first_tg_bot.sock;
    }

编写Bot

这里的first_tg_bot.py是一个简单的Bot示例,来源于pyTelegramBotAPI的webhook_examples并稍作修改从Nginx获取内容。

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import time
import telebot
from flask import Flask, request, abort

# Bot Token
API_TOKEN = ''
# Your domain
WEBHOOK_HOST = ''
# Port
WEBHOOK_PORT = 443

WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT)
WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN)

bot = telebot.TeleBot(API_TOKEN, threaded=False)

app = Flask(__name__)

@app.route('/', methods=['GET', 'HEAD'])
def index():
    return ''

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


# Handle '/start'
@bot.message_handler(commands=['start'])
def send_welcome(message):
    bot.send_message(message.chat.id, 'Nyanpasu~~')

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


bot.remove_webhook()
time.sleep(0.1)
bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH)

到这里,一个简单的Bot就完成了,接下来运行脚本,访问你的Bot发送/start就可以看到自动回复,输入其他文本信息就会变成复读机( ̄、 ̄)

如果要停止运行Bot可以直接使用pkill -9 uwsgikill掉uwsgi进程。接下来,你可以使用Python配合API实现任何你想实现的功能。