惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Threatpost
The Hacker News
The Hacker News
AWS News Blog
AWS News Blog
Spread Privacy
Spread Privacy
T
Tenable Blog
C
CERT Recently Published Vulnerability Notes
Cisco Talos Blog
Cisco Talos Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Securelist
P
Privacy & Cybersecurity Law Blog
Know Your Adversary
Know Your Adversary
T
The Exploit Database - CXSecurity.com
Latest news
Latest news
D
Darknet – Hacking Tools, Hacker News & Cyber Security
I
Intezer
F
Fortinet All Blogs
Engineering at Meta
Engineering at Meta
Simon Willison's Weblog
Simon Willison's Weblog
The Register - Security
The Register - Security
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
L
Lohrmann on Cybersecurity
C
Cyber Attacks, Cyber Crime and Cyber Security
Microsoft Azure Blog
Microsoft Azure Blog
P
Proofpoint News Feed
H
Help Net Security
T
Threat Research - Cisco Blogs
D
DataBreaches.Net
S
Schneier on Security
Cyberwarzone
Cyberwarzone
Google DeepMind News
Google DeepMind News
P
Privacy International News Feed
S
Secure Thoughts
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Recorded Future
Recorded Future
C
Cybersecurity and Infrastructure Security Agency CISA
MyScale Blog
MyScale Blog
M
MIT News - Artificial intelligence
Stack Overflow Blog
Stack Overflow Blog
IT之家
IT之家
人人都是产品经理
人人都是产品经理
NISL@THU
NISL@THU
博客园 - Franky
T
Tor Project blog
G
GRAHAM CLULEY
博客园 - 【当耐特】
Jina AI
Jina AI
Security Archives - TechRepublic
Security Archives - TechRepublic
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
A
About on SuperTechFans
Hacker News - Newest:
Hacker News - Newest: "LLM"

atpX

