@invago/mixin 1.0.10 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,40 +2,42 @@
2
2
 
3
3
  Connect [Mixin Messenger](https://mixin.one/messenger) to [OpenClaw](https://openclaw.ai).
4
4
 
5
- **[Chinese Documentation](README.zh-CN.md)**
6
-
7
- ## Overview
8
-
9
- MixinClaw is an OpenClaw channel plugin. It runs in the same process as the OpenClaw Gateway, receives inbound messages from Mixin Blaze WebSocket, and delivers outbound messages over the Mixin HTTP API.
10
-
11
- Important:
12
-
13
- - Install the plugin on the same machine where the OpenClaw Gateway runs.
14
- - OpenClaw config files use JSON5, so comments and trailing commas are allowed.
15
- - The proxy configured by this plugin only affects this plugin.
16
-
17
- ## Recommended Install
18
-
19
- Use the OpenClaw plugin installer:
20
-
21
- ```bash
22
- openclaw plugins install @invago/mixin
23
- ```
24
-
25
- `@invago/mixin` is the published npm package name. The OpenClaw runtime/plugin name remains `mixin`.
26
-
27
- If the plugin is already installed, upgrade it with the plugin id:
28
-
29
- ```bash
30
- openclaw plugins update mixin
31
- ```
32
-
33
- To install a specific version for the first time:
34
-
35
- ```bash
36
- openclaw plugins install @invago/mixin@<version>
37
- ```
5
+ > Supported on the latest OpenClaw 3.23 plugin architecture.
38
6
 
7
+ **[Chinese Documentation](README.zh-CN.md)**
8
+
9
+ ## Overview
10
+
11
+ MixinClaw is an OpenClaw channel plugin. It runs in the same process as the OpenClaw Gateway, receives inbound messages from Mixin Blaze WebSocket, and delivers outbound messages over the Mixin HTTP API.
12
+
13
+ Important:
14
+
15
+ - Install the plugin on the same machine where the OpenClaw Gateway runs.
16
+ - OpenClaw config files use JSON5, so comments and trailing commas are allowed.
17
+ - The proxy configured by this plugin only affects this plugin.
18
+
19
+ ## Recommended Install
20
+
21
+ Use the OpenClaw plugin installer:
22
+
23
+ ```bash
24
+ openclaw plugins install @invago/mixin
25
+ ```
26
+
27
+ `@invago/mixin` is the published npm package name. The OpenClaw runtime/plugin name remains `mixin`.
28
+
29
+ If the plugin is already installed, upgrade it with the plugin id:
30
+
31
+ ```bash
32
+ openclaw plugins update mixin
33
+ ```
34
+
35
+ To install a specific version for the first time:
36
+
37
+ ```bash
38
+ openclaw plugins install @invago/mixin@<version>
39
+ ```
40
+
39
41
  Then confirm the plugin is installed:
40
42
 
41
43
  ```bash
@@ -43,642 +45,663 @@ openclaw plugins list
43
45
  openclaw plugins info mixin
44
46
  ```
45
47
 
46
- ## Local Development Install
47
-
48
- If you are developing locally, clone the repository and install dependencies:
49
-
50
- ```bash
51
- git clone https://github.com/invago/mixinclaw.git
52
- cd mixinclaw
53
- npm install
54
- ```
55
-
56
- Then install it into OpenClaw from the local path:
57
-
58
- ```bash
59
- openclaw plugins install .
60
- ```
61
-
62
- ## Create a Mixin Bot
63
-
64
- Go to [Mixin Developers Dashboard](https://developers.mixin.one/dashboard), create a bot, and collect:
65
-
66
- - `appId`
67
- - `sessionId`
68
- - `serverPublicKey`
69
- - `sessionPrivateKey`
70
-
71
- ## Configuration
72
-
73
- Edit your `openclaw.json` file manually and add both the channel configuration and the plugin enablement block:
74
-
75
- ```json
76
- {
77
- "channels": {
78
- "mixin": {
79
- "defaultAccount": "default",
80
- "appId": "YOUR_APP_ID",
81
- "sessionId": "YOUR_SESSION_ID",
82
- "serverPublicKey": "YOUR_SERVER_PUBLIC_KEY_BASE64",
83
- "sessionPrivateKey": "YOUR_SESSION_PRIVATE_KEY_BASE64",
84
- "dmPolicy": "pairing",
85
- "allowFrom": ["AUTHORIZED_USER_UUID"],
86
- "requireMentionInGroup": true,
87
- "mediaBypassMentionInGroup": true,
88
- "mediaMaxMb": 30,
89
- "audioSendAsVoiceByDefault": true,
90
- "audioAutoDetectDuration": true,
91
- "audioRequireFfprobe": false,
92
- "mixpay": {
93
- "enabled": true,
94
- "payeeId": "YOUR_MIXPAY_PAYEE_ID",
95
- "defaultSettlementAssetId": "YOUR_SETTLEMENT_ASSET_ID",
96
- "expireMinutes": 15,
97
- "pollIntervalSec": 30,
98
- "allowedCreators": ["AUTHORIZED_USER_UUID"],
99
- "notifyOnPending": false,
100
- "notifyOnPaidLess": true
101
- },
102
- "proxy": {
103
- "enabled": true,
104
- "url": "socks5://127.0.0.1:10808",
105
- "username": "proxy-user",
106
- "password": "proxy-pass"
107
- }
108
- }
109
- },
110
- "plugins": {
111
- "allow": ["mixin"],
112
- "entries": {
113
- "mixin": {
114
- "enabled": true
115
- }
116
- }
117
- }
118
- }
119
- ```
120
-
121
- Notes:
122
-
123
- - `channels.mixin` configures the channel itself.
124
- - `plugins.allow` and `plugins.entries.mixin.enabled` are also required so OpenClaw loads this plugin.
125
- - Mixin supports the standard OpenClaw direct-message policies. The recommended setting is `dmPolicy: "pairing"`.
126
- - `allowFrom` remains useful for pre-authorized users or manual overrides. Pairing approvals are stored in OpenClaw's pairing allowlist store.
127
- - If `proxy.url` already contains credentials, `proxy.username` and `proxy.password` can be omitted.
128
-
129
- ## Pairing
130
-
131
- For private chats, the recommended mode is:
132
-
133
- ```json
134
- {
135
- "channels": {
136
- "mixin": {
137
- "dmPolicy": "pairing"
138
- }
139
- }
140
- }
141
- ```
142
-
143
- Behavior:
144
-
145
- - A new, unauthorized Mixin user gets an 8-character pairing code in the DM.
146
- - Approve that user with `openclaw pairing approve mixin <code>`.
147
- - Use `openclaw pairing list mixin` to inspect pending pairing requests.
148
- - Once approved, the user is added to OpenClaw's pairing allowlist store for the `mixin` channel.
149
- - `allowFrom` is still honored and can be used alongside pairing for users you want to pre-authorize.
150
-
151
- ## Avoid Cross-Channel Session Mixing
152
-
153
- Mixin group chats already stay isolated by channel, but direct-message sessions follow the OpenClaw `session.dmScope` policy. If you keep the default `main` scope, Mixin direct messages can share the same main session with other channels such as Feishu.
154
-
155
- Recommended configuration:
156
-
157
- ```json
158
- {
159
- "session": {
160
- "dmScope": "per-channel-peer"
161
- }
162
- }
163
- ```
164
-
165
- Use `per-account-channel-peer` instead if you run multiple Mixin accounts and want direct-message sessions isolated by both channel and account.
166
-
167
- ## Multi-Agent Routing Per Bot Account
168
-
169
- OpenClaw supports routing different channel accounts to different agents through `bindings[].match.accountId`.
170
-
171
- Recommended pattern:
172
-
173
- - One Mixin bot account = one `accountId`
174
- - One `accountId` = one agent binding
175
- - Keep session isolation at `per-account-channel-peer` when you run multiple bot accounts
176
-
177
- Example:
178
-
179
- ```json
180
- {
181
- "session": {
182
- "dmScope": "per-account-channel-peer"
183
- },
184
- "agents": {
185
- "list": [
186
- {
187
- "id": "main",
188
- "workspace": "E:/AI/workspace-main",
189
- "default": true
190
- },
191
- {
192
- "id": "sales",
193
- "workspace": "E:/AI/workspace-sales"
194
- },
195
- {
196
- "id": "support",
197
- "workspace": "E:/AI/workspace-support"
198
- }
199
- ]
200
- },
201
- "bindings": [
202
- {
203
- "agentId": "main",
204
- "match": {
205
- "channel": "mixin",
206
- "accountId": "default"
207
- }
208
- },
209
- {
210
- "agentId": "sales",
211
- "match": {
212
- "channel": "mixin",
213
- "accountId": "sales"
214
- }
215
- },
216
- {
217
- "agentId": "support",
218
- "match": {
219
- "channel": "mixin",
220
- "accountId": "support"
221
- }
222
- }
223
- ],
224
- "channels": {
225
- "mixin": {
226
- "defaultAccount": "default",
227
- "accounts": {
228
- "default": {
229
- "name": "Main Bot",
230
- "appId": "APP_ID_1",
231
- "sessionId": "SESSION_ID_1",
232
- "serverPublicKey": "SERVER_PUBLIC_KEY_1",
233
- "sessionPrivateKey": "SESSION_PRIVATE_KEY_1"
234
- },
235
- "sales": {
236
- "name": "Sales Bot",
237
- "appId": "APP_ID_2",
238
- "sessionId": "SESSION_ID_2",
239
- "serverPublicKey": "SERVER_PUBLIC_KEY_2",
240
- "sessionPrivateKey": "SESSION_PRIVATE_KEY_2"
241
- },
242
- "support": {
243
- "name": "Support Bot",
244
- "appId": "APP_ID_3",
245
- "sessionId": "SESSION_ID_3",
246
- "serverPublicKey": "SERVER_PUBLIC_KEY_3",
247
- "sessionPrivateKey": "SESSION_PRIVATE_KEY_3"
248
- }
249
- }
250
- }
251
- }
252
- }
253
- ```
254
-
255
- Notes:
256
-
257
- - `match.accountId` binds one Mixin bot account to one agent.
258
- - If `accountId` is omitted in a binding, OpenClaw treats it as the default account only.
259
- - Use `accountId: "*"` only when you want one fallback agent for all Mixin accounts.
260
- - If you need one specific group or DM to override the account-level routing, add a more specific `match.peer` binding. Peer matches win over `accountId` matches.
261
-
262
- ## Configuration Reference
48
+ ## Cross-Platform Checklist
263
49
 
264
- | Parameter | Required | Default | Description |
265
- |-----------|----------|---------|-------------|
266
- | `defaultAccount` | No | `default` | Default account ID used when `accounts` is configured |
267
- | `appId` | Yes | - | Mixin App UUID |
268
- | `sessionId` | Yes | - | Session UUID |
269
- | `serverPublicKey` | Yes | - | Server Public Key (Base64) |
270
- | `sessionPrivateKey` | Yes | - | Session Private Key (Ed25519 Base64) |
271
- | `dmPolicy` | No | `pairing` | Direct-message policy: `pairing`, `allowlist`, `open`, or `disabled` |
272
- | `allowFrom` | No | `[]` | Authorized user UUID whitelist |
273
- | `groupPolicy` | No | OpenClaw default | Group-message policy: `open`, `allowlist`, or `disabled` |
274
- | `groupAllowFrom` | No | `[]` | Authorized sender UUID whitelist for group messages when `groupPolicy` uses allowlisting |
275
- | `requireMentionInGroup` | No | `true` | Apply plugin-side trigger-word filtering to group messages that have already been delivered to the bot |
276
- | `mediaBypassMentionInGroup` | No | `true` | Allow inbound group file/audio messages through even without trigger text |
277
- | `mediaMaxMb` | No | `30` | Max inbound and outbound media size in MB |
278
- | `audioSendAsVoiceByDefault` | No | `true` | Send OpenClaw native outbound audio as Mixin voice when possible |
279
- | `audioAutoDetectDuration` | No | `true` | Detect native outbound audio duration with `ffprobe` before sending voice |
280
- | `audioRequireFfprobe` | No | `false` | Fail native outbound audio instead of falling back to file when duration detection is unavailable |
281
- | `mixpay.enabled` | No | `false` | Enable MixPay collect support for this Mixin account |
282
- | `mixpay.payeeId` | Required when enabled | - | MixPay merchant/payee ID used to create one-time payment orders |
283
- | `mixpay.defaultQuoteAssetId` | No | - | Default quote asset ID for collect templates or future collect commands |
284
- | `mixpay.defaultSettlementAssetId` | No | - | Default settlement asset ID for MixPay orders |
285
- | `mixpay.expireMinutes` | No | `15` | Default MixPay order expiration time in minutes |
286
- | `mixpay.pollIntervalSec` | No | `30` | Poll interval in seconds for pending MixPay orders |
287
- | `mixpay.allowedCreators` | No | `[]` | Optional sender UUID allowlist for creating MixPay collect orders |
288
- | `mixpay.notifyOnPending` | No | `false` | Notify the chat when MixPay reports `pending` |
289
- | `mixpay.notifyOnPaidLess` | No | `true` | Notify the chat when MixPay indicates an underpayment |
290
- | `conversations.<conversationId>.enabled` | No | `true` | Enable or disable a specific group conversation |
291
- | `conversations.<conversationId>.requireMention` | No | Inherit account | Override group trigger-word requirement for a specific conversation |
292
- | `conversations.<conversationId>.allowFrom` | No | Inherit account | Override group sender allowlist for a specific conversation |
293
- | `conversations.<conversationId>.mediaBypassMention` | No | Inherit account | Override whether file/audio messages bypass mention filtering |
294
- | `conversations.<conversationId>.groupPolicy` | No | Inherit account | Override group policy for a specific conversation |
295
- | `debug` | No | `false` | Debug mode |
296
- | `proxy.enabled` | No | `false` | Enable per-plugin proxy |
297
- | `proxy.url` | Required when enabled | - | Proxy URL such as `http://127.0.0.1:7890` or `socks5://127.0.0.1:10808` |
298
- | `proxy.username` | No | - | Proxy username |
299
- | `proxy.password` | No | - | Proxy password |
300
-
301
- ## Proxy
302
-
303
- - Both Mixin HTTP requests and Blaze WebSocket traffic use the same proxy.
304
- - Supported proxy URL styles depend on the underlying proxy agent stack; typical values are `http://...`, `https://...`, and `socks5://...`.
305
- - You must provide your own proxy software or proxy server. The plugin only consumes a proxy, it does not create one.
306
-
307
- ## Group Access Control
308
-
309
- Mixin now supports formal group access controls in addition to direct-message `dmPolicy`.
310
-
311
- - `groupPolicy: "open"` allows any sender in a group conversation.
312
- - `groupPolicy: "allowlist"` requires the sender UUID to appear in `groupAllowFrom`.
313
- - `groupPolicy: "disabled"` blocks the entire conversation.
314
- - `conversations.<conversationId>` overrides account-level group settings for that single conversation.
50
+ - The same install commands work on Windows, Linux, and macOS.
51
+ - Make sure `openclaw`, `node`, and your package manager (`npm` or `pnpm`) are available on `PATH`.
52
+ - Voice duration detection needs `ffprobe`. If it is missing, audio falls back to file sending unless `audioRequireFfprobe` is enabled.
53
+ - For local development, run `npm install` once and then `openclaw plugins install -l .` or `openclaw plugins install .`.
54
+ - Runtime data is stored under the OpenClaw state directory resolved from `OPENCLAW_STATE_DIR`, `CLAWDBOT_STATE_DIR`, or `OPENCLAW_HOME`; no OS-specific path is required in plugin config.
315
55
 
56
+ ## Local Development Install
57
+
58
+ If you are developing locally, clone the repository and install dependencies:
59
+
60
+ ```bash
61
+ git clone https://github.com/invago/mixinclaw.git
62
+ cd mixinclaw
63
+ npm install
64
+ ```
65
+
66
+ Then install it into OpenClaw from the local path:
67
+
68
+ ```bash
69
+ openclaw plugins install .
70
+ ```
71
+
72
+ ## Create a Mixin Bot
73
+
74
+ Go to [Mixin Developers Dashboard](https://developers.mixin.one/dashboard), create a bot, and collect:
75
+
76
+ - `appId`
77
+ - `sessionId`
78
+ - `serverPublicKey`
79
+ - `sessionPrivateKey`
80
+
81
+ ## Configuration
82
+
83
+ Edit your `openclaw.json` file manually and add both the channel configuration and the plugin enablement block:
84
+
85
+ ```json
86
+ {
87
+ "channels": {
88
+ "mixin": {
89
+ "defaultAccount": "default",
90
+ "appId": "YOUR_APP_ID",
91
+ "sessionId": "YOUR_SESSION_ID",
92
+ "serverPublicKey": "YOUR_SERVER_PUBLIC_KEY_BASE64",
93
+ "sessionPrivateKey": "YOUR_SESSION_PRIVATE_KEY_BASE64",
94
+ "dmPolicy": "pairing",
95
+ "allowFrom": ["AUTHORIZED_USER_UUID"],
96
+ "requireMentionInGroup": true,
97
+ "mediaBypassMentionInGroup": true,
98
+ "mediaMaxMb": 30,
99
+ "audioSendAsVoiceByDefault": true,
100
+ "audioAutoDetectDuration": true,
101
+ "audioRequireFfprobe": false,
102
+ "mixpay": {
103
+ "enabled": true,
104
+ "payeeId": "YOUR_MIXPAY_PAYEE_ID",
105
+ "defaultSettlementAssetId": "YOUR_SETTLEMENT_ASSET_ID",
106
+ "expireMinutes": 15,
107
+ "pollIntervalSec": 30,
108
+ "allowedCreators": ["AUTHORIZED_USER_UUID"],
109
+ "notifyOnPending": false,
110
+ "notifyOnPaidLess": true
111
+ },
112
+ "proxy": {
113
+ "enabled": true,
114
+ "url": "socks5://127.0.0.1:10808",
115
+ "username": "proxy-user",
116
+ "password": "proxy-pass"
117
+ }
118
+ }
119
+ },
120
+ "plugins": {
121
+ "allow": ["mixin"],
122
+ "entries": {
123
+ "mixin": {
124
+ "enabled": true
125
+ }
126
+ }
127
+ }
128
+ }
129
+ ```
130
+
131
+ Notes:
132
+
133
+ - `channels.mixin` configures the channel itself.
134
+ - `plugins.allow` and `plugins.entries.mixin.enabled` are also required so OpenClaw loads this plugin.
135
+ - Mixin supports the standard OpenClaw direct-message policies. The recommended setting is `dmPolicy: "pairing"`.
136
+ - `allowFrom` remains useful for pre-authorized users or manual overrides. Pairing approvals are stored in OpenClaw's pairing allowlist store.
137
+ - If `proxy.url` already contains credentials, `proxy.username` and `proxy.password` can be omitted.
138
+
139
+ ## Pairing
140
+
141
+ For private chats, the recommended mode is:
142
+
143
+ ```json
144
+ {
145
+ "channels": {
146
+ "mixin": {
147
+ "dmPolicy": "pairing"
148
+ }
149
+ }
150
+ }
151
+ ```
152
+
153
+ Behavior:
154
+
155
+ - A new, unauthorized Mixin user gets an 8-character pairing code in the DM.
156
+ - Approve that user with `openclaw pairing approve mixin <code>`.
157
+ - Use `openclaw pairing list mixin` to inspect pending pairing requests.
158
+ - Once approved, the user is added to OpenClaw's pairing allowlist store for the `mixin` channel.
159
+ - `allowFrom` is still honored and can be used alongside pairing for users you want to pre-authorize.
160
+
161
+ ## Avoid Cross-Channel Session Mixing
162
+
163
+ Mixin group chats already stay isolated by channel, but direct-message sessions follow the OpenClaw `session.dmScope` policy. If you keep the default `main` scope, Mixin direct messages can share the same main session with other channels such as Feishu.
164
+
165
+ Recommended configuration:
166
+
167
+ ```json
168
+ {
169
+ "session": {
170
+ "dmScope": "per-channel-peer"
171
+ }
172
+ }
173
+ ```
174
+
175
+ Use `per-account-channel-peer` instead if you run multiple Mixin accounts and want direct-message sessions isolated by both channel and account.
176
+
177
+ ## Multi-Agent Routing Per Bot Account
178
+
179
+ OpenClaw supports routing different channel accounts to different agents through `bindings[].match.accountId`.
180
+
181
+ Recommended pattern:
182
+
183
+ - One Mixin bot account = one `accountId`
184
+ - One `accountId` = one agent binding
185
+ - Keep session isolation at `per-account-channel-peer` when you run multiple bot accounts
186
+
187
+ Example:
188
+
189
+ ```json
190
+ {
191
+ "session": {
192
+ "dmScope": "per-account-channel-peer"
193
+ },
194
+ "agents": {
195
+ "list": [
196
+ {
197
+ "id": "main",
198
+ "workspace": "E:/AI/workspace-main",
199
+ "default": true
200
+ },
201
+ {
202
+ "id": "sales",
203
+ "workspace": "E:/AI/workspace-sales"
204
+ },
205
+ {
206
+ "id": "support",
207
+ "workspace": "E:/AI/workspace-support"
208
+ }
209
+ ]
210
+ },
211
+ "bindings": [
212
+ {
213
+ "agentId": "main",
214
+ "match": {
215
+ "channel": "mixin",
216
+ "accountId": "default"
217
+ }
218
+ },
219
+ {
220
+ "agentId": "sales",
221
+ "match": {
222
+ "channel": "mixin",
223
+ "accountId": "sales"
224
+ }
225
+ },
226
+ {
227
+ "agentId": "support",
228
+ "match": {
229
+ "channel": "mixin",
230
+ "accountId": "support"
231
+ }
232
+ }
233
+ ],
234
+ "channels": {
235
+ "mixin": {
236
+ "defaultAccount": "default",
237
+ "accounts": {
238
+ "default": {
239
+ "name": "Main Bot",
240
+ "appId": "APP_ID_1",
241
+ "sessionId": "SESSION_ID_1",
242
+ "serverPublicKey": "SERVER_PUBLIC_KEY_1",
243
+ "sessionPrivateKey": "SESSION_PRIVATE_KEY_1"
244
+ },
245
+ "sales": {
246
+ "name": "Sales Bot",
247
+ "appId": "APP_ID_2",
248
+ "sessionId": "SESSION_ID_2",
249
+ "serverPublicKey": "SERVER_PUBLIC_KEY_2",
250
+ "sessionPrivateKey": "SESSION_PRIVATE_KEY_2"
251
+ },
252
+ "support": {
253
+ "name": "Support Bot",
254
+ "appId": "APP_ID_3",
255
+ "sessionId": "SESSION_ID_3",
256
+ "serverPublicKey": "SERVER_PUBLIC_KEY_3",
257
+ "sessionPrivateKey": "SESSION_PRIVATE_KEY_3"
258
+ }
259
+ }
260
+ }
261
+ }
262
+ }
263
+ ```
264
+
265
+ Notes:
266
+
267
+ - `match.accountId` binds one Mixin bot account to one agent.
268
+ - If `accountId` is omitted in a binding, OpenClaw treats it as the default account only.
269
+ - Use `accountId: "*"` only when you want one fallback agent for all Mixin accounts.
270
+ - If you need one specific group or DM to override the account-level routing, add a more specific `match.peer` binding. Peer matches win over `accountId` matches.
271
+
272
+ ## Configuration Reference
273
+
274
+ | Parameter | Required | Default | Description |
275
+ |-----------|----------|---------|-------------|
276
+ | `defaultAccount` | No | `default` | Default account ID used when `accounts` is configured |
277
+ | `appId` | Yes | - | Mixin App UUID |
278
+ | `sessionId` | Yes | - | Session UUID |
279
+ | `serverPublicKey` | Yes | - | Server Public Key (Base64) |
280
+ | `sessionPrivateKey` | Yes | - | Session Private Key (Ed25519 Base64) |
281
+ | `dmPolicy` | No | `pairing` | Direct-message policy: `pairing`, `allowlist`, `open`, or `disabled` |
282
+ | `allowFrom` | No | `[]` | Authorized user UUID whitelist |
283
+ | `groupPolicy` | No | OpenClaw default | Group-message policy: `open`, `allowlist`, or `disabled` |
284
+ | `groupAllowFrom` | No | `[]` | Authorized sender UUID whitelist for group messages when `groupPolicy` uses allowlisting |
285
+ | `requireMentionInGroup` | No | `true` | Apply plugin-side trigger-word filtering to group messages that have already been delivered to the bot |
286
+ | `mediaBypassMentionInGroup` | No | `true` | Allow inbound group file/audio messages through even without trigger text |
287
+ | `mediaMaxMb` | No | `30` | Max inbound and outbound media size in MB |
288
+ | `audioSendAsVoiceByDefault` | No | `true` | Send OpenClaw native outbound audio as Mixin voice when possible |
289
+ | `audioAutoDetectDuration` | No | `true` | Detect native outbound audio duration with `ffprobe` before sending voice |
290
+ | `audioRequireFfprobe` | No | `false` | Fail native outbound audio instead of falling back to file when duration detection is unavailable |
291
+ | `mixpay.enabled` | No | `false` | Enable MixPay collect support for this Mixin account |
292
+ | `mixpay.payeeId` | Required when enabled | - | MixPay merchant/payee ID used to create one-time payment orders |
293
+ | `mixpay.defaultQuoteAssetId` | No | - | Default quote asset ID for collect templates or future collect commands |
294
+ | `mixpay.defaultSettlementAssetId` | No | - | Default settlement asset ID for MixPay orders |
295
+ | `mixpay.expireMinutes` | No | `15` | Default MixPay order expiration time in minutes |
296
+ | `mixpay.pollIntervalSec` | No | `30` | Poll interval in seconds for pending MixPay orders |
297
+ | `mixpay.allowedCreators` | No | `[]` | Optional sender UUID allowlist for creating MixPay collect orders |
298
+ | `mixpay.notifyOnPending` | No | `false` | Notify the chat when MixPay reports `pending` |
299
+ | `mixpay.notifyOnPaidLess` | No | `true` | Notify the chat when MixPay indicates an underpayment |
300
+ | `conversations.<conversationId>.enabled` | No | `true` | Enable or disable a specific group conversation |
301
+ | `conversations.<conversationId>.requireMention` | No | Inherit account | Override group trigger-word requirement for a specific conversation |
302
+ | `conversations.<conversationId>.allowFrom` | No | Inherit account | Override group sender allowlist for a specific conversation |
303
+ | `conversations.<conversationId>.mediaBypassMention` | No | Inherit account | Override whether file/audio messages bypass mention filtering |
304
+ | `conversations.<conversationId>.groupPolicy` | No | Inherit account | Override group policy for a specific conversation |
305
+ | `debug` | No | `false` | Debug mode |
306
+ | `proxy.enabled` | No | `false` | Enable per-plugin proxy |
307
+ | `proxy.url` | Required when enabled | - | Proxy URL such as `http://127.0.0.1:7890` or `socks5://127.0.0.1:10808` |
308
+ | `proxy.username` | No | - | Proxy username |
309
+ | `proxy.password` | No | - | Proxy password |
310
+
311
+ ## Proxy
312
+
313
+ - Both Mixin HTTP requests and Blaze WebSocket traffic use the same proxy.
314
+ - Supported proxy URL styles depend on the underlying proxy agent stack; typical values are `http://...`, `https://...`, and `socks5://...`.
315
+ - You must provide your own proxy software or proxy server. The plugin only consumes a proxy, it does not create one.
316
+
317
+ ## Group Access Control
318
+
319
+ Mixin now supports formal group access controls in addition to direct-message `dmPolicy`.
320
+
321
+ - `groupPolicy: "open"` allows any sender in a group conversation.
322
+ - `groupPolicy: "allowlist"` requires the sender UUID to appear in `groupAllowFrom`.
323
+ - `groupPolicy: "disabled"` blocks the entire conversation.
324
+ - `conversations.<conversationId>` overrides account-level group settings for that single conversation.
325
+
316
326
  Important delivery boundary:
317
327
 
318
328
  - In practice, Mixin group bots reliably receive messages when the bot is explicitly mentioned.
329
+ - The most reliable format is `@<identity_number> your message`, for example `@7000103034 hello`.
319
330
  - `requireMentionInGroup: false` only disables this plugin's own post-delivery filtering.
320
331
  - It does not guarantee that Mixin will deliver every non-mention group message to the bot.
321
332
  - If a non-mention group message produces no read receipt and no inbound log, the message most likely was not delivered to the plugin by Mixin in the first place.
322
-
323
- Example:
324
-
325
- ```json
326
- {
327
- "channels": {
328
- "mixin": {
329
- "groupPolicy": "allowlist",
330
- "groupAllowFrom": ["USER_A_UUID"],
331
- "conversations": {
332
- "70000000-0000-0000-0000-000000000001": {
333
- "requireMention": false,
334
- "allowFrom": ["USER_B_UUID"],
335
- "mediaBypassMention": false
336
- },
337
- "70000000-0000-0000-0000-000000000002": {
338
- "enabled": false
339
- }
340
- }
341
- }
342
- }
343
- }
344
- ```
345
-
346
- How to get these values:
347
-
348
- - `conversations.<conversationId>`: use the group's `conversation_id`. In practice, the easiest way is to let the group send a message to the bot once, then read the `conversationId` from the plugin logs or inbound event context. Mixin's conversation APIs also use the same `conversation_id` field for group conversations.
349
- - `groupAllowFrom` or `conversations.<conversationId>.allowFrom`: use the sender's Mixin `user_id` UUID. Mixin user IDs can be learned when the user messages the bot, adds the bot as a contact, or authorizes the application.
350
- - If you manage the group through Mixin APIs, the returned conversation payload also includes group participants with their `user_id` fields.
351
-
352
- Recommended operational approach:
353
-
354
- - Let the target group send one message to the bot
355
- - Copy the logged `conversationId`
356
- - Let the target member send one message, then copy that sender's `user_id`
357
- - Put those values into `conversations.<conversationId>` and `groupAllowFrom` / `allowFrom`
358
-
359
- Pairing-style group authorization:
360
-
361
- - An unauthorized user can send `/mixin-group-auth` in the target group
362
- - The plugin replies with a temporary approval code for that `conversationId`
363
- - An operator must approve it in the OpenClaw terminal with `openclaw pairing approve mixin <code>`
364
- - For non-default accounts, use `openclaw pairing approve --account <accountId> mixin <code>`
365
- - Once approved, that entire group conversation is allowed without changing `openclaw.json`
366
- - Repeated `/mixin-group-auth` requests from the same unauthorized group are rate-limited to avoid spam
367
-
368
- Where to look in logs:
369
-
370
- - The plugin logs route resolution like `peer.kind=group, peer.id=<conversationId>`, which gives you the group `conversationId`
371
- - Unauthorized or filtered group logs include `group sender <user_id>` and `conversationId=<conversationId>`
372
- - If needed, temporarily enable a stricter group policy and let one member send a message once; the rejection log is often the fastest way to collect both values
373
-
333
+ - Group quote/reply interactions are currently not treated as a reliable bot trigger, because Mixin may not deliver those events to the bot over Blaze consistently.
334
+
335
+ Example:
336
+
337
+ ```json
338
+ {
339
+ "channels": {
340
+ "mixin": {
341
+ "groupPolicy": "allowlist",
342
+ "groupAllowFrom": ["USER_A_UUID"],
343
+ "conversations": {
344
+ "70000000-0000-0000-0000-000000000001": {
345
+ "requireMention": false,
346
+ "allowFrom": ["USER_B_UUID"],
347
+ "mediaBypassMention": false
348
+ },
349
+ "70000000-0000-0000-0000-000000000002": {
350
+ "enabled": false
351
+ }
352
+ }
353
+ }
354
+ }
355
+ }
356
+ ```
357
+
358
+ How to get these values:
359
+
360
+ - `conversations.<conversationId>`: use the group's `conversation_id`. In practice, the easiest way is to let the group send a message to the bot once, then read the `conversationId` from the plugin logs or inbound event context. Mixin's conversation APIs also use the same `conversation_id` field for group conversations.
361
+ - `groupAllowFrom` or `conversations.<conversationId>.allowFrom`: use the sender's Mixin `user_id` UUID. Mixin user IDs can be learned when the user messages the bot, adds the bot as a contact, or authorizes the application.
362
+ - If you manage the group through Mixin APIs, the returned conversation payload also includes group participants with their `user_id` fields.
363
+
364
+ Recommended operational approach:
365
+
366
+ - Let the target group send one message to the bot
367
+ - Copy the logged `conversationId`
368
+ - Let the target member send one message, then copy that sender's `user_id`
369
+ - Put those values into `conversations.<conversationId>` and `groupAllowFrom` / `allowFrom`
370
+
371
+ Pairing-style group authorization:
372
+
373
+ - An unauthorized user can send `/mixin-group-auth` in the target group
374
+ - The plugin replies with a temporary approval code for that `conversationId`
375
+ - An operator must approve it in the OpenClaw terminal with `openclaw pairing approve mixin <code>`
376
+ - For non-default accounts, use `openclaw pairing approve --account <accountId> mixin <code>`
377
+ - Once approved, that entire group conversation is allowed without changing `openclaw.json`
378
+ - Repeated `/mixin-group-auth` requests from the same unauthorized group are rate-limited to avoid spam
379
+
380
+ Where to look in logs:
381
+
382
+ - The plugin logs route resolution like `peer.kind=group, peer.id=<conversationId>`, which gives you the group `conversationId`
383
+ - Unauthorized or filtered group logs include `group sender <user_id>` and `conversationId=<conversationId>`
384
+ - If needed, temporarily enable a stricter group policy and let one member send a message once; the rejection log is often the fastest way to collect both values
385
+
374
386
  ## Usage
375
387
 
376
388
  - Direct message: `/status` or `Hello`
377
- - Group message: `@Bot your question` with trigger words such as `?` or `help`
378
-
379
- ## Operations
380
-
381
- Useful OpenClaw commands:
382
-
383
- ```bash
384
- openclaw plugins list
385
- openclaw plugins info mixin
386
- openclaw plugins update mixin
387
- openclaw channels status --probe
388
- openclaw status
389
- ```
390
-
391
- Plugin-specific command:
392
-
393
- - Send `/mixin-outbox` to inspect the current pending queue size, next retry time, and latest error.
394
- - Send `/mixin-outbox purge-invalid` to remove old `APP_CARD` / `APP_BUTTON_GROUP` entries that are stuck on permanent invalid-field errors.
395
- - Send `/mixin-group-auth` in a group to create a pending group-authorization request.
396
- - Approve a pending group-authorization request in the OpenClaw terminal with `openclaw pairing approve mixin <code>`.
397
- - For non-default accounts, use `openclaw pairing approve --account <accountId> mixin <code>`.
398
- - Send `/collect status <orderId>` to refresh and inspect a stored MixPay collect order.
399
- - Send `/collect recent` or `/collect recent 10` to list recent MixPay collect orders for the current conversation.
400
-
401
- Companion onboarding CLI:
402
-
403
- - This repository also includes a companion CLI at `tools/mixin-plugin-onboard/README.md`.
404
- - It is bundled into the same npm package, `@invago/mixin`.
405
- - It currently provides `info`, `doctor`, `install`, and `update` commands for local OpenClaw + Mixin plugin maintenance.
406
- - Local examples:
407
- - `node --import jiti/register.js tools/mixin-plugin-onboard/src/index.ts info`
408
- - `node --import jiti/register.js tools/mixin-plugin-onboard/src/index.ts doctor`
409
- - Installed package examples:
410
- - `npx -y @invago/mixin info`
411
- - `npx -y @invago/mixin doctor`
412
-
413
- ## Delivery and Retry Behavior
414
-
415
- - Outbound messages are persisted to a local outbox before send attempts.
416
- - Failed sends are retried automatically until they succeed.
417
- - Pending messages survive plugin restarts.
418
- - Inbound Blaze messages are acknowledged before dispatch so Mixin receives a read receipt as early as possible.
419
-
420
- ## Media Support
421
-
422
- Current media behavior is split into outbound and inbound support:
423
-
424
- - OpenClaw native outbound media is enabled through the channel `sendMedia` path.
425
- - OpenClaw native `sendPayload` now uses the same Mixin outbound planner as buffered agent replies, so text/post/buttons/card/file/audio selection is consistent.
426
- - The plugin sends outbound audio as `PLAIN_AUDIO` when it can resolve the media as audio and detect duration.
427
- - If audio duration cannot be detected, the plugin falls back to regular file attachment sending.
428
- - Non-audio outbound media is sent as Mixin file attachments.
429
- - If OpenClaw sends both caption text and media, the plugin sends the text first and then the file.
430
- - Voice-bubble style outbound audio is currently intended for the explicit `mixin-audio` template path.
431
- - Inbound `PLAIN_DATA` and `PLAIN_AUDIO` messages are downloaded, saved locally, and attached to the OpenClaw inbound context through `MediaPath` / `MediaType`.
432
- - Group attachment messages are allowed through even when `requireMentionInGroup` is enabled, unless `mediaBypassMentionInGroup` is set to `false`.
433
-
434
- Current limits:
435
-
436
- - Outbound audio does not transcode automatically.
437
- - `mixin-audio` still requires a prepared local file, explicit `duration`, and optional `waveForm`.
438
- - OpenClaw native outbound audio depends on local `ffprobe` availability to detect duration.
439
- - OpenClaw native `sendMedia` still does not generate `waveForm`, so explicit `mixin-audio` remains the most deterministic path for polished voice-message output.
440
- - Whether the agent can summarize, transcribe, or reason over inbound files/audio depends on your OpenClaw media-understanding configuration.
441
-
442
- Manual test guide:
443
-
444
- - See [docs/media-testing.md](docs/media-testing.md) for ready-to-run prompts and expected results.
445
-
446
- ## MixPay Collect
447
-
448
- Mixin now supports MixPay collection through one-time payment orders.
449
-
450
- Current capabilities:
451
-
452
- - `mixin-collect` explicit reply template creates a MixPay collect order
453
- - Collect orders are stored locally under the OpenClaw state directory
454
- - Pending orders are polled in the background
455
- - Success and terminal status changes are sent back to the original conversation
456
- - `/collect status <orderId>` refreshes the order from MixPay before replying
457
- - `assetId` in the template can be omitted when `mixpay.defaultQuoteAssetId` is configured
458
-
459
- Template example:
460
-
461
- ````text
462
- ```mixin-collect
463
- {
464
- "amount": "1",
465
- "assetId": "c6d0c728-2624-429b-8e0d-d9d19b6592fa",
466
- "memo": "Order #1001"
467
- }
468
- ```
469
- ````
470
-
471
- Rules:
472
-
473
- - `amount` is required; `assetId` is required unless `mixpay.defaultQuoteAssetId` is configured
474
- - `settlementAssetId`, `memo`, `orderId`, and `expireMinutes` are optional
475
- - Payment success is confirmed from MixPay server-side query results, not only from the client page
476
- - `mixpay.allowedCreators` can restrict who is allowed to create collect orders
477
-
478
- Where funds arrive:
479
-
480
- - For MixPay `Mixin account`, funds settle into the linked Mixin Wallet
481
- - For MixPay `Mixin Robot account`, funds settle into the linked Mixin Robot Wallet
482
- - Other MixPay account types settle into their own linked wallet types
483
-
484
- Recommended setup for this plugin:
485
-
486
- - Use a MixPay `Mixin account` or `Mixin Robot account`
487
- - Use that account's UUID as `mixpay.payeeId`
488
- - Set both `mixpay.defaultQuoteAssetId` and `mixpay.defaultSettlementAssetId` if you want templates to stay short
489
-
490
- How to get the required values:
491
-
492
- - `mixpay.payeeId`: get the UUID from the [MixPay Dashboard](https://dashboard.mixpay.me) settings page, or use the MixPay helper bot described in the official getting-started guide
493
- - `mixpay.defaultQuoteAssetId`: choose the asset ID you want to quote prices in
494
- - `mixpay.defaultSettlementAssetId`: choose the asset ID you want funds to settle into
495
-
496
- Minimal recommended config:
497
-
498
- ```json
499
- {
500
- "channels": {
501
- "mixin": {
502
- "mixpay": {
503
- "enabled": true,
504
- "payeeId": "YOUR_MIXPAY_UUID",
505
- "defaultQuoteAssetId": "YOUR_QUOTE_ASSET_ID",
506
- "defaultSettlementAssetId": "YOUR_SETTLEMENT_ASSET_ID"
507
- }
508
- }
509
- }
510
- }
511
- ```
512
-
513
- Where to put it:
514
-
515
- - Single-account setup: put `mixpay` under `channels.mixin.mixpay`
516
- - Multi-account setup: put it under `channels.mixin.accounts.<accountId>.mixpay`
517
- - `mixpay` is account-scoped, so different Mixin bot accounts can use different MixPay settings
518
-
519
- Field reference:
520
-
521
- - `mixpay.enabled`: enable MixPay collect support for this Mixin account
522
- - `mixpay.apiBaseUrl`: optional custom MixPay API base URL; normally leave it empty and use the default official endpoint
523
- - `mixpay.payeeId`: the MixPay payee/merchant UUID that actually receives the funds; required when MixPay collect is enabled
524
- - `mixpay.defaultQuoteAssetId`: default quoted asset ID; when set, `mixin-collect` can omit `assetId`
525
- - `mixpay.defaultSettlementAssetId`: default settlement asset ID; controls which asset the order prefers to settle into
526
- - `mixpay.expireMinutes`: default expiration time for newly created collect orders
527
- - `mixpay.pollIntervalSec`: background polling interval for pending orders; shorter values detect paid orders faster but create more MixPay API traffic
528
- - `mixpay.allowedCreators`: optional sender UUID allowlist; when non-empty, only these users can create collect orders in chat
529
- - `mixpay.notifyOnPending`: whether to send a conversation update when MixPay reports the order as `pending`
530
- - `mixpay.notifyOnPaidLess`: whether to send a conversation update when MixPay reports an underpayment
531
-
532
- Practical guidance:
533
-
534
- - If you only want the smallest working setup, configure `enabled`, `payeeId`, `defaultQuoteAssetId`, and `defaultSettlementAssetId`
535
- - If you do not want everyone in an authorized chat to create collect orders, set `allowedCreators`
536
- - If you do not run a private MixPay gateway, leave `apiBaseUrl` unset
537
- - If you want fewer status messages in chat, keep `notifyOnPending: false`
538
-
539
- ## Explicit Reply Templates
540
-
541
- When you want deterministic Mixin output instead of heuristic auto-selection, have the agent reply with exactly one fenced template block.
542
-
543
- Text:
544
-
545
- ```text
546
- ```mixin-text
547
- Short plain reply.
548
- ```
549
- ```
550
-
551
- Post:
552
-
553
- ```text
554
- ```mixin-post
555
- # Release Notes
556
-
557
- - Item 1
558
- - Item 2
559
- ```
560
- ```
561
-
562
- Buttons:
563
-
564
- ```text
565
- ```mixin-buttons
566
- {
567
- "intro": "Choose an action",
568
- "buttons": [
569
- { "label": "Open Docs", "action": "https://docs.openclaw.ai" },
570
- { "label": "Open Mixin", "action": "https://developers.mixin.one" }
571
- ]
572
- }
573
- ```
574
- ```
575
-
576
- Card:
577
-
578
- ```text
579
- ```mixin-card
580
- {
581
- "title": "OpenClaw Docs",
582
- "description": "Open the official documentation site.",
583
- "action": "https://docs.openclaw.ai",
584
- "coverUrl": "https://example.com/cover.png",
585
- "shareable": true
586
- }
587
- ```
588
- ```
589
-
590
- File:
591
-
592
- ```text
593
- ```mixin-file
594
- {
595
- "filePath": "/absolute/path/to/report.pdf",
596
- "fileName": "report.pdf",
597
- "mimeType": "application/pdf"
598
- }
599
- ```
600
- ```
601
-
602
- Audio:
603
-
604
- ```text
605
- ```mixin-audio
606
- {
607
- "filePath": "/absolute/path/to/voice.ogg",
608
- "mimeType": "audio/ogg",
609
- "duration": 12,
610
- "waveForm": "AAMMQQ=="
611
- }
612
- ```
613
- ```
614
-
615
- Rules:
616
-
617
- - Explicit templates take priority over automatic detection.
618
- - Replies containing tables or fenced code blocks are sent as `mixin-post` by default.
619
- - `mixin-buttons` and `mixin-card` accept JSON only.
620
- - `mixin-file` and `mixin-audio` also accept JSON only.
621
- - `mixin-audio` requires `duration` in seconds. `waveForm` is optional.
622
- - `mixin-file` and `mixin-audio` require absolute local file paths on the machine where OpenClaw runs.
623
- - Invalid explicit `mixin-*` templates are no longer dropped silently; the plugin now sends a visible `Mixin template error: ...` message instead.
624
- - Button and card links must use `http://` or `https://`.
625
- - Mixin clients may require your target domains to be present in the bot app's `Resource Patterns` allowlist.
626
-
627
- ## Multi-Account Example
628
-
629
- ```json
630
- {
631
- "channels": {
632
- "mixin": {
633
- "accounts": {
634
- "bot1": {
635
- "name": "Customer Service Bot",
636
- "appId": "...",
637
- "sessionId": "...",
638
- "serverPublicKey": "...",
639
- "sessionPrivateKey": "...",
640
- "dmPolicy": "pairing",
641
- "allowFrom": ["..."]
642
- },
643
- "bot2": {
644
- "name": "Tech Support Bot",
645
- "appId": "...",
646
- "sessionId": "...",
647
- "serverPublicKey": "...",
648
- "sessionPrivateKey": "...",
649
- "proxy": {
650
- "enabled": true,
651
- "url": "http://127.0.0.1:7890"
652
- }
653
- }
654
- }
655
- }
656
- }
657
- }
658
- ```
659
-
660
- ## Troubleshooting
661
-
662
- | Problem | What to check |
663
- |---------|---------------|
664
- | Plugin not loaded | Run `openclaw plugins list` and `openclaw plugins info mixin` |
665
- | Channel not starting | Verify `channels.mixin` exists and credentials are complete |
666
- | Not receiving messages | Check pairing approval or `allowFrom`, trigger words, and Blaze connectivity |
667
- | Messages not sending | Check proxy reachability, outbox backlog, and `/mixin-outbox` output |
668
- | Repeated inbound pushes | Check Blaze connectivity and confirm ACK logs/behavior |
669
-
670
- ## Security Notes
671
-
672
- - Keep `sessionPrivateKey` private.
673
- - Use `dmPolicy: "pairing"` or a strict `allowFrom` list in production.
674
- - Outbox files contain pending message bodies, so do not expose the `data/` directory.
675
-
676
- ## Links
677
-
678
- - [OpenClaw Documentation](https://openclaw.ai)
679
- - [OpenClaw Plugins](https://docs.openclaw.ai/tools/plugin)
680
- - [OpenClaw Plugin CLI](https://docs.openclaw.ai/cli/plugins)
681
- - [OpenClaw Configuration](https://docs.openclaw.ai/gateway/configuration)
682
- - [OpenClaw Configuration Reference](https://docs.openclaw.ai/gateway/configuration-reference)
683
- - [Mixin Developers Dashboard](https://developers.mixin.one/dashboard)
684
- - [Mixin Bot API Documentation](https://developers.mixin.one/docs/bot-api)
389
+ - Group message: `@<identity_number> your question`
390
+ - Recommended example: `@7000103034 help me summarize this`
391
+ - Do not rely on quote-only or quote-plus-mention group replies as a stable trigger path.
392
+
393
+ ## Operations
394
+
395
+ Useful OpenClaw commands:
396
+
397
+ ```bash
398
+ openclaw plugins list
399
+ openclaw plugins info mixin
400
+ openclaw plugins update mixin
401
+ openclaw channels status --probe
402
+ openclaw status
403
+ ```
404
+
405
+ Plugin-specific command:
406
+
407
+ - Send `/mixin-outbox` to inspect the current pending queue size, next retry time, and latest error.
408
+ - Send `/mixin-outbox purge-invalid` to remove old `APP_CARD` / `APP_BUTTON_GROUP` entries that are stuck on permanent invalid-field errors.
409
+ - Send `/mixin-group-auth` in a group to create a pending group-authorization request.
410
+ - Approve a pending group-authorization request in the OpenClaw terminal with `openclaw pairing approve mixin <code>`.
411
+ - For non-default accounts, use `openclaw pairing approve --account <accountId> mixin <code>`.
412
+ - Send `/collect status <orderId>` to refresh and inspect a stored MixPay collect order.
413
+ - Send `/collect recent` or `/collect recent 10` to list recent MixPay collect orders for the current conversation.
414
+
415
+ Companion onboarding CLI:
416
+
417
+ - This repository also includes a companion CLI at `tools/mixin-plugin-onboard/README.md`.
418
+ - It is bundled into the same npm package, `@invago/mixin`.
419
+ - It currently provides `info`, `doctor`, `install`, and `update` commands for local OpenClaw + Mixin plugin maintenance.
420
+ - Local examples:
421
+ - `node --import jiti/register.js tools/mixin-plugin-onboard/src/index.ts info`
422
+ - `node --import jiti/register.js tools/mixin-plugin-onboard/src/index.ts doctor`
423
+ - Installed package examples:
424
+ - `npx -y @invago/mixin info`
425
+ - `npx -y @invago/mixin doctor`
426
+
427
+ ## Delivery and Retry Behavior
428
+
429
+ - Outbound messages are persisted to a local outbox before send attempts.
430
+ - Failed sends are retried automatically until they succeed.
431
+ - Pending messages survive plugin restarts.
432
+ - Inbound Blaze messages are acknowledged before dispatch so Mixin receives a read receipt as early as possible.
433
+
434
+ ## Media Support
435
+
436
+ Current media behavior is split into outbound and inbound support:
437
+
438
+ - OpenClaw native outbound media is enabled through the channel `sendMedia` path.
439
+ - OpenClaw native `sendPayload` now uses the same Mixin outbound planner as buffered agent replies, so text/post/buttons/card/file/audio selection is consistent.
440
+ - The plugin sends outbound audio as `PLAIN_AUDIO` when it can resolve the media as audio and detect duration.
441
+ - If audio duration cannot be detected, the plugin falls back to regular file attachment sending.
442
+ - Non-audio outbound media is sent as Mixin file attachments.
443
+ - If OpenClaw sends both caption text and media, the plugin sends the text first and then the file.
444
+ - Voice-bubble style outbound audio is currently intended for the explicit `mixin-audio` template path.
445
+ - Inbound `PLAIN_DATA` and `PLAIN_AUDIO` messages are downloaded, saved locally, and attached to the OpenClaw inbound context through `MediaPath` / `MediaType`.
446
+ - Group attachment messages are allowed through even when `requireMentionInGroup` is enabled, unless `mediaBypassMentionInGroup` is set to `false`.
447
+
448
+ Current limits:
449
+
450
+ - Outbound audio does not transcode automatically.
451
+ - `mixin-audio` still requires a prepared local file, explicit `duration`, and optional `waveForm`.
452
+ - OpenClaw native outbound audio depends on local `ffprobe` availability to detect duration.
453
+ - OpenClaw native `sendMedia` still does not generate `waveForm`, so explicit `mixin-audio` remains the most deterministic path for polished voice-message output.
454
+ - Whether the agent can summarize, transcribe, or reason over inbound files/audio depends on your OpenClaw media-understanding configuration.
455
+
456
+ Manual test guide:
457
+
458
+ - See [docs/media-testing.md](docs/media-testing.md) for ready-to-run prompts and expected results.
459
+
460
+ ## MixPay Collect
461
+
462
+ Mixin now supports MixPay collection through one-time payment orders.
463
+
464
+ Current capabilities:
465
+
466
+ - `mixin-collect` explicit reply template creates a MixPay collect order
467
+ - Collect orders are stored locally under the OpenClaw state directory
468
+ - Pending orders are polled in the background
469
+ - Success and terminal status changes are sent back to the original conversation
470
+ - `/collect status <orderId>` refreshes the order from MixPay before replying
471
+ - `assetId` in the template can be omitted when `mixpay.defaultQuoteAssetId` is configured
472
+
473
+ Template example:
474
+
475
+ ````text
476
+ ```mixin-collect
477
+ {
478
+ "amount": "1",
479
+ "assetId": "c6d0c728-2624-429b-8e0d-d9d19b6592fa",
480
+ "memo": "Order #1001"
481
+ }
482
+ ```
483
+ ````
484
+
485
+ Rules:
486
+
487
+ - `amount` is required; `assetId` is required unless `mixpay.defaultQuoteAssetId` is configured
488
+ - `settlementAssetId`, `memo`, `orderId`, and `expireMinutes` are optional
489
+ - Payment success is confirmed from MixPay server-side query results, not only from the client page
490
+ - `mixpay.allowedCreators` can restrict who is allowed to create collect orders
491
+
492
+ Where funds arrive:
493
+
494
+ - For MixPay `Mixin account`, funds settle into the linked Mixin Wallet
495
+ - For MixPay `Mixin Robot account`, funds settle into the linked Mixin Robot Wallet
496
+ - Other MixPay account types settle into their own linked wallet types
497
+
498
+ Recommended setup for this plugin:
499
+
500
+ - Use a MixPay `Mixin account` or `Mixin Robot account`
501
+ - Use that account's UUID as `mixpay.payeeId`
502
+ - Set both `mixpay.defaultQuoteAssetId` and `mixpay.defaultSettlementAssetId` if you want templates to stay short
503
+
504
+ How to get the required values:
505
+
506
+ - `mixpay.payeeId`: get the UUID from the [MixPay Dashboard](https://dashboard.mixpay.me) settings page, or use the MixPay helper bot described in the official getting-started guide
507
+ - `mixpay.defaultQuoteAssetId`: choose the asset ID you want to quote prices in
508
+ - `mixpay.defaultSettlementAssetId`: choose the asset ID you want funds to settle into
509
+
510
+ Minimal recommended config:
511
+
512
+ ```json
513
+ {
514
+ "channels": {
515
+ "mixin": {
516
+ "mixpay": {
517
+ "enabled": true,
518
+ "payeeId": "YOUR_MIXPAY_UUID",
519
+ "defaultQuoteAssetId": "YOUR_QUOTE_ASSET_ID",
520
+ "defaultSettlementAssetId": "YOUR_SETTLEMENT_ASSET_ID"
521
+ }
522
+ }
523
+ }
524
+ }
525
+ ```
526
+
527
+ Where to put it:
528
+
529
+ - Single-account setup: put `mixpay` under `channels.mixin.mixpay`
530
+ - Multi-account setup: put it under `channels.mixin.accounts.<accountId>.mixpay`
531
+ - `mixpay` is account-scoped, so different Mixin bot accounts can use different MixPay settings
532
+
533
+ Field reference:
534
+
535
+ - `mixpay.enabled`: enable MixPay collect support for this Mixin account
536
+ - `mixpay.apiBaseUrl`: optional custom MixPay API base URL; normally leave it empty and use the default official endpoint
537
+ - `mixpay.payeeId`: the MixPay payee/merchant UUID that actually receives the funds; required when MixPay collect is enabled
538
+ - `mixpay.defaultQuoteAssetId`: default quoted asset ID; when set, `mixin-collect` can omit `assetId`
539
+ - `mixpay.defaultSettlementAssetId`: default settlement asset ID; controls which asset the order prefers to settle into
540
+ - `mixpay.expireMinutes`: default expiration time for newly created collect orders
541
+ - `mixpay.pollIntervalSec`: background polling interval for pending orders; shorter values detect paid orders faster but create more MixPay API traffic
542
+ - `mixpay.allowedCreators`: optional sender UUID allowlist; when non-empty, only these users can create collect orders in chat
543
+ - `mixpay.notifyOnPending`: whether to send a conversation update when MixPay reports the order as `pending`
544
+ - `mixpay.notifyOnPaidLess`: whether to send a conversation update when MixPay reports an underpayment
545
+
546
+ Practical guidance:
547
+
548
+ - If you only want the smallest working setup, configure `enabled`, `payeeId`, `defaultQuoteAssetId`, and `defaultSettlementAssetId`
549
+ - If you do not want everyone in an authorized chat to create collect orders, set `allowedCreators`
550
+ - If you do not run a private MixPay gateway, leave `apiBaseUrl` unset
551
+ - If you want fewer status messages in chat, keep `notifyOnPending: false`
552
+
553
+ ## Explicit Reply Templates
554
+
555
+ When you want deterministic Mixin output instead of heuristic auto-selection, have the agent reply with exactly one fenced template block.
556
+
557
+ Text:
558
+
559
+ ```text
560
+ ```mixin-text
561
+ Short plain reply.
562
+ ```
563
+ ```
564
+
565
+ Post:
566
+
567
+ ```text
568
+ ```mixin-post
569
+ # Release Notes
570
+
571
+ - Item 1
572
+ - Item 2
573
+ ```
574
+ ```
575
+
576
+ Buttons:
577
+
578
+ ```text
579
+ ```mixin-buttons
580
+ {
581
+ "intro": "Choose an action",
582
+ "buttons": [
583
+ { "label": "Open Docs", "action": "https://docs.openclaw.ai" },
584
+ { "label": "Open Mixin", "action": "https://developers.mixin.one" }
585
+ ]
586
+ }
587
+ ```
588
+ ```
589
+
590
+ Card:
591
+
592
+ ```text
593
+ ```mixin-card
594
+ {
595
+ "title": "OpenClaw Docs",
596
+ "description": "Open the official documentation site.",
597
+ "action": "https://docs.openclaw.ai",
598
+ "coverUrl": "https://example.com/cover.png",
599
+ "shareable": true
600
+ }
601
+ ```
602
+ ```
603
+
604
+ File:
605
+
606
+ ```text
607
+ ```mixin-file
608
+ {
609
+ "filePath": "/absolute/path/to/report.pdf",
610
+ "fileName": "report.pdf",
611
+ "mimeType": "application/pdf"
612
+ }
613
+ ```
614
+ ```
615
+
616
+ Audio:
617
+
618
+ ```text
619
+ ```mixin-audio
620
+ {
621
+ "filePath": "/absolute/path/to/voice.ogg",
622
+ "mimeType": "audio/ogg",
623
+ "duration": 12,
624
+ "waveForm": "AAMMQQ=="
625
+ }
626
+ ```
627
+ ```
628
+
629
+ Rules:
630
+
631
+ - Explicit templates take priority over automatic detection.
632
+ - Replies containing tables or fenced code blocks are sent as `mixin-post` by default.
633
+ - `mixin-buttons` and `mixin-card` accept JSON only.
634
+ - `mixin-file` and `mixin-audio` also accept JSON only.
635
+ - `mixin-audio` requires `duration` in seconds. `waveForm` is optional.
636
+ - `mixin-file` and `mixin-audio` require absolute local file paths on the machine where OpenClaw runs.
637
+ - Invalid explicit `mixin-*` templates are no longer dropped silently; the plugin now sends a visible `Mixin template error: ...` message instead.
638
+ - Button and card links must use `http://` or `https://`.
639
+ - Mixin clients may require your target domains to be present in the bot app's `Resource Patterns` allowlist.
640
+
641
+ ## Multi-Account Example
642
+
643
+ ```json
644
+ {
645
+ "channels": {
646
+ "mixin": {
647
+ "accounts": {
648
+ "bot1": {
649
+ "name": "Customer Service Bot",
650
+ "appId": "...",
651
+ "sessionId": "...",
652
+ "serverPublicKey": "...",
653
+ "sessionPrivateKey": "...",
654
+ "dmPolicy": "pairing",
655
+ "allowFrom": ["..."]
656
+ },
657
+ "bot2": {
658
+ "name": "Tech Support Bot",
659
+ "appId": "...",
660
+ "sessionId": "...",
661
+ "serverPublicKey": "...",
662
+ "sessionPrivateKey": "...",
663
+ "proxy": {
664
+ "enabled": true,
665
+ "url": "http://127.0.0.1:7890"
666
+ }
667
+ }
668
+ }
669
+ }
670
+ }
671
+ }
672
+ ```
673
+
674
+ ## Troubleshooting
675
+
676
+ | Problem | What to check |
677
+ |---------|---------------|
678
+ | Plugin not loaded | Run `openclaw plugins list` and `openclaw plugins info mixin` |
679
+ | Channel not starting | Verify `channels.mixin` exists and credentials are complete |
680
+ | Not receiving messages | Check pairing approval or `allowFrom`, trigger words, and Blaze connectivity |
681
+ | Messages not sending | Check proxy reachability, outbox backlog, and `/mixin-outbox` output |
682
+ | Repeated inbound pushes | Check Blaze connectivity and confirm ACK logs/behavior |
683
+
684
+ ## Security Notes
685
+
686
+ - Keep `sessionPrivateKey` private.
687
+ - Use `dmPolicy: "pairing"` or a strict `allowFrom` list in production.
688
+ - Outbox files contain pending message bodies, so do not expose the `data/` directory.
689
+
690
+ ## Links
691
+
692
+ - [OpenClaw Documentation](https://openclaw.ai)
693
+ - [OpenClaw Plugins](https://docs.openclaw.ai/tools/plugin)
694
+ - [OpenClaw Plugin CLI](https://docs.openclaw.ai/cli/plugins)
695
+ - [OpenClaw Configuration](https://docs.openclaw.ai/gateway/configuration)
696
+ - [OpenClaw Configuration Reference](https://docs.openclaw.ai/gateway/configuration-reference)
697
+ - [Mixin Developers Dashboard](https://developers.mixin.one/dashboard)
698
+ - [Mixin Bot API Documentation](https://developers.mixin.one/docs/bot-api)
699
+
700
+ ## OpenClaw 3.23 notes
701
+
702
+ - The plugin manifest is `openclaw.plugin.json`.
703
+ - Channel config stays under `channels.mixin` and `channels.mixin.accounts.<accountId>`.
704
+ - Host-side diagnostics are available as `/setup`, `/setup single`, `/setup multi`, `/mixin-status`, `/mixin-accounts`, and `/mixin-help`.
705
+ - For local development, prefer `openclaw plugins install -l .`.
706
+
707
+ - Use `/setup` for the guided setup flow.