直以来,我都在用着别人的 bot ,前不久,心血来潮想做一个 bot ,于是便着手开始了。为什么是从零开始呢,因为我之前并没有了解过 bot 方面的东西,还有,我不会python。。。虽然 bot 也可以用其它语言来进行开发。但使用 python 确实一个不容错过的好选择。一方面,使用的用户多,有较为成熟的解决方案;另一方面,听说 python 较为简单,我也想学习学习,入门python(笑 。在本文中我使用 python 2/3 和 pycharm 进行开发,不过推荐使用 python 3。操作环境Arch Linux。如果你还不曾了解 Telegram,请看之前的文章: 谈谈Telegram&食用教程。本文可能会有部分内容翻译自 Telegram 官方网站,翻译可能出现偏差,如果有条件的话,还是看原版的英文文档比较好。

什么是 Bot(机器人)

机器人是一种特殊的不额外需要手机号码的账号。用户可以通过给机器人发消息来进行交互。从 2015年6月24日 以后,Telegram 正式开始提供开放的 机器人平台。实际上在这之前,Telegram 上已经有大量的 Bots 存在,只不过它们多数是使用 telegram-cli 实现的。但是这些机器人不能够与 Telegram 客户端的UI交互,不能够弹出可选项之类,而且也不能设置是否允许加入群组、是否能够查看所有消息等。而机器人平台具有一些特殊的API接口,比如说能够自定义用户可见的选项、可以在Telegram客户端中添加一个命令菜单等等。更重要的是,机器人API采用的是简单的 http(s) 协议,而不需要像以前那样自己实现一套 MTProto
Bot和普通用户有何不同呢?

  • Bot没有在线状态,也没有没有上次在线时间,界面上只显示标签'bot'。
  • Bot的存储空间是有限的, 服务器处理完毕后,服务器可能会删除较旧的消息。
  • Bot无法与用户进行对话。用户必须将它们添加到组中或首先向它们发送消息。我们可以使用telegram.me/链接或用户名搜索来查找Bot。
  • Bot用户名总是以'bot'结尾(例如@TriviaBot,@ GitHub_bot)。
  • 把Bot加到群里的时候,默认情况下(开启隐私模式),Bot是不会收到所有的消息的;
  • Bot从不吃饭,睡觉或抱怨(除非另有明确规定)。

下图为BotFather

如何拥有一个自己的Bot

很简单,跟 @BotFather 进行对话,他会引导你创建一个机器人的。

这里我创建了一个叫做 Spiribot 的Bot,链接为 t.me/Spiriibot ,API TOKEN为XXXX:XXXXXX(我肯定不会告诉你啦哈哈哈哈哈哈哈哈

搭建开发环境

首先是官方文档:Telegram Bots API,官方文档其实已经非常详尽,Telegram Bot 的一切动作都由发送 get/post 请求来完成,也基本没有奇怪的 header 要求,非常简明了。

python 比较方便,能通过 pip/pip3 来安装开发所需要的库,这里我使用了 python-telegram-bot ,当然也可以选择另一个比较优秀的 pyTelegramBotAPI 。如果你还没有 pip/pip3 ,请参考 Installation

准备venv & python-telegram-bot

Virtual Environment/虚拟环境 是一个包含二进制程序和 shell 脚本的目录。二进制程序包含执行脚本的 python 和安装其它模块的 pip。脚本包括激活环境的脚本,bash,csh 和fish 各有一个。这个虚拟环境模拟了一个完整的 Python 执行环境和需要的模块,将程序运行的环境与系统其它部分隔离开来。

为什么要使用 virtual environment 呢?唔是这样的,pip 默认会把库安装到系统里对应的 site-packages 里,这样无论你在哪打开 Python,都是可以用这个库的。这样听起来好像挺好,但实际上还是有一些弊端的。比如说,在一些项目中,我们最后需要整理出来一个这个项目使用了哪些第三方库,然后指引用户去安装这些库。一个两个还好,多了的话,恐怕开发者也容易找不全、漏掉一些库吧。再加上一些库的依赖、版本等问题,会让我们的开发环境和项目造成很多不必要的麻烦,管理也相当混乱。所以如果有必要,那么一定要使用 Virtual Environment/虚拟环境 的。

命令行方式

如果是 python3 就使用 pip3 ,否则使用 pip/pip2 .

pip install virtualenv

之后我们创建一个目录,使之成为工程目录:

mkdir mybot
cd mybot
#创建virtualenv
virtualenv venv
#激活virtualenv
source venv/bin/activate

此时命令行前面会变成 (venv),代表着已经进入了。这个时候再运行 pip 就会安装到虚拟环境中

pip install python-telegram-bot

IDE方式

如果你和我一样使用IDE,那么事情就变得很简单啦,pycharm 里新建 project ,选择 Virtualenv 即可,之后在 File - Settings - Project interpreter - Project Interpreter 右侧直接点击 + 搜索 python-telegram-bot 添加即可

开始编写第一个Bot

.py文件说明

在 pycharm 中新建 Python File

说明:
第一行的 # !/usr/bin/python 用于指定当给文件加 x 权限并执行时 (./main.py) 该使用哪个解释器,用法和 shell 脚本中 #! /bin/bash 是一样的

第二行用于指定编码格式,下面则是注释说明与 doc 。好奇这些是怎么来的吗?实际上是 IDE 自动生成的啦~

可以在 File - Settings - Editor - File and Code Templates自行修改,更多信息请参考 JetBrains File and code templates

编写tset.py

接下来我们编写一个Echo Bot,它会重复用户的话(其实就是复读机23333

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# author:spirit
# 首先需要引入需要的库
import telegram
import logging
import telegram
from telegram.error import NetworkError, Unauthorized
from time import sleep
# 初始化 update_id
update_id = None
# 定义主函数
def main():
    global update_id
    ## 创建Bot类的对象,下方填入自己的TOKEN
    bot = telegram.Bot('XXXXXXXX:XXXXXXXXXXXXX')

    # 获取第一个挂起的update_id,这样我们可以跳过它以防万一
    # 得到一个 "Unauthorized" 异常.
    try:
        update_id = bot.get_updates()[0].update_id
    except IndexError:
        update_id = None
    # 记录日志
    logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    # 主函数功能
    while True:
        try:
            echo(bot)
        except NetworkError:
            sleep(1)
        except Unauthorized:
            # 用户已屏蔽或取消了与机器人对话时
            update_id += 1
# 定义复读机(雾 
def echo(bot):
    global update_id
    # 在最后一个update_id之后请求更新
    for update in bot.get_updates(offset=update_id, timeout=10):
        update_id = update.update_id + 1

        if update.message:  # 如果Bot收到消息
            # 回复同样的消息给用户
            update.message.reply_text(update.message.text)
# 定义程序的入口
if __name__ == '__main__':
    main()

运行与调试

Webhook与Long Polling

目前 Telegram 支持两种取得更新的方法,分别为

  • 设定Webhook
    • 在有人发送消息给你的bot (或加入群组、点击按键等) 时, Telegram 将会使用 JSON 格式 POST 到你的服务器
    • 需使用 setWebhook 预先设定
    • 可用 getWebhookInfo 自行debug
    • 可参考官方 完整教学 (英文)
  • 每次主动询问
    • getUpdates 请求,将会回传一个 JSON 阵列
    • 也可用Long polling取得,详见 WiKi

Long Polling是指程序间隔一定时间通过过getUpdates 取得讯息,缺点是浪费资源,不够即时,所以适合在程序还没有发布,在开发和测试阶段时使用。

Webhook是指向Telegram设定一组callback url,只要当使用者发送消息给Bot,Telegram就会把消息连同metada透过url传给web server。适合在程序已经发布,有固定url的生产环境使用。

ps:我个人是极其建议使用Webhook的方式的,不过由于Webhook的配置使用略微复杂,且我们的 Echo Bot 比较简单,故没有使用Webhook的方式传送信息

开始运行

由于 Webhook 活动时不能使用 getUpdates 方式发送信息,所以我们需要删除 Webhook 集成,访问 https://api.telegram.org/bot'token'/deleteWebhook'token' 替换为申请的TOKEN。如果浏览器返回 True 就说明删除成功。

在 Terminal 执行 pip freeze > requirements.txt 导出所需依赖

test.pyrequirements.txt 传到服务器上,如果你对如何上传没有头绪,请安装 lrzsz

服务器执行 pip install -r requirements.txt 安装对应的依赖,直接执行我们的代码

python test.py

这时返回客户端,找到刚申请的Bot : t.me/Spiriibot ,进行调戏对话,如果不出意外的话,你会收获一台可爱的复读机(捂脸

当然如果你觉得不过瘾的话可以再进一步改进它,让它化身人工智障人工智能
tset.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# author:spirit
import sys

reload(sys)  # Python2初始化后会删除 sys.setdefaultencoding 这个方法,我们需要重新载入
sys.setdefaultencoding('utf-8')

import logging
import telegram
from telegram.error import NetworkError, Unauthorized
from time import sleep

update_id = None

def main():
    global update_id
    bot = telegram.Bot('XXXXXXXX:XXXXXXXXXXXXX')

    try:
        update_id = bot.get_updates()[0].update_id
    except IndexError:
        update_id = None

    logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

    while True:
        try:
            echo(bot)
        except NetworkError:
            sleep(1)
        except Unauthorized:
            update_id += 1

def echo(bot):
    global update_id
    for update in bot.get_updates(offset=update_id, timeout=10):
        update_id = update.update_id + 1

        if update.message:
            if update.message.text.find("你好"):
                update.message.reply_text(
                    update.message.text.replace("你", "我").replace("吗", "").replace("?", "!").replace("?", "!"))
            else:
                update.message.reply_text("你好!")

if __name__ == '__main__':
    main()

效果如图(好羞耻~):

后记

编写这个 Bot 的过程让我学到了很多东西,尤其是 python 的相关知识,由于初学,一些东西难免出错,欢迎在下方评论区指正
在之后的文章中我会继续完善 Bot ,主要为以下:

  • Bot 响应 '/' 菜单
  • Bot 添加正在输入标签
  • Bot 使用表情包
  • Bot 添加选单
  • Bot 结合数据库
  • Bot 部署在GAE
  • Bot 结合WordPress
  • ...

暂时只想到那么多,以后再慢慢增加吧 ^_^