自建 S3 兼容对象存储服务 Garage 听风的歌 偶尔是深夜就好了 新玩具 AirPods Pro 3 公交车上的时间 从 WHOIS 到 RDAP 通过 WireGuard 访问 NFS 共享文件 自建音乐串流服务探索 我停止了探索 Fediverse 互联网背景噪音 我的数字生活降级 小城与确定性的墙 一只特立独行的猪 Fediverse 与社交 承认的勇气 mediaX - 轻量书影音记录管理工具 我的博客写作流程 网站新增 Misc 页面 擅长对线的鲍勃 在 Chroot 环境下使用 Rsync 同步 再见 JavaScript 当我玩博客时我在玩什么 为什么我的博客没有友链页面 小熊猫与大熊猫 (HDR 照片测试) 是时候为网站开启 HTTP/3 支持了吗 使用 AdGuard Home 搭建自用 DoH 服务 谈谈读书与消遣 2023 年终总结 西安两日游 从 AirPods「升级」到 EarPods Docker 搭建去中心化的微博客平台 Mastodon 聊聊 iPhone 15 Pro 使用感受 能否将 TOTP 二次验证存放在密码管理器里 认识史蒂夫·乔布斯 与“锤哥”克里斯·海姆斯沃斯一起养生 逃离爆炸的信息 你想活出怎样的人生 重温《龙珠》动画 为什么关闭评论 从域名注册商到 DNS 服务,找到自己的组合 如何提高用户网页阅读体验 从 Typecho 迁移到 Hugo 个人博客的最终归宿是静态网站吗 我选择了放弃 jQuery 关于我的小破站折腾速度优化这件事 记一次家庭网络折腾 贴一些 SmokePing 记录 我的域名邮箱选择 不存在的语录 Typecho 评论验证插件 CaptchaPlus 谈谈写字这件事 新的网站 Logo 探索 曲线解决 Typecho 图片占位抖动问题 自建 vaultwarden / bitwarden_rs 密码管理器 使用 Plausible 自建网站流量统计分析工具 如何优雅地徒手剥开火腿肠 简单的集中隔离生活记录 新玩具 M2 MacBook Air 使用 acme.sh 申请 Google 公共证书 Docker 搭建去中心化的微博客平台 Misskey 搭建 Lsky Pro 兰空图床 折腾树莓派系统的一天 我的吉卜力之旅 使用 Uptime Kuma 自建服务器/网站在线率监控 Spartan Host Review - Best Hosting & Service Support Debian 下 Nginx 配合 Fail2Ban 减少恶意扫描和攻击 升级 Typecho 到最新开发版本 v1.2.0 Linux rm 命令详解 Debian 系统安装 Docker 教程 BandwagonHost - Reliable Blog and Business VPS Hosting 使用 Isso 为 Hugo/Hexo 等静态网站添加评论功能 推荐 5 款免费开源的网站流量分析统计工具 搭建 Shynet 网站流量统计分析工具 聊聊 DDoS 攻击那些事 Hugo 使用 Fancybox 实现图片灯箱/放大功能 从 Debian 10 升级到 Debian 11 教程 使用 Umami 自建网站流量统计分析工具 Linux 一次执行多个命令的 3 种方式 Typecho 修改永久链接后旧链接 301 跳转到新链接 从不同需求推荐几家稳定可靠的 VPS 服务商 Build EchoIP service with Docker 浅谈一下这些年折腾过的 VPS 单线复用解决一根网线同时 IPTV 和宽带问题 不完全吃灰的树莓派 使用 Nginx 实现 TCP 四层反向代理 画画真难 我读村上春树 使用 acme.sh 自动签发和更新证书 卡拉马佐夫式悲剧 安装 File Browser 轻量网盘工具 新玩具树莓派到手 Linux 定时自动备份数据到 OneDrive/Google Drive 记一次服务器崩溃 使用 rsync 同步文件 短信的消失 幻灭的艺术家 新玩具戴尔 S2721DGF 开箱 蝴蝶 用 Docker 整合 SeafilePro 搭建私人云盘 搭建自己的 kms 服务器
开发一个自己的 Telegram Bot
ATP · 2021-03-02 · via atpX
Telegram bot

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

准备工作

这里跳过 Bot 的申请过程。网上有多种语言的集成 API,PHP、Node.js、Rust、Python 等等,但人生苦短,我选 Python。API 则选择了 pyTelegramBotAPI

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

配置虚拟环境

Debian 11 下配置

apt install python3-pip
pip3 install virtualenv

创建一个工程目录:

mkdir /home/mybot && cd /home/mybot

创建并激活虚拟环境:

virtualenv env
source env/bin/activate

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

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

pip3 freeze > requirements.txt

然后在新的环境中安装:

pip3 install -r requirements.txt

Debian 12 下配置

今天(2023-10-08)将系统升级到 Debian 12 (bookworm) 后发现 uWSGI 启动失败,提示错误:

uwsgi: error while loading shared libraries: libpython3.9.so.1.0: cannot open shared

显然是因为 Debian 12 升级了系统 Python 版本,而我升级系统后执行了 autoclean,于是准备重新安装初始化一个虚拟环境,执行 pip3 install virtualenv 后提示:

error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install.
  
    If you wish to install a non-Debian-packaged Python package,
    create a virtual environment using python3 -m venv path/to/venv.
    Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
    sure you have python3-full installed.
  
    If you wish to install a non-Debian packaged Python application,
    it may be easiest to use pipx install xyz, which will manage a
    virtual environment for you. Make sure you have pipx installed.
  
    See /usr/share/doc/python3.11/README.venv for more information.

发现 Python 3.11+ 会默认要求用户在 venv 虚拟环境中安装 Package,于是按照它建议的来:

apt install python3-venv

创建一个工程目录:

mkdir /home/mybot && cd /home/mybot

创建并激活虚拟环境:

python3 -m venv env
source env/bin/activate

安装所需依赖

