python-telegram-bot 是一个著名的Telegram bot第三方API库。考虑到这方面的中文资料还比较少,我就花了点时间把Extensions - Your first Bot这篇文章翻译了一下,顺便留着自己以后用。本文原链接:https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-Your-first-Bot

叠盾:我没有受过系统的计算机教育,英语水平近似于工地英语。欢迎你在评论区指出翻译错误,为此我将不胜感激。

官方的wiki上一共有四十多篇文章,我以后可能会挑几篇翻译出来。


扩展 —— 你的第一个机器人

⚠️这是v20.x版本的维基。对于v13.x版本,请看这里

介绍

Telegram.ext 子模块建立在纯API实现之上。它提供了简单易用的接口(interface),减少了程序员的任务,使你不必重复工作
它包含了一些类,但是最重要的是 telegram.ext.Application

Application 类负责从 update_queue 取得更新(update),这样, Updater 类不断从Telegram取得更新并加到队列里。如果你创建一个 Application 对象,使用 ApplicationBuilder ,这会自动创建一个 Updater 并且将它们与一个 asyncio.Queue 关联。然后,你可以在 Application 中注册(register)不同种类的处理程序(handler),这将会根据你注册的处理程序把从 Updater 取得的更新排序,并且将它们投送到你定义的回调函数。

每一个处理程序都是任何 telegram.ext.BaseHandler 类的任何子类的实例。这个库(library)提供了几乎所有使用情形的处理程序类,但是如果你需要特定的,你也可以自己创建 Handler 子类。

首先,你需要一个Access Token。如果你已经读了API介绍(Introduction to the API)并且照着做了,你可以使用你自己生成的。如果没有,为了生成一个Access Token,你需要与@BotFather对话并且照着做几个简单的步骤(介绍在这里)。虽然你真的应该先看介绍的。

你的第一个机器人,一步一步来

如果你想要找着这个教程做,请创建一个新文件。在教程中,我们会分几次在这个文件中添加新内容。为了简洁,当我们添加新东西的时候我们不会每时每刻重复每一件事。

好,让我们开始吧! 将下面的内容粘贴到你的文件中:

import logging
from telegram import Update
from telegram.ext import ApplicationBuilder, ContextTypes, CommandHandler

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

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await context.bot.send_message(chat_id=update.effective_chat.id, text="I'm a bot, please talk to me!")

if __name__ == '__main__':
    application = ApplicationBuilder().token('TOKEN').build()
    
    start_handler = CommandHandler('start', start)
    application.add_handler(start_handler)
    
    application.run_polling()
    

现在有许多要消化的内容,那么让我们一步一步弄明白它吧。

import logging
logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    level=logging.INFO
)

这部分用于设置 logging 模块,以便让你知道什么时候(以及为什么)出了岔子:

注意: 阅读文章异常,警告与记录(Exceptions, Warnings and Logging)以了解更多。

application = ApplicationBuilder().token('TOKEN').build()

在这里,第一个真实魔法发生了:你必须创建一个 Application 对象。用你的Bot API token来替换 TOKEN 。想知道关于这个的工作原理的更多信息,看这页

相关文档telegram.ext.ApplicationBuilder , telegram.ext.Application

单独的application什么也不做。为了添加功能,我们做两件事。首先,我们定义一个函数来处理特定类型的更新:

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await context.bot.send_message(
        chat_id=update.effective_chat.id,
        text="I'm a bot, please talk to me!"
    )

这个的目的是,使机器人收到一条包含 /start 命令的Telegram消息后,调用这个函数。

如你所见,这个函数接收两个参数: update ,一个包含来自telegram信息和数据的对象(例如消息、发送命令的用户等); context ,另一个包含关于库本身的信息和数据的状态(像BotApplicationjob_queue等等)。

相关文档send_message , telegram.ext.CallbackContext (context参数的种类) , telegram.Update (update参数的种类)

为了让你的机器人监听 /start 命令,你可以使用 CommandHandler (一个提供的 Handler 子类)并且在应用程序中注册:

from telegram.ext import CommandHandler
start_handler = CommandHandler('start', start)
application.add_handler(start_handler)

相关文档telegram.ext.CommandHandler , telegram.ext.Application.add_handler

这就是你所需要的全部。

最后,application.run_polling()这一行使机器人运行起来,直到你按下CTRL+C

相关文档telegram.ext.Application.run_polling

试试吧!和你的机器人开始对话,发送一个 /start 命令,如果一切正确的话,它将回复。

