记我自己的Telegram Bot开发到第一阶段

2020-03-03

介绍

Telegram是一款及时消息(Instant Messaging)软件,特点是支持自定义客户端API「机器人」API,「机器人」即Telegram Bot,简称Bot,它类似于一个用户,可以发送和接受消息,创建不需要手机号(用户可向@BotFather申请创建一个机器人,可得到一个用于Bot API的Token),它允许用户设置成将所接收到的消息都保存在Telegram中心服务器上,用户可通过HTTP请求获取Telegram中心服务器上保存着的Bot收到的消息,或者是设置Bot将收到的消息立即转发给指定的服务器(Webhook方式),具体是当有用户向该Bot发送消息,消息到达了Telegram中心服务器,Telegram中心服务器就会将消息封装成Bot API专用的对象,称为Update,通过POST方法将Update发送到用户指定的URL,然后用户可在自己的服务器端,实现处理来自Telegram中心服务器POST过来的Update,即用户发送给Bot的消息,的逻辑.

botintro

目的

参考本站的中期发展规划,我实际上是想:

  1. 在服务器上做一个字面意义上的『消息中心』,届时该网站和Telegram Bot都可看成是一个『终端』,数据统一放在『消息中心』存储和处理,『消息中心』就像是系统的『大脑』一样的存在.
  2. 打通网站和服务器之间的双向通信,或者更加广义的说是『消息中心』和『终端』之间的双向通信,『终端』获得信息立马上报给『消息中心』,『消息中心』根据紧急程度,处理完成后立刻返回,或者先将消息存储在数据库中交给人工处理.

所以说实现这个Telegram Bot实际上算是打通『消息中心』到『终端』之间的双向通信的一个子任务.可能有人要问了,现在手机上的iOS啊,Android啊,电脑上的Windows啊,都有完善的图像界面当然也包括消息中心和消息通知功能,为何我还要去自己实现一个呢?

我们知道,我们现在能够获取的信息虽然变多了,但是也面临着『信息轰炸』,从海量信息中获取有效信息的成本也增加了,比如说我们要从短信/邮件列表中翻找一会儿才找到自己真正要找的短信/邮件,而且有那么多的网站社区我们想关注,但是去浏览时发现并没有多少有价值的东西,所以我个人而言还是想有这么一个消息中心,它连通信息孤岛,集中整合处理数据,从数据中获取知识.再通过简单的,结构化,以及可计算的方式供人们访问.

当前的阶段

经过一天左右的开发,当前的进度:

  1. 将Webhook绑定到我自己租用的云服务器
  2. 在服务器上配置好了专用域名,TLS证书,Nginx反向代理
  3. 初步确定好了是该选用Java还是Node.js技术栈:选择Node.js技术栈来进行该项目的初期工程
  4. 能够接收用户发送给自己的信息,通过Bot API提供的Webhook机制自动转发到服务器上,服务器再同样地调用Bot API给原发送者相同内容的回复.

即当前已经实现了一个Echo Bot,打通了Telegram客户端到私有服务器事件处理单元的信道.

设想下一步

最开始想的是在网站上做一个消息窗口,访客可以在消息窗口留言,在消息窗口的留言不会像评论那样公开可见而是直达站长,这样的话,更加及时可靠,也更有隐私,同时站长的回复应该也可以通过特定会话的聊天窗口回复给原发送者.

那么接下来要实现的应该是:

  1. 浏览器打开页面时,JavaScript脚本主动向服务器请求WebSocket连接,连接建立后保持通信.
  2. 或许是通过第三方API的方式实现聊天窗口的界面和逻辑,总之要使浏览器和服务器都共同维护一个会话标识符,使得消息和会话标识符能够发送到服务器就行.
  3. 网站开放一套独立于内容提供系统之外的API,这里『独立』是指维持这套API的系统的正常运行与否,都不能够影响这个网站正常地向公众提供内容,即哪怕这套API的支持系统不工作了,网站还应可正常访问.

那么API要实现的应该是:

  1. 支持页面主题的切换,包括全站作用域的(在服务器修改样式),会话作用域的(只针对当前用户的当前会话),支持网站内容服务器的启停和向内容服务器发送信号(这就是为什么说API要和内容服务器独立).

  2. 网站统计数据的获取,包括历史统计数据和实时汇总数据.以及由自动化性能测试程序产生的性能测试结果数据.

  3. 事件的通知,例如内容发生了变更,统计数据发生了变更,服务器运行状态发生了重大变更,以及其他需要通过事件API来通知的事件的通知.现认为事件通知可分为两类,主动事件通知和被动事件通知:

    • 主动事件通知:由事件源,在本网站,或者内容服务器,当侦测到事件时,检查事件委托列表,所谓事件委托列表的每一项是包括事件名称和事件通知URL,找出事件委托列表中符合条件的项,逐一发送至对应的URL.例如,Bot API提供的Webhook机制可看做是主动事件通知:当服务器收到Updates时,主动将Updates推送至用户设定好的URL.
    • 被动事件通知:当事件发生时,如果事件满足:1)可序列化,以及2)可持久化,那么就将数据持久化,当有事件请求时,再检索以持久化而请求者尚未知晓的事件,发送给事件请求者.
  4. 其他被认为可由本网站的API完成而非其他组件完成的功能.

所以说事实上『消息中心』应该是和『终端』分离的,通过接口和适配器访问『终端』.接口和适配器的实现,就是API,但他们不应该是『消息中心』的组成.

开发历程和感想

技术栈的选择

