@invago/mixin 1.0.16 → 1.0.18

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
@@ -10,11 +10,12 @@ Connect [Mixin Messenger](https://mixin.one/messenger) to [OpenClaw](https://ope
10
10
 
11
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
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.
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
+ - Per confirmed Mixin platform behavior, user messages in group chats are delivered to the bot only when the bot is explicitly mentioned.
18
19
 
19
20
  ## Recommended Install
20
21
 
@@ -325,11 +326,11 @@ Mixin now supports formal group access controls in addition to direct-message `d
325
326
 
326
327
  Important delivery boundary:
327
328
 
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`.
329
+ - Per confirmed Mixin platform behavior, group messages from users are delivered to the bot only when the bot is explicitly mentioned.
330
+ - Use `@<identity_number> your message`, for example `@7000103034 hello`.
330
331
  - `requireMentionInGroup: false` only disables this plugin's own post-delivery filtering.
331
- - It does not guarantee that Mixin will deliver every non-mention group message to the bot.
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.
332
+ - It does not cause Mixin to forward non-mention user messages in groups to the bot.
333
+ - If a non-mention user message produces no read receipt and no inbound log, the message was not delivered to the plugin by Mixin in the first place.
333
334
  - 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
 
335
336
  Example:
package/README.zh-CN.md CHANGED
@@ -10,11 +10,12 @@
10
10
 
11
11
  MixinClaw 是一个 OpenClaw 频道插件。它运行在 OpenClaw Gateway 同一进程中,使用 Mixin Blaze WebSocket 接收入站消息,并通过 Mixin HTTP API 发送出站消息。
12
12
 
13
- 重要说明:
14
-
15
- - 插件需要安装在 OpenClaw Gateway 所在的机器上。
16
- - OpenClaw 配置文件使用 JSON5,支持注释和尾逗号。
17
- - 这里配置的代理只作用于这个插件,不影响其他插件。
13
+ 重要说明:
14
+
15
+ - 插件需要安装在 OpenClaw Gateway 所在的机器上。
16
+ - OpenClaw 配置文件使用 JSON5,支持注释和尾逗号。
17
+ - 这里配置的代理只作用于这个插件,不影响其他插件。
18
+ - 根据 Mixin 官方确认,群内用户消息只有在显式 `@` 机器人的情况下才会投递给 bot。
18
19
 
19
20
  ## 推荐安装方式
20
21
 
@@ -317,12 +318,12 @@ OpenClaw 支持通过 `bindings[].match.accountId` 把不同的频道账号直
317
318
 
318
319
  关于群消息投递边界:
319
320
 
320
- - 目前从实际联调看,Mixin 群里稳定可用的触发方式仍然是显式 `@bot`。
321
- - 最稳的写法是 `@<identity_number> + 文本`,例如 `@7000103034 你好`。
321
+ - 根据 Mixin 官方确认,群内用户消息只有在显式 `@bot` 时才会投递给机器人。
322
+ - 推荐写法是 `@<identity_number> + 文本`,例如 `@7000103034 你好`。
322
323
  - `requireMentionInGroup: false` 只表示关闭插件自身的群消息二次过滤。
323
- - 它不能保证 Mixin 平台一定把所有未 `@` 的群消息投递给机器人。
324
- - 如果群里未 `@` 的消息既没有已读,也没有任何入站日志,通常说明这条消息根本没有被 Mixin 投递到插件。
325
- - 目前群内“引用回复”不应被当成稳定触发方式,因为 Mixin 不一定会把这类事件稳定投递给 bot。
324
+ - 它不会让 Mixin 把未 `@` 的群内用户消息投递给 bot。
325
+ - 如果一条未 `@` 的群内用户消息既没有已读,也没有任何入站日志,那不是插件过滤了它,而是 Mixin 根本没有把这条消息送到插件。
326
+ - 目前群内“引用回复”也不应被当成稳定触发方式,因为 Mixin 不一定会把这类事件稳定投递给 bot。
326
327
 
327
328
  示例:
328
329
 
@@ -2,7 +2,7 @@
2
2
  "id": "mixin",
3
3
  "name": "Mixin Messenger Channel",
4
4
  "description": "Mixin Messenger channel via Blaze WebSocket",
5
- "version": "1.0.16",
5
+ "version": "1.0.18",
6
6
  "channels": [
7
7
  "mixin"
8
8
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@invago/mixin",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "description": "Mixin Messenger channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "index.ts",
package/src/channel.ts CHANGED
@@ -10,7 +10,7 @@ import { buildClient, sleep } from "./shared.js";
10
10
  import { MixinConfigSchema } from "./config-schema.js";
11
11
  import { describeAccount, isConfigured, listAccountIds, resolveAccount, resolveDefaultAccountId, resolveMediaMaxMb } from "./config.js";
12
12
  import type { MixinAccountConfig } from "./config-schema.js";
13
- import { handleMixinMessage, type MixinInboundMessage } from "./inbound-handler.js";
13
+ import { handleMixinMessage, handleMixinSystemConversation, type MixinInboundMessage } from "./inbound-handler.js";
14
14
  import { getMixpayStatusSnapshot, startMixpayWorker } from "./mixpay-worker.js";
15
15
  import { mixinOnboardingAdapter } from "./onboarding.js";
16
16
  import { buildMixinOutboundPlanFromReplyPayload, executeMixinOutboundPlan } from "./outbound-plan.js";
@@ -459,8 +459,50 @@ export const mixinPlugin = {
459
459
  onSenderReady: (sender) => {
460
460
  setMixinBlazeSender(accountId, sender);
461
461
  },
462
- handler: {
463
- onMessage: async (rawMsg: any) => {
462
+ handler: {
463
+ onConversation: async (rawMsg: any) => {
464
+ if (stopped) {
465
+ return;
466
+ }
467
+ if (!rawMsg || !rawMsg.message_id) {
468
+ return;
469
+ }
470
+
471
+ const isDirect = await resolveIsDirectMessage({
472
+ config,
473
+ conversationId: rawMsg.conversation_id,
474
+ userId: rawMsg.user_id,
475
+ log,
476
+ });
477
+ const quoteMessageId = extractQuoteMessageId(rawMsg);
478
+ const rawCategory = typeof rawMsg.category === "string" ? rawMsg.category : "SYSTEM_CONVERSATION";
479
+ const rawData = typeof rawMsg.data_base64 === "string"
480
+ ? rawMsg.data_base64
481
+ : typeof rawMsg.data === "string"
482
+ ? rawMsg.data
483
+ : "";
484
+
485
+ log.info(
486
+ `[mixin] blaze inbound: messageId=${rawMsg.message_id}, conversationId=${rawMsg.conversation_id ?? ""}, userId=${rawMsg.user_id ?? ""}, category=${rawCategory}, isDirect=${isDirect}, quoteMessageId=${quoteMessageId ?? "none"}, dataLength=${rawData.length}`,
487
+ );
488
+
489
+ const msg: MixinInboundMessage = {
490
+ conversationId: rawMsg.conversation_id ?? "",
491
+ userId: rawMsg.user_id ?? "",
492
+ messageId: rawMsg.message_id,
493
+ category: rawCategory,
494
+ data: rawData,
495
+ createdAt: rawMsg.created_at ?? new Date().toISOString(),
496
+ quoteMessageId,
497
+ };
498
+
499
+ try {
500
+ await handleMixinSystemConversation({ cfg, accountId, msg, log });
501
+ } catch (err) {
502
+ log.error(`error handling system conversation ${msg.messageId}`, err);
503
+ }
504
+ },
505
+ onMessage: async (rawMsg: any) => {
464
506
  if (stopped) {
465
507
  return;
466
508
  }
@@ -471,24 +513,34 @@ export const mixinPlugin = {
471
513
  return;
472
514
  }
473
515
 
474
- const isDirect = await resolveIsDirectMessage({
475
- config,
476
- conversationId: rawMsg.conversation_id,
477
- userId: rawMsg.user_id,
478
- log,
479
- });
480
- log.info(
481
- `[mixin] inbound route context: messageId=${rawMsg.message_id}, conversationId=${rawMsg.conversation_id ?? ""}, userId=${rawMsg.user_id}, isDirect=${isDirect}`,
482
- );
483
-
516
+ const isDirect = await resolveIsDirectMessage({
517
+ config,
518
+ conversationId: rawMsg.conversation_id,
519
+ userId: rawMsg.user_id,
520
+ log,
521
+ });
522
+ const quoteMessageId = extractQuoteMessageId(rawMsg);
523
+ const rawCategory = typeof rawMsg.category === "string" ? rawMsg.category : "PLAIN_TEXT";
524
+ const rawData = typeof rawMsg.data_base64 === "string"
525
+ ? rawMsg.data_base64
526
+ : typeof rawMsg.data === "string"
527
+ ? rawMsg.data
528
+ : "";
529
+ log.info(
530
+ `[mixin] blaze inbound: messageId=${rawMsg.message_id}, conversationId=${rawMsg.conversation_id ?? ""}, userId=${rawMsg.user_id}, category=${rawCategory}, isDirect=${isDirect}, quoteMessageId=${quoteMessageId ?? "none"}, dataLength=${rawData.length}`,
531
+ );
532
+ log.info(
533
+ `[mixin] inbound route context: messageId=${rawMsg.message_id}, conversationId=${rawMsg.conversation_id ?? ""}, userId=${rawMsg.user_id}, isDirect=${isDirect}`,
534
+ );
535
+
484
536
  const msg: MixinInboundMessage = {
485
537
  conversationId: rawMsg.conversation_id ?? "",
486
538
  userId: rawMsg.user_id,
487
539
  messageId: rawMsg.message_id,
488
- category: rawMsg.category ?? "PLAIN_TEXT",
489
- data: rawMsg.data_base64 ?? rawMsg.data ?? "",
540
+ category: rawCategory,
541
+ data: rawData,
490
542
  createdAt: rawMsg.created_at ?? new Date().toISOString(),
491
- quoteMessageId: extractQuoteMessageId(rawMsg),
543
+ quoteMessageId,
492
544
  };
493
545
 
494
546
  try {