@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 +13 -3
- package/common/message-context.ts +5 -5
- package/http/message-handler.ts +4 -4
- package/index.ts +53 -11
- package/openclaw.plugin.json +2 -2
- package/package.json +4 -4
- package/websocket/message-handler.ts +2 -2
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
|
-
|
|
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 处理消息所需的所有信息
|
package/http/message-handler.ts
CHANGED
|
@@ -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. 注册
|
|
230
|
-
|
|
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
|
},
|
package/openclaw.plugin.json
CHANGED
|
@@ -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.
|
|
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
|
});
|