Puppet in Wechaty is an Abstract Class for implementing protocol plugins. The plugins are the component that helps Wechaty to control the IMs like WeChat(that's the reason we call it puppet!).
The plugins are named
PuppetXXX, like PuppetPuppeteer is using the google puppeteer to control the WeChat Web API via a chrome browser, PuppetPadLocal is using the Pad Protocol to connect with WeChat Server.
- Puppet Directory: https://github.com/wechaty/wechaty-puppet/wiki/Directory
- Puppet Compatibility Table: https://github.com/wechaty/wechaty-puppet/wiki/Compatibility
- Puppet Development Guide: https://github.com/wechaty/wechaty-puppet/wiki/Development
- Puppet Related Links: https://github.com/wechaty/wechaty-puppet/wiki/Links
- Puppet Documentation: https://wechaty.github.io/wechaty-puppet/typedoc/classes/puppet.html
Puppet in Wechaty is a name that we had picked up to describe part of our system: Puppet is an Abstract Class for implementing plugins, the plugins are the component that helps Wechaty to control the Wechat, that's the reason we call it
Plugins are named PuppetXXX, like PuppetPuppeteer is using the chrome puppeteer to control the WeChat Web API via a chrome browser, PuppetService is using the gRPC protocol to connect with a Protocol Server for controlling an iPad/Windows/whatever program.
For a deeper understanding of the Puppet in Wechaty, you can read its documentation from https://wechaty.github.io/wechaty-puppet/typedoc/classes/puppet.html and source code if you like at https://github.com/wechaty/wechaty-puppet/blob/master/src/puppet.ts
A web solution to connect WeChat, Wechaty init implement is by web WeChat, which inject js code into chrome.
A mock function to connect WeChat, not a real implement, for testing other connectors to connect with Wechaty, in other words: a mock solution to implement puppet. This is used for further to connect other solutions, such as iPad, Xposed, iOs, windows client...
An iPad solution to connect WeChat
- WECHATY_PUPPET=wechaty-puppet-mock npm start
- WECHATY_PUPPET=wechaty-puppet-padpro npm start
Here are some rules that a Wechaty Puppet should follow:
- Emit Self Messages: when the bot says anything, the bot should receive a message said by itself. (and the
message.self()will return true for this message)
- Perfect Logout:
logoutmethod should clean all the user session data from the puppet service, and the status of the App on the phone should display correctly (not log in on any devices).
- State-less Session Management (with MemoryCard support): the puppet service should save the user session data to the memory card, and can be restored from the memory card. (See: #16)
- MIME File Name Extension Convention: FileBoxChunk.name must be able to convert to a MIME type and versa visa. The puppet needs to set the name with the right extension (.jpg, .pdf, etc) to the name of the file box. See: https://github.com/wechaty/wechaty-puppet-hostie/discussions/115,
- To be added.
When a Wechaty bots logged in, it will have a authorized secret data to store their logged-in session, for example, the device information (62 data for pad protocol), the cookie (if you are using the web protocol), and the user session secrets, etc.
The memory card is a module designed to store those data.
- wechaty start()
- wechaty instanciates memory card (see wechaty.ts:start())
- wechaty set memory card to puppet (see wechaty.ts:initPuppet())
- puppet start()
- puppet load session from memory card
- puppet logged in
- by loaded session, or
- by scan qr code
- puppet save the session secret data to memory card
- memory card will be saved to file or network storage for future reuse
By saving the user login session secret data to memory card, the Wechaty system can save the state to local, so that it can make the puppet service provider service stateless.
Current neither of the Donut, WXWork, Rock, PadLocal have not support this stateless feature, nor the Wechaty system are ready for it.
To be implemented for our ecosystem.
I think I can contribute some analytics to this problem:
- As the log & puppet testing shows: the
readyevent is after the
- However, the Wechaty system needs to load the contact payload of the
userSelfbefore it emits the
loginevent because the login event of Wechaty needs to take a
userSelfinstance, and it needs to be
So there will be some delay before the Wechaty emit the
login event after it received the
login event from its puppet.
Please pay attention to this behavior and let me know if this issue was caused by it.
For a more robust Wechaty system, we can consider saving the
ready event if the
login event is not emitted in Wechaty.
And when we emit the
login event in Wechaty, we can check if the puppet has already
ready-ed, and if so, it can emit the
ready event right after the
Need to fire
ready event after the bot logined and all data has been synced.
For example, after we re-installed the WeChat app on our phone, it has to load contacts/rooms from the server for a long time.
wechaty-puppetmust not a dependency. It should be put in devDependencies and peerDependencies
wechatymust not a dependency. It should be put in devDependencies and peerDependencies
- must exist
examples/ding-dong-bot.tsto implement the ding/dong logic, use puppet api only.
Puppet must emit heartbeats for provide a health check signal.
Here are the details:
- Wechaty Puppet is designed to emit at least one event in 60 seconds. If we do not have any events to emit, then we need to emit a
heartbeatevent so that it can prove itself is alive and healthy. See: https://github.com/wechaty/wechaty-puppet/blob/bab9e29c088c33fa8bc6e148d9bdadbd453fd138/src/puppet.ts#L253-L254
- It seems that the PadLocal does not have any
heartbeatevent to emit when there are no other events, so if your bot idle for more than 60 seconds, then the Wechaty Puppet system will think the puppet provider has dead, so it will call
resetto try to recover the puppet.
A leaking of
heartbeat example logs:
Here's an example from our puppeteer puppet, which emits heartbeats in the browser, so if the browser dead, we will get to know because the heartbeat will be lost.
video messagge is not supported now, and pr is welcome. I believe this feature is simple for you. You can have a try so you can become a wechaty contributor 👍
Please refer to :
We now support very limit message types:
In order to support receiving more message types, like audio, you need to look at:
Congratulations! It seems that you can receive the right webhook payload from WeChat Official Server when you sent an audio message to your Official Account!
However, it seems that the Wechaty Message Payload has not been set correctly. In order to make it correct, you need to understand the message processing flow in Wechaty Puppet.
The Webhook get called by the Tencent Server (what you have done already)
The message event will be propagated from the
Webhookclass to the
The message event will be propagated from the
OfficialAccountclass to the
After message event be propagated from the PuppetOA to Wechaty, then the puppet.messagePayload() will be called to get the Wechaty Message Payload. Here is the most important part: we need to construct a Wechaty Message Payload from the Raw Message Payload (source code at here):const rawPayload = await this.messageRawPayload(messageId)const payload = await this.messageRawPayloadParser(rawPayload)
So it will be very clear that, what we need to do to support the new Message Type (audio in this case), is to implement the
messageRawPayload methods, which you can find here:
I hope the above explanation could help you to move forward, please feel free to let me know if you have more questions.
Puppet has a API named
ding(data: string): void, and the Puppet must:
- emit a
dongevent when the
ding()method has been called
- the payload of the
dongevent might contains a
datakey with the value exactly match the
datawhen calling the
This is for active(passive) health checking, and this is also a workaround for some edge case communication between the top puppet with the bottom puppet.