但是现在我们的机器人只回应 /start 命令。让我们添加另一个处理程序来监听常规消息。使用 MessageHandler ,另一个 Handler 子类,来复读所有文本消息。首先,按下 CTRL+C 来停下你的机器人,现在定义一个新函数并添加对应的处理程序:

from telegram import Update
from telegram.ext import filters, MessageHandler, ApplicationBuilder, CommandHandler, ContextTypes

...

async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await context.bot.send_message(chat_id=update.effective_chat.id, text=update.message.text)

    
if __name__ == '__main__':
    ...
    echo_handler = MessageHandler(filters.TEXT & (~filters.COMMAND), echo)
    
    application.add_handler(start_handler)
    application.add_handler(echo_handler)

    application.run_polling()

相关文档telegram.ext.MessageHandler , telegram.ext.filters

从现在开始,你的机器人应该复读所有它收到的非命令消息了。

注意filters 模块包含许多所谓的过滤器(filter),用于过滤传入的文本、图片、状态更新(status update)等的消息。任何对至少一个过滤器返回 True 的、投送到 MessageHandler 的消息都会被接受。如果你想的话,也可以写自己的过滤器,参见Advanced Filters(高级过滤器)

让我们给你的机器人加一些实际的功能吧。我们想要实现一个 /caps 命令,它把文本作为参数并把它们以大写形式回复。为了让事情简单些,你可以在回调函数里接收被传入命令的参数(作为一个 list(列表) ,用空格分割):

async def caps(update: Update, context: ContextTypes.DEFAULT_TYPE):
    text_caps = ' '.join(context.args).upper()
    await context.bot.send_message(chat_id=update.effective_chat.id, text=text_caps)
    
if __name__ == '__main__':
    ...
    caps_handler = CommandHandler('caps', caps)
    
    application.add_handler(start_handler)
    application.add_handler(echo_handler)
    application.add_handler(caps_handler)

    application.run_polling()

注意:看一下 context.args 的用法。 CallbackContext 有一些属性,这取决于使用的处理程序。

另一个Telegram Bot API的很酷的功能是内联模式(inline mode)。如果你想为你的机器人实现内联功能,首先和@BotFather对话,并使用 /setinline 启用内联模式。有时候,你的机器人在你的客户端上注册为内联机器人之前,需要一点时间。通过重启你的Telegram App,你或许可以加速进程(或许有时,你只是需要等一会)。

显而易见,你的机器人已经是一个很大声的机器人了,让我们在内联模式上继续这个主题吧。到现在,你可能知道这个过程了,但是这里使用了很多新种类,所以注意:

from telegram import InlineQueryResultArticle, InputTextMessageContent
from telegram.ext import InlineQueryHandler

...

async def inline_caps(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.inline_query.query
    if not query:
        return
    results = []
    results.append(
        InlineQueryResultArticle(
            id=query.upper(),
            title='Caps',
            input_message_content=InputTextMessageContent(query.upper())
        )
    )
    await context.bot.answer_inline_query(update.inline_query.id, results)

if __name__ == '__main__':
    ...
    inline_caps_handler = InlineQueryHandler(inline_caps)
    application.add_handler(inline_caps_handler)

    application.run_polling()

相关文档telegram.ext.InlineQueryHandler , answer_inline_query

不赖嘛!你的机器人现在可以根据命令通过内联模式大喊大叫了。(哈!)

一些感到困惑的用户可能试着给机器人发送一些它不理解的命令,那么你可以使用一个 MessageHandler 和一个 COMMAND 过滤器来回复在之前所有的处理程序中没有都被识别的命令。

...

async def unknown(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await context.bot.send_message(chat_id=update.effective_chat.id, text="Sorry, I didn't understand that command.")

if __name__ == '__main__':
    ...
    
    # Other handlers
    unknown_handler = MessageHandler(filters.COMMAND, unknown)
    application.add_handler(unknown_handler)

    application.run_polling()

注意:这个处理程序 必须 被放在最后。如果你把它放在其他的处理程序之前,它将会在 CommandHandlers 有机会查看更新前被触发。一旦一个更新被处理,其他的处理程序都将被忽略。为了避免这样,你可以把关键字参数 group (int) 以一个非0的值传递到 add_handler 。参看 telegram.ext.Application.add_handler 以及这个维基页获取更多细节。

如果你玩够了,按下 CTRL+C 使机器人停止运行吧。

接下来应该读什么呢?

看看这些准备好运行的例子吧。

异常,警告与记录中学习库异常和最好的实践。

想要更多的功能吗?看看扩展————工作队列(Extension – JobQuene)吧!