@qihoo/tuitui-openclaw-channel 1.0.30 → 1.0.31

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.
@@ -43,7 +43,7 @@
43
43
  "enum": ["channel", "thread"]
44
44
  },
45
45
  "emojiReaction": { "type": "boolean", "default": true },
46
- "monitorEnabled": { "type": "boolean", "default": false },
46
+ "monitorEnabled": { "type": "boolean", "default": true },
47
47
  "accounts": {
48
48
  "type": "object",
49
49
  "additionalProperties": {
@@ -79,7 +79,7 @@
79
79
  "enum": ["channel", "thread"]
80
80
  },
81
81
  "emojiReaction": { "type": "boolean", "default": true },
82
- "monitorEnabled": { "type": "boolean", "default": false }
82
+ "monitorEnabled": { "type": "boolean", "default": true }
83
83
  }
84
84
  }
85
85
  }
@@ -131,7 +131,7 @@
131
131
  "advanced": true
132
132
  },
133
133
  "monitorEnabled": {
134
- "help": "是否开启agent事件信息上报(默认 false)。",
134
+ "help": "是否开启频道agent事件上报,用于频道展示agent执行中间步骤(默认 true)",
135
135
  "order": 17,
136
136
  "advanced": true
137
137
  },
@@ -185,7 +185,7 @@
185
185
  "advanced": true
186
186
  },
187
187
  "accounts.*.monitorEnabled": {
188
- "help": "是否开启agent事件信息上报(默认 false)。",
188
+ "help": "是否开启频道agent事件上报,用于频道展示agent执行中间步骤(默认 true)。",
189
189
  "order": 311,
190
190
  "advanced": true
191
191
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qihoo/tuitui-openclaw-channel",
3
- "version": "1.0.30",
3
+ "version": "1.0.31",
4
4
  "maintainers": [
5
5
  {
6
6
  "name": "huzunjie",
package/src/accounts.ts CHANGED
@@ -7,7 +7,7 @@ export const dmPolicyDefault = 'pairing';
7
7
  export const groupPolicyDefault = 'allowlist';
8
8
  export const requireMentionDefault = true;
9
9
  export const emojiReactionDefault = true;
10
- export const monitorEnabledDefault = false;
10
+ export const monitorEnabledDefault = true;
11
11
 
12
12
  const mergeArrs = (arr1: any, arr2: any) => {
13
13
  return [...new Set([...(arr1 || []), ...arr2 || []])];
@@ -20,7 +20,7 @@ export const getAccountInfo = (acct?: any) => ({
20
20
  dmPolicy: acct?.dmPolicy || dmPolicyDefault,
21
21
  allowFrom: parseAllowFroms(acct?.allowFrom || []),
22
22
  // 群组策略与白名单、群组级覆盖
23
- groupPolicy: acct?.groupPolic || groupPolicyDefault,
23
+ groupPolicy: acct?.groupPolicy || groupPolicyDefault,
24
24
  groupAllowFrom: parseAllowFroms(acct?.groupAllowFrom || []),
25
25
  requireMention: isEnabled(acct?.requireMention ?? requireMentionDefault),
26
26
  emojiReaction: isEnabled(acct?.emojiReaction ?? emojiReactionDefault),
package/src/channel.ts CHANGED
@@ -6,6 +6,7 @@
6
6
  import { DEFAULT_ACCOUNT_ID } from 'openclaw/plugin-sdk/account-id';
7
7
  import { setAccountEnabledInConfigSection, deleteAccountFromConfigSection, OpenClawPluginApi } from 'openclaw/plugin-sdk/core';
8
8
  import { CHANNEL_ID, CHANNEL_NAME } from "./const";
9
+ import { version } from "../package.json"
9
10
  import { handleInboundMessage } from './inbound';
10
11
  import { guessChatTypeV2 } from "./chat_record"
11
12
  import {
@@ -55,7 +56,7 @@ function createTuiTuiChannelPlugin(apiRuntime: any) {
55
56
  id: CHANNEL_ID,
56
57
  label: CHANNEL_NAME,
57
58
  selectionLabel: CHANNEL_NAME,
58
- detailLabel: CHANNEL_NAME,
59
+ detailLabel: CHANNEL_NAME + " " + version,
59
60
  docsPath: `/channels/${CHANNEL_ID}`,
60
61
  blurb: `Connect to ${CHANNEL_NAME} bot via WebSocket`,
61
62
  order: 100,
package/src/monitor.ts CHANGED
@@ -2,10 +2,10 @@
2
2
  * TuiTui Monitor — 将 OpenClaw Hook 事件批量上报到监控接口。
3
3
  *
4
4
  * 每个账户(appId/appSecret)维护独立队列,满足以下任一条件时批量上报:
5
- * - 队列积累达 BATCH_MAX_SIZE 条(默认 100)
6
- * - 距上次 flush 超过 BATCH_INTERVAL_MS(默认 15s)
5
+ * - 队列积累达 BATCH_MAX_SIZE
6
+ * - 距上次 flush 超过 BATCH_INTERVAL_MS
7
7
  *
8
- * 需在配置中将 monitorEnabled 设为 true 才会上报(默认 false)。
8
+ * 需在配置中将 monitorEnabled 设为 true 才会上报
9
9
  *
10
10
  * 配置示例(openclaw.json):
11
11
  * channels.tuitui.monitorEnabled: true
@@ -14,6 +14,7 @@ import type { OpenClawPluginApi } from 'openclaw/plugin-sdk';
14
14
  import { DEFAULT_ACCOUNT_ID } from 'openclaw/plugin-sdk/account-id';
15
15
  import { CHANNEL_ID } from './const';
16
16
  import { tuituiRobotApi } from './robot_api';
17
+ import {parseChannelIdBySessionKey} from "./chat_base"
17
18
 
18
19
  // 批量上报参数
19
20
  const BATCH_MAX_SIZE = 100; // 达到此条数立即 flush
@@ -272,19 +273,27 @@ function report(
272
273
  ctx: unknown,
273
274
  data: unknown,
274
275
  ): void {
275
- let agentId: string | null;
276
+ let sessionKey = "";
276
277
 
277
278
  // subagent hook 的 ctx 有 childSessionKey,普通 hook 有 sessionKey
278
279
  if (eventType === 'subagent_spawned' || eventType === 'subagent_ended') {
279
- agentId = extractAgentId((ctx as any)?.childSessionKey);
280
+ sessionKey = (ctx as any)?.childSessionKey
280
281
  } else {
281
- agentId = extractAgentId((ctx as any)?.sessionKey);
282
+ sessionKey = (ctx as any)?.sessionKey
282
283
  }
283
284
 
285
+ const agentId = extractAgentId(sessionKey);
284
286
  if (!agentId) return;
285
287
 
288
+ // 仅频道支持;私聊群聊不启用
289
+ const channelId = parseChannelIdBySessionKey(sessionKey);
290
+ if(!channelId) return;
291
+
292
+ const reportedAccountIds = new Map<string, boolean>();
286
293
  for (const target of targets) {
287
294
  if (target.agentId !== agentId) continue;
295
+ if (reportedAccountIds.has(target.accountId)) continue;
296
+ reportedAccountIds.set(target.accountId, true);
288
297
 
289
298
  const payload: MonitorPayload = {
290
299
  event: eventType,
package/src/tools.ts CHANGED
@@ -4,7 +4,7 @@ import { resolveAccount } from "./accounts"
4
4
  import { Type } from "@sinclair/typebox";
5
5
 
6
6
  import {sendTextMsg, get_announcement} from "./outbound"
7
- import {CHAT_TYPE_DIRECT,CHAT_TYPE_GROUP,CHAT_TYPE_CHANNEL, teamsBuildChatId} from "./chat_base"
7
+ import {CHAT_TYPE_DIRECT,CHAT_TYPE_GROUP,CHAT_TYPE_CHANNEL,guessChatType, teamsBuildChatId} from "./chat_base"
8
8
  import {getChatRecord, getChannelInfoById} from "./chat_record"
9
9
  import {file_space_list, file_space_add} from "./filespace"
10
10
 
@@ -74,9 +74,17 @@ const tuitui_send_channel_post_factory = (ctx: OpenClawPluginToolContext) => {
74
74
  return tool_errmsg(`invalid tuitui account ${ctx.agentAccountId}`);
75
75
  }
76
76
 
77
- const channel_info = await getChannelInfoById(account, params?.channel_id);
78
- const team_id = channel_info?.team_id;
79
- const chatId = teamsBuildChatId(team_id, params?.channel_id, params?.parent_id);
77
+ let chatId = "";
78
+ const guessType = guessChatType(params?.channel_id);
79
+ if(guessType == CHAT_TYPE_CHANNEL) {
80
+ // 解决badcase: 发个帖子,内容是个笑话
81
+ // 如果用户没明确说频道ID,大模型在频道调用发帖tool会传sessionKey过来
82
+ chatId = params?.channel_id;
83
+ } else {
84
+ const channel_info = await getChannelInfoById(account, params?.channel_id);
85
+ const team_id = channel_info?.team_id;
86
+ chatId = teamsBuildChatId(team_id, params?.channel_id, params?.parent_id);
87
+ }
80
88
 
81
89
  const result: Promise<any> = sendTextMsg(account, chatId, CHAT_TYPE_CHANNEL, params?.markdown);
82
90
  return result;
package/src/websocket.ts CHANGED
@@ -73,6 +73,14 @@ export default function createWebSocket({ account, log, abortSignal, onConnected
73
73
  if (firsEvtId) wsEvtIds.delete(firsEvtId);
74
74
  }
75
75
 
76
+ // 收到包含心跳在内的任意消息,则重置心跳超时计时器,如果 300 秒内没有收到任何消息(包括心跳),则认为连接已失效
77
+ _clearTimeoutTimer();
78
+ _timeoutId = setTimeout(() => {
79
+ log?.warn?.(`[${CHANNEL_ID}] AccountId: ${accountId}, WebSocket[${wsId}] Heartbeat Timeout`);
80
+ _closeWS();
81
+ _restartWS(1e3);
82
+ }, 3e5); // 300秒心跳超时
83
+
76
84
  const wsEvent = json?.body?.event;
77
85
  if (wsEvent === 'keepalive') return;
78
86