前几天给 RSSHub 提交了一个 PR,把我所就读的大学的两个学院官网做成了 RSS 源。
RSS 好是好,但就是缺少即时性,于是打算利用企业微信机器人做一个及时推送的服务, 并把服务范围扩大。
关于配置模式的思考
我想到了几种配置方案:
- 用户为单位,每个用户下面配置他订阅的路由,以及他的推送信息。
- 路由为单位,每个路由下面记录订阅该路由的用户。
由于暂时只有我一个人使用,便使用了第二种方案,并把本该配置在用户字段下的”订阅频道“提到了全局。
架构设计
由于不确定各个模块的具体逻辑,这方面我也做了很多技术权衡:
A.每个模块负责返回最新推文
这种模式源于 RSSHub 的实现模式。控制器依次注册每个模块,传递一个上下文方法。
模块内部有一个定时器,每隔一段时间透过上下文更新推文。
推文数据保存在控制器,一旦数据不一致则执行推送流程。
如此,推送逻辑就由控制器处理。好处是,如果以后想抓取其他网站的文章,可以很方便地扩展。但是,后续的开发流程中数据可能污染污染控制器(因为每个网站的推文结构不一样,比如 B 站就会有 video 字段。)
B.每个模块执行全部自己的逻辑
这个方法简单粗暴,模块只需暴露一个start()方法,控制器直接调用即可。
如果后续要扩展,比如用户选择推送逻辑,也可以提取出公共组件。
并且,以后可能会有一些不需要推送的服务,例如网课签到。
这也是最终采用的代码。
C.每个模块提供一个更新、推送方法
看似简洁,事实上这是一个很糟糕的设计,数据到了上游,但是数据消费却到了下游。
理解发布-订阅模式
事实上,这并非是一个标准的发布-订阅模式。
我们先来看看入口函数如何调用个人助理:
这确实是标准的订阅模式。
如果 push 中写进一个具体的推送方法,并且每个模块仅仅返回最新的推文,那么发布者(控制器)则会存储一系列推送函数,一旦模块返回了最新的推文,就依次执行发布流程。
但本项目中,推送流程其实由控制器自己调度了(事实上,每个模块都是控制器的组成部分,此处推送逻辑在模块里面)。所以仅仅实现了订阅模式。
后续开发
基于发布订阅模式,我保留了许多方法,例如list() destory(),后续可以搭配 Web 应用进行远程管理。
国内开放 API 的 IM 平台还比较少,后续将接入国外的一些平台。值得一提的是,配置结构可能就要采取”订阅源“和”用户“分开存储的逻辑。
项目地址: github.com/rivertwilight/assistant