Telegraf入门使用不完全指北
最近因为某些原因需要做个Telegram的Bot帮我自动管理频道,于是寻思着用Node做一个bot,tg这么大的平台肯定是有Node专属的SDK的,于是在谷歌上找了一下,发现了两个star数量比较多的框架,第一个是 node-telegram-bot-api,第二个就是今天要介绍的 Telegraf。
可是我在谷歌翻了半天也没找到比较易懂的中文的开发教程,官方的文档都是英文的,ytb上的介绍视频大多数也是英文的,虽然也能搜到零零星星的中文介绍,但都是写的含糊不清。在对官方的英文文档研究了一番之后,于是我决定自己写一篇比较浅层的入门介绍。
Bot的工作模式
Telegram提供了两种方式使Bot能够接收到最新的消息,第一种是长轮询(Long Polling),第二种是Webhook,下面简单介绍一下这两种方式。
- 长轮询:这是Telegram官方比较推荐的一种方式,它通过客户端向Telegram的服务器建立一个长时间不会断开请求,服务器检测到更新后立即返回消息详情给客户端,客户端收到响应后断开链接然后重新建立一个新的长连接请求,如此往复。这种方式的好处就是效率高、回复即时,但需要占用连接资源。
- WebHook:所谓WebHook,就是当Telegram检测的消息后立即向你的客户端(或者是服务器)发出Http请求来告诉内容,但是缺点显而易见,起码你需要一个公网环境下能够被Telegram服务器访问的IP或域名。但是相比较WebHook来说这种方式占用的资源要少一些。
Telegraf框架介绍
之所以选用这个框架,是因为它其中使用了中间件的设计模式,如果你用过像Koa、Express这样的web框架,你就可以像写Web后端一样来写Bot的后端。官方文档:https://telegraf.js.org/
基础使用
首先你需要以Bot的Token为参数来新建一个Telegraf对象,这个对象就像Koa里的app一样。
1 | const { Telegraf } = require('telegraf') |
添加中间件
就像koa-rounter一样,你可以使用bot.use()给bot添加中间件。大致写法如下:
1 | bot.use(async (ctx, next) => ( |
其中的next与koa的next作用一样,如果不需要的话也可以不加。比较复杂的就是ctx对象了,它里面包括了当前消息的上下文,包括发送方的信息以及telegram对象等,下面来简单介绍一下用法。
Context对象
获取消息内容
首先可以通过ctx.message
拿到一个Message对象,不过这个Message其实是一个泛型接口,也就是说拿到的对象实际上都是继承于(或者说实现了)Messqge这个接口的。比如文字消息,就可以拿到一个TextMessage对象。然后我们就可以通过ctx.message.text
获取文字信息的字符串内容,整个过程从原理上来讲比较复杂,其实Message又继承了Update,也就是一种包括了所有从Telegram收到的更新内容(因为使用Bot不仅限于发消息,还包括使用键盘,Inline查询等,这里就不多赘述了)
于是我们可以这样来把用户发送的消息返回给用户:
1 | bot.use(async ctx => { |
另外,如果你不知道什么是Inline消息的话,我认为最快的方式是去体验一下@like这个Bot。
回复消息
上面使用了ctx.reply
来进行回复,它用来单纯的发送文本类型的消息。
如果要回复其他类型的消息,ctx中提供了一系列的replyWith…函数,这样就可以发送诸如图片、文件等其他消息。这些函数一般都可以接收多个参数,比如你可以使用replyWithPhoto一次性回复多张图片。
另外,reply以及相关的函数也返回一个Message对象,我们在获取这个对象之后可以先保存起来,之后需要编辑或删除消息的时候就可以从这个对象里获取消息的message_id和chat_id等信息。
获取发送方
部分情况下我们需要验证发送命令的一方是不是Bot的管理员,于是我们就可以对发送方的id进行验证:
1 | bot.use(async (ctx, next) => { |
其中ctx.from
中就包含了所有发送方的信息,包括ID,用户名等。如果是在群组中使用,还会包含群组信息。
构造中间件
往往直接使用bot.use()来添加中间件很麻烦,但是直接使用外部模块作为中间件时又没有提示信息可以参考,于是Telegraf专门提供了一个构造中间件的函数:Telegraf.fork
于是我们可以像这样来构造中间件:
1 | const { Telegraf } = require('telegraf') |
然后可以在中间件管理的程序里统一导入,然后再使用bot.use()来使用所构造的中间件。
专属中间件加载器
Telegraf为一部分的消息类型提供了专属的中间件加载器(中间件加载器这个词是我编的,因为不知道怎么翻译比较合适),比如命令类型的消息、Inline类型消息等。这里注意介绍一下命令类型的。
使用方法比较简单,直接使用bot.command('命令',中间件)
这样的格式就行。
比如接收add命令:
1 | bot.command('add',async (ctx,next) => { |
如果使用上面的fork构造器话,第二个参数也就可以写成中间件对象。
如果要获取命令的参数,我们在中间件里任然可以使用ctx.message.text
来获取消息文本,进而获取命令参数。
此外Telegraf还提供了其他的加载器来处理特定类型的消息,比如bot.hears()用来处理特定内容的文本消息,bot.textLink()只处理内容为一个链接的消息,bot.textMetion()只处理内容为Metion类型的消息(也就是@xxx)等等,这里不做过多介绍。
主动发送消息
Bot不仅可以在收到更新时执行相应的动作,也可以主动执行某个动作。
Telegraf在bot.telegram
中提供了一系列函数供我们调用,下面会挑几个简单介绍一下。
下面的几个函数均有返回值,同上面的reply()函数,不再赘述了。
发送文本消息 - sendMessage
此函数主要接收2个参数:chat_id,text。
要注意Telegram中的chat_id既可以是一串数字,也可以是以@开头的一个字符串,比如 @zh_CN
就可以是一个chat_id。但区别在于@形式的id随时都可以被用户修改,而数字id一旦创建则不可修改。
发送图片 - sendPhoto
此函数的第一个参数也是chat_id,但第二个参数既可以是字符串又可以是一个文件流对象。其中字符串参数又分为两种情况:file_id或file_url,也就是可以传一个在Telegram已经存在的文件的id作为要发送的文件,也可以直接传一个图片链接让Telegram去获取这个图片。
其中有一个小坑,如果传一个图片链接给Telegram的话,有可能会返回链接不是图片的错误。解决方法很简单,在链接的query里加一个随机参数即可,比如时间戳参数。
编辑消息 - editMessageText
这个函数主要接收4个参数,分别是chat_id,message_id,inline_message_id,text这四个参数。
其中message_id,inline_message_id只需要一个有值,另一个为undefined就行。
对于其他的函数比如editMessageMedia,使用方法类似,详情可参考官方文档。
下面是一个发送消息然后编辑的示例:
1 | async ctx => { |
需要注意的是message的chat_id是使用message.chat.id来获取的,而message_id是直接通过message_id字段来获取的。
Extra参数与Markup
其实上面提到的reply系列函数与send系列函数都有一个最后面的extra参数,它是可选参数,代表着这条消息的附属信息,比如文本信息等。
如果我们想在发送图片的同时携带一段文本,就可以使用extra中的caption字段:
1 | ctx.telegram.sendPhoto('chat_id', 'picture', { |
此外,extra参数中还有几个比较常用的字段,比如parse_mode字段可以指定内容的解析格式为Markdown或者HTML等,再比如如果这条发送的消息是要回复另一条消息的,可以在其中指定reply_to_message_id为要回复的消息ID即可。
除此之外,Telegram中还有一个有趣的功能:Keyboard。也就是说,你可以在消息的下方插入几个按钮来供用户点击,是不是很有意思?
要实现这个功能,就需要用到Telegraf提供的Markup。
Markup简单介绍
Telegram中的键盘(Keyboard)分为两种:Inline Keyboard和普通的Keyboard。其中Inline Keyboard是出现在消息下方的键盘,比较常用。而普通的KeyBoard则是出现在输入框下方的键盘,一般用于标签检索等场景。
对此,Markup类里分别提供了inlineKeyboard和keyboard供我们创建键盘对象。这里我们直接通过官方的几个的示例来看一下它是怎么用的:
1 | bot.command('onetime', (ctx) => |
这个示例中的extra参数就是由由Markup创建的普通键盘对象,键盘中的数组代表了三个按钮。这个键盘会展示在输入框的下面,oneTime代表这个键盘被点击一次会自动收起,resize代表这个键盘会根据高度自动调整尺寸。
另外,如果直接使用数组的方式来创建键盘,那么键盘上的按钮点击后会是用户发送该文本,一般会同时使用bot.hears()来判断用户点击了哪个按钮。
1 | bot.command('special', (ctx) => { |
这个则复杂一些,通过Markup.button来调用Telegram的其他功能,比如contactRequest则会请求用户发送一个联系人信息,locationRequest则会请求用户发送位置信息。参数中的字符串仍代表按钮文本。
1 | bot.command('inline', (ctx) => { |
这个就是创建Inline Keybord了,它会展示在消息的下面,button.callback函数接收2个参数:text和data,text仍然是按钮文本,data则是回调数据。用这个搭配bot.action()就可以实现点击选项然后执行回调的过程。下面是官方的示例:
1 | bot.command('caption', (ctx) => { |
这个例子中,用户点击了Italic按钮后,就会执行bot.action('italic', ...)
中的中间件,这里首先调用了answerCbQuery()向用户展示操作正在执行,然后调用调用editMessageCaption来编辑这个消息中的文本,并指定解析模式为Markdown,然后消息中的文本就被编辑为了斜体(下划线在Markdown中表示斜体)。
本文章参考来源
Telegraf官方文档:https://telegraf.js.org/classes/telegraf.html
Telegraf官方示例:https://github.com/telegraf/telegraf/tree/develop/docs/examples
Telegram Bot API文档:https://core.telegram.org/bots/api#sendphoto