pip install pyTelegramBotAPI
pip install flask
pip install uwsgi 
# 或者不用 uwsgi,使用更简单的 gunicorn
# pip install gunicorn

设置 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

配置 WSGI 服务器

开发测试环境中直接使用 Flask 接收 Webhook 即可,如果是部署生产环境,建议使用 WSGI 服务器网关接口提高性能。常见的 WSGI 服务器可以选择 uWSGI 或者 Gunicorn,uWSGI 是由 C 语言编写,通过编译安装,Gunicorn 由 Python 编写。一般来说,uWSGI 的性能更好,尤其是在高并发环境下,而 Gunicorn 更加简单,容易上手,这里你可以根据自己的需求选择。

使用 uWSGI

运行 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

启动服务:

uwsgi --ini uwsgi.ini

使用 Gunicorn

在项目路径下新建一个 gunicorn_config.py 配置文件:

bind = '127.0.0.1:8000'  # 绑定的主机和端口
workers = 3  # worker 进程的数量,建议的数量是 (2*CPU)+1
worker_class = 'sync'  # worker 进程的类型
timeout = 30  # 超时时间,单位为秒
keepalive = 2  # keep-alive 连接的最大数量
errorlog = '-'  # 错误日志文件,默认输出到 stdout
accesslog = '-'  # 访问日志文件,默认输出到 stdout
loglevel = 'info'  # 日志级别:debug, info, warning, error, critical
# access_log_format = '%({X-Forwarded-For}i)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"' # 获取真实 IP

启动服务:

env/bin/gunicorn -c gunicorn_config.py mybot:app

创建守护进程

投入生产环境运行时,可以使用 systemd 控制进程。这里简单写一个使用 systemd 来创建进程并保持后台运行的示例:

vim /etc/systemd/system/mybot.service

使用 uWSGI:

[Unit]
Description=uWSGI - Telegram Bot by atpx.com
After=network.target

[Service]
WorkingDirectory=/home/mybot
ExecStart=/home/mybot/env/bin/uwsgi --ini /home/mybot/uwsgi.ini
ExecStop=/home/mybot/env/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

使用 Gunicorn:

[Unit]
Description=Gunicorn - Telegram Bot by atpx.com
After=network.target

[Service]
WorkingDirectory=/home/mybot
ExecStartPre=/bin/bash -c 'source /home/mybot/env/bin/activate'
ExecStart=/home/mybot/env/bin/gunicorn -c gunicorn_config.py mybot:app
ExecStopPost=/bin/bash -c 'deactivate'
Restart=on-failure
RestartSec=20s

[Install]
WantedBy=multi-user.target

配置 Nginx

通常建议再加一层 Nginx 反代到 WSGI 服务器以提高安全性和性能。同时 Webhook 只能使用 https,因此你需要签发 SSL 证书,可以申请免费的 Let’s Encrypt 证书,具体步骤可查看这篇文章 使用acme.sh自动签发和更新证书,Nginx 配置文件中的反代部分配置如下:

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

# 或者使用 Gunicorn:
location /SECRET_PATH {
    proxy_redirect off;
    proxy_set_header Host $http_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-Proto $scheme;
    proxy_pass http://127.0.0.1:8000;
}

其中的 SECRET_PATH 可以是任意的字符串,推荐使用足够长的随机字符串,后面编写 Bot 代码时需要和这里一致。

编写第一个 Bot

这里的 /home/mybot/mybot.py 是一个简单的 Bot 示例,来源于 pyTelegramBotAPI 的 webhook_examples,并由 atpX 稍作修改。

#!/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 反代中设置的自定义路径
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)

# 使用 Gunicorn 时注释掉下方三行
bot.remove_webhook()
time.sleep(0.1)
bot.set_webhook(url=WEBHOOK_URL)

# 使用 Gunicorn 时取消注释下方两行
# if __name__ == '__main__':
#     app.run(host='127.0.0.1', port=8000)

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

Telegram 简单的复读机器人
Telegram 简单的复读机器人。

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