qidao-openclaw-plugin 1.0.1 → 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/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/index.js +166 -170
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 栖岛聊天 OpenClaw Channel 插件
|
|
3
|
-
*
|
|
4
|
-
* 这是OpenClaw插件的主入口文件
|
|
5
3
|
*/
|
|
6
4
|
|
|
7
5
|
import { QidaoChannel } from './qidao-channel.js';
|
|
@@ -9,194 +7,192 @@ import { QidaoChannel } from './qidao-channel.js';
|
|
|
9
7
|
// 存储所有账户的连接实例
|
|
10
8
|
const accountConnections = new Map();
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* @param {Object} api - OpenClaw Plugin API
|
|
15
|
-
*/
|
|
16
|
-
export default function register(api) {
|
|
17
|
-
api.logger.info('栖岛聊天插件加载中...');
|
|
10
|
+
export default {
|
|
11
|
+
id: "qidao-openclaw-plugin",
|
|
18
12
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
configSchema: {
|
|
14
|
+
type: "object",
|
|
15
|
+
properties: {}
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
async register(api, ctx) {
|
|
19
|
+
api.logger.info('栖岛聊天插件加载中...');
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
// 注册Channel
|
|
22
|
+
api.registerChannel({
|
|
24
23
|
id: 'qidao',
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
chatTypes: ['direct', 'group'],
|
|
32
|
-
supportsMedia: true,
|
|
33
|
-
supportsReactions: false,
|
|
34
|
-
supportsThreads: false,
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
config: {
|
|
38
|
-
// 列出所有配置的账户ID
|
|
39
|
-
listAccountIds: (cfg) => {
|
|
40
|
-
const accounts = cfg.channels?.qidao?.accounts ?? {};
|
|
41
|
-
return Object.keys(accounts);
|
|
24
|
+
|
|
25
|
+
meta: {
|
|
26
|
+
id: 'qidao',
|
|
27
|
+
label: '栖岛聊天',
|
|
28
|
+
selectionLabel: '栖岛聊天 (Qidao Chat)',
|
|
29
|
+
blurb: '连接到栖岛聊天服务,支持实时消息收发',
|
|
42
30
|
},
|
|
43
31
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (!account) {
|
|
50
|
-
return { accountId };
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return {
|
|
54
|
-
accountId: accountId ?? 'default',
|
|
55
|
-
enabled: account.enabled ?? true,
|
|
56
|
-
chatId: account.chatId,
|
|
57
|
-
userId: account.userId,
|
|
58
|
-
serverUrl: account.serverUrl ?? 'wss://oc.qidao.chat/ws',
|
|
59
|
-
};
|
|
32
|
+
capabilities: {
|
|
33
|
+
chatTypes: ['direct', 'group'],
|
|
34
|
+
supportsMedia: true,
|
|
35
|
+
supportsReactions: false,
|
|
36
|
+
supportsThreads: false,
|
|
60
37
|
},
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
outbound: {
|
|
64
|
-
deliveryMode: 'direct',
|
|
65
38
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
ok: false,
|
|
73
|
-
error: '未连接到栖岛服务'
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// chatId必须由调用方传入
|
|
78
|
-
if (!chatId) {
|
|
79
|
-
return {
|
|
80
|
-
ok: false,
|
|
81
|
-
error: '未指定chatId'
|
|
82
|
-
};
|
|
83
|
-
}
|
|
39
|
+
config: {
|
|
40
|
+
// 列出所有配置的账户ID
|
|
41
|
+
listAccountIds: (cfg) => {
|
|
42
|
+
const accounts = cfg.channels?.qidao?.accounts ?? {};
|
|
43
|
+
return Object.keys(accounts);
|
|
44
|
+
},
|
|
84
45
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
46
|
+
// 解析指定账户的配置
|
|
47
|
+
resolveAccount: (cfg, accountId) => {
|
|
48
|
+
const accounts = cfg.channels?.qidao?.accounts ?? {};
|
|
49
|
+
const account = accounts[accountId ?? 'default'];
|
|
88
50
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
51
|
+
if (!account) {
|
|
52
|
+
return { accountId };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
accountId: accountId ?? 'default',
|
|
57
|
+
enabled: account.enabled ?? true,
|
|
58
|
+
chatId: account.chatId,
|
|
59
|
+
userId: account.userId,
|
|
60
|
+
serverUrl: account.serverUrl ?? 'wss://oc.qidao.chat/ws',
|
|
94
61
|
};
|
|
95
|
-
}
|
|
62
|
+
},
|
|
96
63
|
},
|
|
97
64
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const connection = accountConnections.get(accountId ?? 'default');
|
|
101
|
-
|
|
102
|
-
if (!connection || !connection.isConnected()) {
|
|
103
|
-
return {
|
|
104
|
-
ok: false,
|
|
105
|
-
error: '未连接到栖岛服务'
|
|
106
|
-
};
|
|
107
|
-
}
|
|
65
|
+
outbound: {
|
|
66
|
+
deliveryMode: 'direct',
|
|
108
67
|
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
68
|
+
// 发送文本消息
|
|
69
|
+
sendText: async ({ text, accountId, chatId }) => {
|
|
70
|
+
const connection = accountConnections.get(accountId ?? 'default');
|
|
71
|
+
|
|
72
|
+
if (!connection || !connection.isConnected()) {
|
|
73
|
+
return {
|
|
74
|
+
ok: false,
|
|
75
|
+
error: '未连接到栖岛服务'
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!chatId) {
|
|
80
|
+
return {
|
|
81
|
+
ok: false,
|
|
82
|
+
error: '未指定chatId'
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
await connection.sendMessage(parseInt(chatId), text);
|
|
88
|
+
return { ok: true };
|
|
89
|
+
} catch (error) {
|
|
90
|
+
return {
|
|
91
|
+
ok: false,
|
|
92
|
+
error: error.message
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
},
|
|
116
96
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
97
|
+
// 发送媒体消息
|
|
98
|
+
sendMedia: async ({ url, accountId, chatId }) => {
|
|
99
|
+
const connection = accountConnections.get(accountId ?? 'default');
|
|
120
100
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
|
|
101
|
+
if (!connection || !connection.isConnected()) {
|
|
102
|
+
return {
|
|
103
|
+
ok: false,
|
|
104
|
+
error: '未连接到栖岛服务'
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!chatId) {
|
|
109
|
+
return {
|
|
110
|
+
ok: false,
|
|
111
|
+
error: '未指定chatId'
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
await connection.sendImageMessage(parseInt(chatId), url);
|
|
117
|
+
return { ok: true };
|
|
118
|
+
} catch (error) {
|
|
119
|
+
return {
|
|
120
|
+
ok: false,
|
|
121
|
+
error: error.message
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
},
|
|
128
125
|
},
|
|
129
|
-
},
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
// 获取配置
|
|
133
|
-
const config = api.getConfig();
|
|
134
|
-
const accounts = config.channels?.qidao?.accounts ?? {};
|
|
135
|
-
|
|
136
|
-
// 为每个启用的账户创建连接
|
|
137
|
-
Object.entries(accounts).forEach(([accountId, accountConfig]) => {
|
|
138
|
-
if (accountConfig.enabled === false) {
|
|
139
|
-
api.logger.info(`栖岛账户 ${accountId} 已禁用`);
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (!accountConfig.chatId) {
|
|
144
|
-
api.logger.warn(`栖岛账户 ${accountId} 缺少chatId配置`);
|
|
145
|
-
api.logger.warn(`请运行 'cd qidao-channel && node test.js' 获取chatId`);
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const serverUrl = accountConfig.serverUrl ?? 'wss://oc.qidao.chat/ws';
|
|
150
|
-
|
|
151
|
-
api.logger.info(`连接栖岛账户: ${accountId} (chatId: ${accountConfig.chatId})`);
|
|
152
|
-
|
|
153
|
-
// 创建连接
|
|
154
|
-
const connection = new QidaoChannel({
|
|
155
|
-
serverUrl,
|
|
156
|
-
chatId: accountConfig.chatId,
|
|
157
|
-
userId: accountConfig.userId,
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
// 监听连接事件
|
|
161
|
-
connection.on('connect', () => {
|
|
162
|
-
api.logger.info(`栖岛账户 ${accountId} 已连接`);
|
|
163
126
|
});
|
|
164
127
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
128
|
+
// 获取OpenClaw配置
|
|
129
|
+
const openclawConfig = api.getGatewayConfig?.() || {};
|
|
130
|
+
const accounts = openclawConfig.channels?.qidao?.accounts ?? {};
|
|
168
131
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
api.logger.debug(`栖岛账户 ${accountId} 收到消息:`, message);
|
|
132
|
+
// 为每个启用的账户创建连接
|
|
133
|
+
Object.entries(accounts).forEach(([accountId, accountConfig]) => {
|
|
134
|
+
if (accountConfig.enabled === false) {
|
|
135
|
+
api.logger.info(`栖岛账户 ${accountId} 已禁用`);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
176
138
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
channelId: 'qidao',
|
|
181
|
-
accountId: accountId,
|
|
182
|
-
senderId: message.senderId.toString(),
|
|
183
|
-
senderName: message.senderName,
|
|
184
|
-
text: message.messageText,
|
|
185
|
-
timestamp: message.timestamp,
|
|
186
|
-
chatType: message.chatType === 0 ? 'direct' : 'group',
|
|
187
|
-
chatId: message.chatId.toString(),
|
|
188
|
-
});
|
|
139
|
+
if (!accountConfig.chatId) {
|
|
140
|
+
api.logger.warn(`栖岛账户 ${accountId} 缺少chatId配置`);
|
|
141
|
+
return;
|
|
189
142
|
}
|
|
143
|
+
|
|
144
|
+
const serverUrl = accountConfig.serverUrl ?? 'wss://oc.qidao.chat/ws';
|
|
145
|
+
|
|
146
|
+
api.logger.info(`连接栖岛账户: ${accountId} (chatId: ${accountConfig.chatId})`);
|
|
147
|
+
|
|
148
|
+
// 创建连接
|
|
149
|
+
const connection = new QidaoChannel({
|
|
150
|
+
serverUrl,
|
|
151
|
+
chatId: accountConfig.chatId,
|
|
152
|
+
userId: accountConfig.userId,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// 监听连接事件
|
|
156
|
+
connection.on('connect', () => {
|
|
157
|
+
api.logger.info(`栖岛账户 ${accountId} 已连接`);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
connection.on('disconnect', () => {
|
|
161
|
+
api.logger.warn(`栖岛账户 ${accountId} 已断开`);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
connection.on('error', (error) => {
|
|
165
|
+
api.logger.error(`栖岛账户 ${accountId} 错误: ${error.message}`);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// 监听栖岛消息,转发给OpenClaw
|
|
169
|
+
connection.on('message', (message) => {
|
|
170
|
+
api.logger.debug(`栖岛账户 ${accountId} 收到消息:`, message);
|
|
171
|
+
|
|
172
|
+
// 将栖岛消息转换为OpenClaw消息格式
|
|
173
|
+
if (message.type === 'new_message') {
|
|
174
|
+
api.ingestMessage({
|
|
175
|
+
channelId: 'qidao',
|
|
176
|
+
accountId: accountId,
|
|
177
|
+
senderId: message.senderId.toString(),
|
|
178
|
+
senderName: message.senderName,
|
|
179
|
+
text: message.messageText,
|
|
180
|
+
timestamp: message.timestamp,
|
|
181
|
+
chatType: message.chatType === 0 ? 'direct' : 'group',
|
|
182
|
+
chatId: message.chatId.toString(),
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// 连接到服务器
|
|
188
|
+
connection.connect().catch((error) => {
|
|
189
|
+
api.logger.error(`栖岛账户 ${accountId} 连接失败: ${error.message}`);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// 保存连接实例
|
|
193
|
+
accountConnections.set(accountId, connection);
|
|
190
194
|
});
|
|
191
195
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
// 保存连接实例
|
|
198
|
-
accountConnections.set(accountId, connection);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
api.logger.info('栖岛聊天插件加载完成');
|
|
202
|
-
}
|
|
196
|
+
api.logger.info('栖岛聊天插件加载完成');
|
|
197
|
+
}
|
|
198
|
+
};
|