telegram-bot

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

准备工作

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

以下所有演示为在 Debian 10 系统中使用 root 用户进行。

安装 Virtual Environment

apt install python3-pip
pip3 install virtualenv

创建一个工程目录:

mkdir /home/mybot
cd /home/mybot
virtualenv env

激活virtualenv:

source env/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 = mybot
path = /home/mybot
wsgi-file = %(path)/mybot.py
socket = %(path)/run/%(project).sock
pidfile = %(path)/run/uwsgi.pid
stats = %(path)/run/uwsgi.status
chmod-socket = 666
callable = app
master = true
vacuum = true
processes = 4
threads = 2

为了方便运行调试,你可以创建一个脚本 mybot.sh

#!/usr/bin/env bash
cd /home/mybot
source env/bin/activate
uwsgi --ini uwsgi.ini

记得赋予脚本可执行权限 chmod +x mybot.sh,后台的运行的方式有很多种,可以在前面的脚本最后一行改为:

uwsgi -d --ini uwsgi.ini

或者使用 nohup 运行脚本也可以。如果要停止运行 Bot 可以直接使用 pkill -9 uwsgi kill 掉 uwsgi 进程。等投入生产环境运行时,推荐使用下面介绍的通过 systemd 控制进程。

创建守护进程

这里简单写一个使用 systemd 来创建进程并保持后台运行的示例:

vim /etc/systemd/system/mybot.service
[Unit]
Description=uWSGI - Telegram Bot by ATP BLOG - atpx.com
After=network.target

[Service]
WorkingDirectory=/home/mybot
ExecStart=/home/mybot/env/bin/uwsgi --ini /home/mybot/uwsgi.ini
ExecStop=/home/mybotenv/bin/uwsgi --stop /home/mybot/run/uwsgi.pid
ExecReload=/home/mybot/env/bin/uwsgi --reload /home/mybot/run/uwsgi.pid
Restart=on-failure
RestartSec=20s

[Install]
WantedBy=multi-user.target

配置 Nginx

Webhook 只能使用 https,因此你需要签发 SSL 证书,可以申请免费的 Let's Encrypt 证书,具体步骤可查看这篇文章 使用acme.sh自动签发和更新证书,Nginx 配置文件中的反代部分配置如下:

location /SECRET_PATH {
    include uwsgi_params;
    uwsgi_pass unix:/home/mybot/run/mybot.sock;
}

其中的 SECRET_PATH 可以是任意的字符串,推荐使用足够长的随机字符串,这里用 ATPBLOG404 作为示例,下方编写 Bot 时也会用到。

编写第一个 Bot

这里的 /home/mybot/mybot.py 是一个简单的 Bot 示例,来源于 pyTelegramBotAPI 的 webhook_examples,并由 ATP BLOG 稍作修改,以便从 Nginx 获取内容。

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

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

# Bot Token / 你申请到的 Bot API Token
API_TOKEN = ''
# Your domain / 前面 Webhook 中设置的域名,例如 https://www.example.com
WEBHOOK_HOST = ''
# Nginx reverse proxy path / Nginx 反代中设置的自定义路径,例如 ATPBLOG404
SECRET_PATH = ''

WEBHOOK_URL = "%s/%s" % (WEBHOOK_HOST, SECRET_PATH)

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)

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

my-first-telegram-bot

接下来,你可以使用 Python 配合 API 实现任何你想实现的功能。