swiftui-shapes
swiftui-shapes copied to clipboard
【RFC】Wechaty support middleware.
问题
Wechaty 已经开发过 plugin 机制,参考:
- https://github.com/wechaty/wechaty/issues/1939
但 plugin 的粒度还是相对较粗,导致不同的 plugin 之间还是存在许多重复代码。
以 vote-out 插件为例,假设我们有个需求,需要写个 vote-up
来记录每天群内谁被点贊最多,此时就会发现 vote-out
的所有过滤代码都得原封不动的拷贝至 vote-up
中,然后再完成 vote-up
的真实逻辑。
所以根本原因是 plugin 中的逻辑无法被更小粒度的抽象和复用,
思考
很早之前也粗略的思考过这里的问题,但改造成本较高,且与历史API存在兼容性问题,但最近又思考了一下,发现还是可以解决的。 可以扩展一种针对事件维度的 middleware 机制(洋葱模型)进行更小粒度的抽象。
示例代码如下
在现有模式下,如果要完整类似功能,需要这么写。
// room a 需要 vote-out 功能
bot.on('message', async (message) => {
const room = message.room()
if (!room) return
if (message.type() !== type.Message.Text) return
if (!matchers.roomMatcher('room a')) return
console.log('do vote-out action');
});
// room b 需要 vote-up 功能
bot.on('message', (msg) => {
const room = message.room()
if (!room) return
if (message.type() !== type.Message.Text) return
if (!matchers.roomMatcher('room a')) return
console.log('do vote-up action');
});
设计事件维度中间件能力后,可以这样写。
// 抽象通过能力为中间件,由中间件控制事件响应
const filterMiddleWare = (options) {
return async (message, next) => {
const room = message.room()
if (!room) return
if (message.type() !== option.type) return
if (!matchers.roomMatcher(option.room)) return
await next();
}
}
// 只有 room a 中会响应, vote-out 事件
bot.on('message',
filterMiddlerWare({ type: type.Message.Text, room: 'room a'}),
(message) => console.log('do vote-out action')
);
// 只有 room b 中会响应, vote-up 事件
bot.on('message',
[ filterMiddlerWare({ type: type.Message.Text, room: 'room a'}) ], // support middleware array.
(message) => console.log('do vote-up action')
);
对于现有插件,我们也可以扩展 use
方法在执行前去装载中间件
// VoteOut 插件只对 room a 生效
bot.use(VoteOut({ /* options */ }), { message: [ filterMiddleWare({ room: 'room a'}) ] })
这样,就能无缝的兼容原来的 API 和 插件能力啦 。
未来插件也可以做得更轻,只专注于功能本身,而不用去写一堆的 if (xxxx) return
啦。
而同时,插件本身也可以通过拼接一堆 middleware 来完成一个大的功能插件。
变更
on (event: WechatyEventName, listener: (...args: any[]) => any): this
on (event: WechatyEventName, middlewares: WechatyMiddleWare | WechatyMiddleWare[], listener: (...args: any[]) => any): this
use(...plugins, option: WechatyPluginMiddleWare);
Related issues
- https://github.com/websockets/ws/issues/1818