-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
【RFC】Wechaty support middleware. #2282
Comments
两处代码块中 vote-up 中对应的 room 应为 |
Recently I'm reading the W3C EventTarget and Node.js EventTarget, which is the browser version of the What attracted me the most is, the As we know the But the I'm thinking about switching from @Gcaufy What do you think? |
We'd better not to stop events inside one event, because there is no way to reuse the event stopped by other events. For example
So, if we need to make it right, we need to move the 3rd event to the top, which means we need to be starkly aware of the event sequence. So in this case, I think |
I have been thinking about the Middleware proposal for the past few days and I must say this is a brilliant design. Thank you very much @Gcaufy! I think basically the whole middleware design is good, we definitely can move forward based on that. Below is some of what I thought about this design:
The above is what's in my head now, would love to hear any feedback, and let's push this RFC design move forward together. |
作为 .NET 平台上的开发者,我建议参考 ASP.NET CORE middleware 的设计 //添加全局 middleware
bot.messages
.use(middleware1)
.use(middleware2)
//添加指定筛选的 middleware
bot.messages
.where(filter)
.use(middleware3)
.use(middleware4) middleware 的效果类似于如下签名的函数 //其中 next 代表下一个管道,可以决定是否继续执行下一层
handler(context, next) :Promise 实际编写的时候可以如下面的效果: bot.messages
.use(async (context, next) -> {
console.log("global middleware 1")
await next(context)
})
.use(async (context, next) -> {
console.log("global middleware 2")
await next(context)
})
.where(message->message.room()=="a")
.use(async (context, next) -> {
console.log("middleware 3 with filter")
await next(context)
}).use(async (context, next) -> {
console.log("middleware 4 with filter")
await next(context)
}) 以上代码中的 |
这个是 middlewares 本来应该有的样子,完全赞同。 |
同意使用
意思是 middleware 同时返回 eventname 和 function 吗?最初我也这么想过,但考虑到限制死了 middleware 只能用过这个 event了, 所以还是放在外面声明了。比如说, 有个 middleware 在 message 和 friendship 上面都可以用。那该如何去复用呢?
同意,我也是这么想的。
Good idea. 写的时间没考虑到这种方式。后面我再看看这种实现。
这个我也想过,把所有事件属性都放在 Context 下,但可能会出现不同事件只 Context 内容大不相同,而且对 listener 也存在向下不兼容的问题。
同意。 代码里我也设计了 global 的 middlewares. |
另外一个想法: Plugin can be a type of middelware. 初衷你设计middleware的初衷就是:plugin 的粒度还是相对较粗,导致不同的 plugin 之间还是存在许多重复代码。 那我就想,我们只需要解决plugin的代码复用性的问题就行了,这个时候我想解决办法有很多种,其中的一种就是让插件之间存在一定的有向拓扑依赖关系,然后在wechaty启动的时候就编译成一个plugin tree,同一个tree level的plugins可以并行emit,不同level之间需要同步执行。 草图simple codefrom typing import List, Any
class Plugin1(WechatyPlugin):
def __init__(self):
self.plugin_global_variables: Any = {}
self.plugin_conversation_variables: Any = {}
def do_something(self):
pass
class Plugin2(WechatyPlugin):
def __init__(self):
self.plugin_global_variables: Any = {}
self.plugin_conversation_variables: Any = {}
def do_another_thing(self, data: Any):
pass
class Plugin3(WechatyPlugin):
pass
class Plugin4(WechatyPlugin):
def dependencies(self) -> List[WechatyPlugin]:
return [Plugin1, Plugin2]
def customized_func(self):
plugins = self.get_dependency_plugins()
plugin1: Plugin1 = plugins[0]
plugin2: Plugin2 = plugins[1]
result = plugin1.do_something()
# but plugin2 don't require plugin1
plugin2.do_another_thing(result) |
After some time, I think there should be some way to know them at some point... function use (fn: Plugin | Middleware) {
if (fn.length === 1) {
fn(this) // <- this is plugin, expecting one arg: wechaty
} else if (fn.length === 2) {
fn(context, () => {}) // <- this is middleware
} else {
console.error('what fn I have got?')
}
} Please correct me if I have missed anything. |
Should we support removing middleware/plugins after they have been installed?
Currently, once a plugin or middleware has been installed, there's no way to remove it. So does the Express middlewares. |
* restructure mixins * export use singlar instead of plural * export use singlar instead of plural * 1.3.5 * comment * 1.3.6 * update puppet * 1.3.7 * update puppet * update puppet * 1.3.8 * add unitt tests for mods * fix circle dependency problem * 1.3.9 * add valid method for WechatyBuilder * rename to Accepter * 1.3.10 * clean unit test for plugin (#2282 (comment)) * 1.3.11 * fix smoke testing * v1.5 * 1.5.1
It make sense, but
How about we use |
Are we sure to use I still prefer to use expanded arguments.
|
@huan Please check the comments so that I can continue :0) |
Dear @Gcaufy , Thank you very much for pushing this RFC forward, it's a great design, so please go ahead with the PR and make the CI green, then we start discussing based on the draft code, and run some example code based on your new code and test the user experiences for developing. This week I'm mainly working on the below PR, will back to you after I have finished it: Below are some of my options to our previous question: Typing change of
|
type RoomEventListenerInvite = (this: RoomInterface, inviter: ContactInterface, invitation: RoomInvitationInterface) => void | Promise<void> | |
type RoomEventListenerJoin = (this: RoomInterface, inviteeList: ContactInterface[], inviter: ContactInterface, date?: Date) => void | Promise<void> | |
type RoomEventListenerLeave = (this: RoomInterface, leaverList: ContactInterface[], remover?: ContactInterface, date?: Date) => void | Promise<void> | |
type RoomEventListenerMessage = (this: RoomInterface, message: MessageInterface, date?: Date) => void | Promise<void> | |
type RoomEventListenerTopic = (this: RoomInterface, topic: string, oldTopic: string, changer: ContactInterface, date?: Date) => void | Promise<void> |
How do we define a middleware for it?
One middleware interface for all event types?
In the design, I saw that each middleware needs to bind to one Wechaty event, and I understand that. For example, a middleware is dealing with messages, and another is dealing with friendship. Can we encapsulate this event name inside the middleware, instead of specifying the name outside? For example, it would be more simple if we can rewrite the below code:
意思是 middleware 同时返回 eventname 和 function 吗?最初我也这么想过,但考虑到限制死了 middleware 只能用过这个 event了, 所以还是放在外面声明了。比如说, 有个 middleware 在 message 和 friendship 上面都可以用。那该如何去复用呢?
Could you please provide an example middle that "在 message 和 friendship 上面都可以用"?
After I think it more, maybe we can also consider that one middleware only deals with one event type?
The context
Context is used for requests because every request would have Req and Resp etc.
I'm always thinking about that, the interaction between the chatbot and the user is very similar to the Web server and the user., because as you said: "every request would have Req and Resp", the conversation between the chatbot and user have Turn (a concept from BotFramework) for a question/answer (request/response), and the bot needs to store information between turns, which needs a context
to store those pieces of information, and BotFramework has a TurnContext for that.
However, as our listener functions will have a this: Wechaty
, I believe we can use the wechaty.memory
to store that information. If we use wechaty.memory
as the context of the turn, then we do not need to do anything additionally.
wechaty.memory
is a MemoryCard instance with a ES6 Map like API in Async.
@Gcaufy Hello, are you still following this issue? This problem is also bothering me. |
Hi, @Gcaufy! I'm Dosu, and I'm here to help the wechaty team manage their backlog. I wanted to let you know that we are marking this issue as stale. From what I understand, you proposed adding middleware support to Wechaty to reduce code duplication and improve code reusability between plugins. There have been discussions about the design and implementation details, including the use of Before we close this issue, we wanted to check with you if it is still relevant to the latest version of the wechaty repository. If it is, please let us know by commenting on the issue. Otherwise, feel free to close the issue yourself or it will be automatically closed in 7 days. Thank you for your contribution and understanding. Let us know if you have any further questions or concerns. |
问题
Wechaty 已经开发过 plugin 机制,参考:
但 plugin 的粒度还是相对较粗,导致不同的 plugin 之间还是存在许多重复代码。
以 vote-out 插件为例,假设我们有个需求,需要写个
vote-up
来记录每天群内谁被点贊最多,此时就会发现vote-out
的所有过滤代码都得原封不动的拷贝至vote-up
中,然后再完成vote-up
的真实逻辑。所以根本原因是 plugin 中的逻辑无法被更小粒度的抽象和复用,
思考
很早之前也粗略的思考过这里的问题,但改造成本较高,且与历史API存在兼容性问题,但最近又思考了一下,发现还是可以解决的。
可以扩展一种针对事件维度的 middleware 机制(洋葱模型)进行更小粒度的抽象。
示例代码如下
在现有模式下,如果要完整类似功能,需要这么写。
设计事件维度中间件能力后,可以这样写。
对于现有插件,我们也可以扩展
use
方法在执行前去装载中间件这样,就能无缝的兼容原来的 API 和 插件能力啦 。
未来插件也可以做得更轻,只专注于功能本身,而不用去写一堆的
if (xxxx) return
啦。而同时,插件本身也可以通过拼接一堆 middleware 来完成一个大的功能插件。
变更
Related issues
EventTarget
object websockets/ws#1818The text was updated successfully, but these errors were encountered: