@henryxiaoyang/wechat-access-unqclawed 1.0.0 → 1.0.2

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
@@ -8,7 +8,13 @@ OpenClaw 微信通路插件 — 通过 WeChat OAuth 扫码登录获取 token,
8
8
  openclaw plugins install @henryxiaoyang/wechat-access-unqclawed
9
9
  ```
10
10
 
11
- 重启 Gateway 后生效。
11
+ 安装后启用渠道:
12
+
13
+ ```bash
14
+ openclaw config set channels.wechat-access-unqclawed.enabled true
15
+ ```
16
+
17
+ 重启 Gateway,首次启动会在终端显示微信扫码登录二维码。
12
18
 
13
19
  ## 功能
14
20
 
@@ -17,15 +23,18 @@ openclaw plugins install @henryxiaoyang/wechat-access-unqclawed
17
23
  - AGP 协议 WebSocket 双向通信(流式文本、工具调用)
18
24
  - 邀请码验证(可配置跳过)
19
25
  - 支持生产/测试环境切换
26
+ - `/wechat-login` 命令手动触发扫码登录
27
+ - `/wechat-logout` 命令清除已保存的登录态
20
28
 
21
29
  ## 配置
22
30
 
23
- 在 OpenClaw 配置文件的 `channels.wechat-access` 下:
31
+ 在 OpenClaw 配置文件的 `channels.wechat-access-unqclawed` 下:
24
32
 
25
33
  ```json
26
34
  {
27
35
  "channels": {
28
- "wechat-access": {
36
+ "wechat-access-unqclawed": {
37
+ "enabled": true,
29
38
  "token": "",
30
39
  "wsUrl": "",
31
40
  "bypassInvite": false,
@@ -37,6 +46,7 @@ openclaw plugins install @henryxiaoyang/wechat-access-unqclawed
37
46
 
38
47
  | 字段 | 类型 | 说明 |
39
48
  |------|------|------|
49
+ | `enabled` | boolean | 启用渠道(必须设为 `true`) |
40
50
  | `token` | string | 手动指定 channel token(留空则走扫码登录) |
41
51
  | `wsUrl` | string | WebSocket 网关地址(留空使用环境默认值) |
42
52
  | `bypassInvite` | boolean | 跳过邀请码验证 |
@@ -82,7 +82,7 @@ export const buildMessageContext = (message: FuwuhaoMessage): MessageContext =>
82
82
  // 根据频道、账号、对话类型等信息,决定使用哪个 Agent 处理消息
83
83
  const frameworkRoute = runtime.channel.routing.resolveAgentRoute({
84
84
  cfg, // 全局配置
85
- channel: "wechat-access", // 频道标识
85
+ channel: "wechat-access-unqclawed", // 频道标识
86
86
  accountId: "default", // 账号 ID(支持多账号场景)
87
87
  peer: {
88
88
  kind: "dm", // 对话类型:dm=私聊,group=群聊
@@ -132,7 +132,7 @@ export const buildMessageContext = (message: FuwuhaoMessage): MessageContext =>
132
132
  // runtime.channel.reply.formatInboundEnvelope 将原始消息格式化为标准格式
133
133
  // 添加时间戳、发送者信息、格式化选项等
134
134
  const body = runtime.channel.reply.formatInboundEnvelope({
135
- channel: "wechat-access", // 频道标识
135
+ channel: "wechat-access-unqclawed", // 频道标识
136
136
  from: userId, // 发送者 ID
137
137
  timestamp, // 消息时间戳
138
138
  body: content, // 消息内容
@@ -161,11 +161,11 @@ export const buildMessageContext = (message: FuwuhaoMessage): MessageContext =>
161
161
  ChatType: "direct" as const, // 对话类型
162
162
  ChannelSource: WECHAT_CHANNEL_LABELS.serviceAccount, // 渠道来源标识(用于 UI 侧区分消息来源)
163
163
  SenderId: userId, // 发送者 ID
164
- Provider: "wechat-access", // 提供商标识
165
- Surface: "wechat-access", // 界面标识
164
+ Provider: "wechat-access-unqclawed", // 提供商标识
165
+ Surface: "wechat-access-unqclawed", // 界面标识
166
166
  MessageSid: messageId, // 消息唯一标识
167
167
  Timestamp: timestamp, // 时间戳
168
- OriginatingChannel: "wechat-access" as const, // 原始频道
168
+ OriginatingChannel: "wechat-access-unqclawed" as const, // 原始频道
169
169
  OriginatingTo: `wechat-access:${userId}`, // 原始接收者
170
170
  });
171
171
  // ctx 包含了 Agent 处理消息所需的所有信息
@@ -103,7 +103,7 @@ export const handleMessage = async (message: FuwuhaoMessage): Promise<string | n
103
103
  // runtime.channel.activity.record 记录频道的活动统计
104
104
  // 用于监控、分析、计费等场景
105
105
  runtime.channel.activity.record({
106
- channel: "wechat-access", // 频道标识
106
+ channel: "wechat-access-unqclawed", // 频道标识
107
107
  accountId: "default", // 账号 ID
108
108
  direction: "inbound", // 方向:inbound=入站(用户发送),outbound=出站(Bot 回复)
109
109
  });
@@ -196,7 +196,7 @@ export const handleMessage = async (message: FuwuhaoMessage): Promise<string | n
196
196
 
197
197
  // 记录出站活动统计(Bot 回复)
198
198
  runtime.channel.activity.record({
199
- channel: "wechat-access",
199
+ channel: "wechat-access-unqclawed",
200
200
  accountId: "default",
201
201
  direction: "outbound", // 出站:Bot 发送给用户
202
202
  });
@@ -322,7 +322,7 @@ export const handleMessageStream = async (
322
322
  // 4. 记录频道活动统计
323
323
  // ============================================
324
324
  runtime.channel.activity.record({
325
- channel: "wechat-access",
325
+ channel: "wechat-access-unqclawed",
326
326
  accountId: "default",
327
327
  direction: "inbound",
328
328
  });
@@ -519,7 +519,7 @@ const unsubscribeAgentEvents = onAgentEvent((evt: AgentEventPayload) => {
519
519
 
520
520
  // 记录出站活动
521
521
  runtime.channel.activity.record({
522
- channel: "wechat-access",
522
+ channel: "wechat-access-unqclawed",
523
523
  accountId: "default",
524
524
  direction: "outbound",
525
525
  });
package/index.ts CHANGED
@@ -3,7 +3,7 @@ import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
3
3
  import { WechatAccessWebSocketClient, handlePrompt, handleCancel } from "./websocket/index.js";
4
4
  // import { handleSimpleWecomWebhook } from "./http/webhook.js";
5
5
  import { setWecomRuntime } from "./common/runtime.js";
6
- import { performLogin, loadState, getDeviceGuid, getEnvironment } from "./auth/index.js";
6
+ import { performLogin, loadState, clearState, getDeviceGuid, getEnvironment } from "./auth/index.js";
7
7
 
8
8
  // 类型定义
9
9
  type NormalizedChatType = "direct" | "group" | "channel";
@@ -13,14 +13,14 @@ const wsClients = new Map<string, WechatAccessWebSocketClient>();
13
13
 
14
14
  // 渠道元数据
15
15
  const meta = {
16
- id: "wechat-access",
16
+ id: "wechat-access-unqclawed",
17
17
  label: "腾讯通路",
18
18
  /** 选择时的显示文本 */
19
19
  selectionLabel: "腾讯通路",
20
20
  detailLabel: "腾讯通路",
21
21
  /** 文档路径 */
22
22
  docsPath: "/channels/wechat-access",
23
- docsLabel: "wechat-access",
23
+ docsLabel: "wechat-access-unqclawed",
24
24
  /** 简介 */
25
25
  blurb: "通用通路",
26
26
  /** 图标 */
@@ -31,7 +31,7 @@ const meta = {
31
31
 
32
32
  // 渠道插件
33
33
  const tencentAccessPlugin = {
34
- id: "wechat-access",
34
+ id: "wechat-access-unqclawed",
35
35
  meta,
36
36
 
37
37
  // 能力声明
@@ -46,13 +46,13 @@ const tencentAccessPlugin = {
46
46
 
47
47
  // 热重载:token 或 wsUrl 变更时触发 gateway 重启
48
48
  reload: {
49
- configPrefixes: ["channels.wechat-access.token", "channels.wechat-access.wsUrl"],
49
+ configPrefixes: ["channels.wechat-access-unqclawed.token", "channels.wechat-access-unqclawed.wsUrl"],
50
50
  },
51
51
 
52
52
  // 配置适配器(必需)
53
53
  config: {
54
54
  listAccountIds: (cfg: any) => {
55
- const accounts = cfg.channels?.["wechat-access"]?.accounts;
55
+ const accounts = cfg.channels?.["wechat-access-unqclawed"]?.accounts;
56
56
  if (accounts && typeof accounts === "object") {
57
57
  return Object.keys(accounts);
58
58
  }
@@ -60,7 +60,7 @@ const tencentAccessPlugin = {
60
60
  return ["default"];
61
61
  },
62
62
  resolveAccount: (cfg: any, accountId: string) => {
63
- const accounts = cfg.channels?.["wechat-access"]?.accounts;
63
+ const accounts = cfg.channels?.["wechat-access-unqclawed"]?.accounts;
64
64
  const account = accounts?.[accountId ?? "default"];
65
65
  return account ?? { accountId: accountId ?? "default" };
66
66
  },
@@ -86,7 +86,7 @@ const tencentAccessPlugin = {
86
86
  startAccount: async (ctx: any) => {
87
87
  const { cfg, accountId, abortSignal, log } = ctx;
88
88
 
89
- const tencentAccessConfig = cfg?.channels?.["wechat-access"];
89
+ const tencentAccessConfig = cfg?.channels?.["wechat-access-unqclawed"];
90
90
  let token = tencentAccessConfig?.token ? String(tencentAccessConfig.token) : "";
91
91
  const configWsUrl = tencentAccessConfig?.wsUrl ? String(tencentAccessConfig.wsUrl) : "";
92
92
  const bypassInvite = tencentAccessConfig?.bypassInvite === true;
@@ -211,7 +211,7 @@ const tencentAccessPlugin = {
211
211
  };
212
212
 
213
213
  const index = {
214
- id: "wechat-access",
214
+ id: "wechat-access-unqclawed",
215
215
  name: "通用通路插件",
216
216
  description: "腾讯通用通路插件",
217
217
  configSchema: emptyPluginConfigSchema(),
@@ -226,8 +226,50 @@ const index = {
226
226
  // 2. 注册渠道插件
227
227
  api.registerChannel({ plugin: tencentAccessPlugin as any });
228
228
 
229
- // 3. 注册 HTTP 处理器(如需要)
230
- // api.registerHttpHandler(handleSimpleWecomWebhook);
229
+ // 3. 注册 /wechat-login 命令(手动触发扫码登录)
230
+ api.registerCommand?.({
231
+ name: "wechat-login",
232
+ description: "手动执行微信扫码登录,获取 channel token",
233
+ handler: async ({ config }) => {
234
+ const channelCfg = config?.channels?.["wechat-access-unqclawed"];
235
+ const bypassInvite = channelCfg?.bypassInvite === true;
236
+ const authStatePath = channelCfg?.authStatePath
237
+ ? String(channelCfg.authStatePath)
238
+ : undefined;
239
+ const envName = channelCfg?.environment
240
+ ? String(channelCfg.environment)
241
+ : "production";
242
+
243
+ const env = getEnvironment(envName);
244
+ const guid = getDeviceGuid();
245
+
246
+ try {
247
+ const credentials = await performLogin({
248
+ guid,
249
+ env,
250
+ bypassInvite,
251
+ authStatePath,
252
+ });
253
+ return { text: `登录成功! token: ${credentials.channelToken.substring(0, 6)}... (已保存,重启 Gateway 生效)` };
254
+ } catch (err) {
255
+ return { text: `登录失败: ${err instanceof Error ? err.message : String(err)}`, isError: true };
256
+ }
257
+ },
258
+ });
259
+
260
+ // 4. 注册 /wechat-logout 命令(清除已保存的登录态)
261
+ api.registerCommand?.({
262
+ name: "wechat-logout",
263
+ description: "清除已保存的微信登录态",
264
+ handler: async ({ config }) => {
265
+ const channelCfg = config?.channels?.["wechat-access-unqclawed"];
266
+ const authStatePath = channelCfg?.authStatePath
267
+ ? String(channelCfg.authStatePath)
268
+ : undefined;
269
+ clearState(authStatePath);
270
+ return { text: "已清除登录态,下次启动将重新扫码登录。" };
271
+ },
272
+ });
231
273
 
232
274
  console.log("[wechat-access] 腾讯通路插件已注册");
233
275
  },
@@ -1,9 +1,9 @@
1
1
  {
2
- "id": "wechat-access",
2
+ "id": "wechat-access-unqclawed",
3
3
  "name": "WeChat Access",
4
4
  "description": "微信通路插件 — 扫码登录 + AGP WebSocket 双向通信",
5
5
  "version": "1.0.0",
6
- "channels": ["wechat-access"],
6
+ "channels": ["wechat-access-unqclawed"],
7
7
  "configSchema": {
8
8
  "type": "object",
9
9
  "properties": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@henryxiaoyang/wechat-access-unqclawed",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "type": "module",
5
5
  "description": "OpenClaw 微信通路插件 — 扫码登录 + AGP WebSocket 双向通信",
6
6
  "author": "HenryXiaoYang",
@@ -24,12 +24,12 @@
24
24
  "./index.ts"
25
25
  ],
26
26
  "channel": {
27
- "id": "wechat-access",
28
- "label": "wechat-access",
27
+ "id": "wechat-access-unqclawed",
28
+ "label": "wechat-access-unqclawed",
29
29
  "selectionLabel": "WeCom (plugin)",
30
30
  "detailLabel": "WeCom Bot",
31
31
  "docsPath": "/channels/wechat-access",
32
- "docsLabel": "wechat-access",
32
+ "docsLabel": "wechat-access-unqclawed",
33
33
  "blurb": "Enterprise WeCom intelligent bot (API mode) via encrypted webhooks + passive replies.",
34
34
  "aliases": [
35
35
  "wechatwork",
@@ -210,7 +210,7 @@ export const handlePrompt = async (
210
210
  * 这些统计数据用于 OpenClaw 控制台的活动监控面板。
211
211
  */
212
212
  runtime.channel.activity.record({
213
- channel: "wechat-access",
213
+ channel: "wechat-access-unqclawed",
214
214
  accountId: route.accountId ?? "default",
215
215
  direction: "inbound",
216
216
  });
@@ -449,7 +449,7 @@ export const handlePrompt = async (
449
449
 
450
450
  // 记录出站活动统计(每次 deliver 都算一次出站)
451
451
  runtime.channel.activity.record({
452
- channel: "wechat-access",
452
+ channel: "wechat-access-unqclawed",
453
453
  accountId: route.accountId ?? "default",
454
454
  direction: "outbound",
455
455
  });