一开始,我只是简单地看了下Bot API官网对Telegram Bot及其工作方式的简单介绍,看见是由Telegram服务器向指定URL发送HTTP POST请求,我就打算用SpringBoot来实现在我自己服务器上接受Telegram服务的POST并且自己解析Update Object,一是因为我比较喜欢Java(现在还正在学),二是也想学习SpringBoot(打算现学现用),但是后来我发现Update Object解析起来实在是比较复杂(虽说不难),主要是一个Update Object包含一个或数个Message Object作为成员,而一个Message Object又有数十个字段,这时我开始转向已经实现好了的Bot API,这个页面真的列出了很多种语言的Bot API库,我挑了一个Java的,但是由于维护人数太少,文档也太少,而且我的Webhook是指向一个子域名,这个子域名由Nginx监听,说白了我想把这个响应Webhook的程序置于Nginx的后面,由Nginx将收到的来自公网的Webhook POST转给Webhook响应程序,但是Java的那个库确实没说该怎么做,于是我就转向使用基于Node.js的Telegraf,据说是Full Telegram Bot API support,也希望真的如此,这个项目的维护人数、使用人数和关注人数都挺多的,而且还有自己的文档官网用来描述具体如何使用这个库,于是我就转向了Telegraf.

Telegraf

Telegraf呢是基于Node.js的,一开始我不是太喜欢Node.js或者JavaScript,原因是学习开发过程中我逐渐意识到『类型系统』的重要性,觉得就像数学一样,我觉得,计算机程序,作为对现实世界中客体的抽象,它离不开清晰、明确而良好的形式化的定义,自从开始学习Java和Haskell之后我就更加迷恋『类型系统』和Type theory,所以呢传统的面向对象的JavaScript由于没有完备的类型声明和类型检查功能,我现在不是太喜欢,虽然说这学期课程设计的时候JavaScript救过我,让我用一个通宵加一个上午把项目的后端部分快速的完成,使项目基本可运行.

TypeScript

不过呢Telegraf是支持TypeScript的,这是最重要的一点,意味着用Telegraf库创建的对象可以声明类型,开发环境(我用VS Code)也会自动提示,VS Code还会提示一个函数的类型签名,更有利于对Telegraf的学习和理解,并且在编译的过程中编译工具也会自动检查类型的正确性,我虽然不喜欢JavaScript,但是TypeScript是喜欢的,优点真的是太多了,它没有Java技术圈里那种过度工程化(Over-engineering)的氛围(虽然未必不好),但是TypeScript相对Java真的简洁了不止一点,并且呢,我发现用VS Code写TypeScript真的很舒服,VS Code对TypeScript的Intellisense的支持做的真的非常好,自动提示和补全,自动错误修正,自动import依赖项,Go to definition功能都很方便.

踩过的一些坑

这两天呢因为这个新的网站我确实是比较亢奋,花了可以说好长时间来进行相关的开发、部署和调整,包括这两天我一直在做这方面的工作,连网课的进度都暂时放到一边去了,睡觉的时间也很少,导致我的记忆力也有些下降,我记不得这个网站是昨天早上建的还是前天早上建的,好像是昨天早上建的,3月2号我记得,这种感觉就像是在网吧通宵打游戏一样,整个人都非常的颓废虚弱.

然后因为睡眠不足,过度亢奋和大量饮用可乐,心态变得比平时浮躁,亢奋的时候特别亢奋,一两个小时之后又感觉特别的累(这种极度的疲惫我在去年参加全国数学建模大赛时体会过),这就导致我看文档都是跳着,我总是觉得文档的字儿太多,殊不知只要稍微地认真一点就可以很快的Get到点,导致我看文档的效率降低,这是一方面.

另外开发起来,比起平时确实容易累,在写服务器配置文件的时候,在写TypeScript的时候都犯了很多小错误,比如说记不清名词的单数和复数形式,忘记在末尾加个s,比如说将网页服务器设定的端口号记错,导致处理Webhook的Node.js程序一直在监听错误的端口,而服务器的访问日志却一直提示502错误,环境变量没有设置BOT_TOKEN却不知道,等等诸如此类.

收获和总结

经过这短时间的高密度高强度的开发,我得到了一个Echo Bot,基本上实现了对Webhook Updates的响应,以后可在此基础上添加业务逻辑,认识到了充足的睡眠是必不可少的,为了开发的效率,为了身体健康,都是如此,然后我还初步地学习了TypeScript,认识了Gulp自动化构建工具,以及有限的成就感.

另外技术栈的选择真的不应该太早地确定下来,你得考虑更多的因素,选用的库应该有完善的文档,活跃的社区和及时的维护,并且你还要学会估计:当选用这套技术栈进行开发时,所需的总工作量是多少,自己每天有效的工作速度如何,比如说我一天能写100行有效代码,而如果我用SpringBoot来做这个Webhook处理程序的话,我可能要写500到600行代码,才能实现一些基本的功能,期间可能还会不小心陷入过度工程化的泥沼:就是功能还没实现多少,自己却开始写起了框架,所以我就不能用SpringBoot来实现这个Bot,而要是我用我说的这个Java的Bot API实现来实现这个Bot的话,那恐怕,不一定不行,也不是不可以,我要想监听来自前置服务器转发而来的Webhook POST的话,可能也会比较麻烦,但是做出选择现成的Bot API实现而不是自己解析Updates Object的这个决定已经是向前迈出了一大步.

最后吧,我觉得开发作为一种创造的过程,体会到其中的乐趣也是有必要的,正确的学习态度,才能是自己正确看待开发这件事情,有创意就应该尽早地去实践、去验证,才能知道自己的能力如何,也不应该过早的对自己没有接触过的事物做出判断,因为你没有体会过,或许就真的不会发现它和自己所想的有何不同.

开发记录telegram

聊一聊我最近都关注哪些技术点

奇怪的favicon问题