qidao-openclaw-plugin 1.2.7 → 1.3.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +130 -34
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qidao-openclaw-plugin",
3
- "version": "1.2.7",
3
+ "version": "1.3.2",
4
4
  "type": "module",
5
5
  "main": "./src/index.js",
6
6
  "description": "OpenClaw 栖岛聊天 Channel 插件 - 连接到栖岛聊天服务",
package/src/index.js CHANGED
@@ -6,6 +6,19 @@ import { QidaoChannel } from './qidao-channel.js';
6
6
  import { registerAuthCommand } from './auth-cli.js';
7
7
 
8
8
  let connection = null;
9
+ let pluginApi = null;
10
+ let qidaoRuntime = null;
11
+
12
+ function setQidaoRuntime(r) {
13
+ qidaoRuntime = r;
14
+ }
15
+
16
+ function getQidaoRuntime() {
17
+ if (!qidaoRuntime) {
18
+ throw new Error('Qidao runtime not initialized - plugin not registered');
19
+ }
20
+ return qidaoRuntime;
21
+ }
9
22
 
10
23
  const qidaoChannel = {
11
24
  id: 'qidao',
@@ -19,6 +32,7 @@ const qidaoChannel = {
19
32
 
20
33
  capabilities: {
21
34
  chatTypes: ['direct', 'group'],
35
+ hasGateway: true,
22
36
  },
23
37
 
24
38
  config: {
@@ -126,32 +140,25 @@ const qidaoChannel = {
126
140
  }
127
141
  },
128
142
  },
129
- };
130
-
131
- export default function register(api) {
132
- api.logger.info('栖岛聊天插件加载中...');
133
143
 
134
- api.registerChannel({ plugin: qidaoChannel });
135
-
136
- // 注册认证命令
137
- registerAuthCommand(api);
138
-
139
- setImmediate(async () => {
140
- try {
141
- const cfg = api.getGatewayConfig?.() || {};
142
- const account = qidaoChannel.config.resolveAccount(cfg);
144
+ gateway: {
145
+ startAccount: async (ctx) => {
146
+ const { account, cfg, runtime, abortSignal } = ctx;
147
+
148
+ runtime.log?.('🚀 栖岛聊天 gateway.startAccount 被调用!');
149
+ runtime.log?.(`账户信息: ${JSON.stringify(account)}`);
143
150
 
144
151
  if (!account.enabled) {
145
- api.logger.info('栖岛聊天未启用');
152
+ runtime.log?.('栖岛聊天未启用');
146
153
  return;
147
154
  }
148
155
 
149
156
  if (!account.chatId) {
150
- api.logger.warn('栖岛聊天缺少chatId配置');
157
+ runtime.log?.('栖岛聊天缺少chatId配置');
151
158
  return;
152
159
  }
153
160
 
154
- api.logger.info(`连接栖岛聊天 (chatId: ${account.chatId})`);
161
+ runtime.log?.(`连接栖岛聊天 (chatId: ${account.chatId})`);
155
162
 
156
163
  connection = new QidaoChannel({
157
164
  serverUrl: account.serverUrl,
@@ -160,38 +167,127 @@ export default function register(api) {
160
167
  });
161
168
 
162
169
  connection.on('connect', () => {
163
- api.logger.info('栖岛聊天已连接');
170
+ runtime.log?.('栖岛聊天已连接');
164
171
  });
165
172
 
166
173
  connection.on('disconnect', () => {
167
- api.logger.warn('栖岛聊天已断开');
174
+ runtime.log?.('栖岛聊天已断开');
168
175
  });
169
176
 
170
177
  connection.on('error', (error) => {
171
- api.logger.error(`栖岛聊天错误: ${error.message}`);
178
+ runtime.error?.(`栖岛聊天错误: ${error.message}`);
172
179
  });
173
180
 
174
- connection.on('message', (message) => {
181
+ connection.on('message', async (message) => {
175
182
  if (message.type === 'new_message') {
176
- api.ingestMessage({
177
- channelId: 'qidao',
178
- accountId: 'default',
179
- senderId: message.senderId.toString(),
180
- senderName: message.senderName,
181
- text: message.messageText,
182
- timestamp: message.timestamp,
183
- chatType: message.chatType === 0 ? 'direct' : 'group',
184
- chatId: message.chatId.toString(),
185
- });
183
+ try {
184
+ // 过滤掉机器人自己发送的消息,避免循环
185
+ // 栖岛消息中,senderId=1 是畅小猪,otherUserId=175 是机器人
186
+ if (message.senderId === 175 || message.otherUserId === 1) {
187
+ runtime.log?.(`跳过机器人自己的消息`);
188
+ return;
189
+ }
190
+
191
+ runtime.log?.(`收到栖岛消息: ${message.messageText}`);
192
+
193
+ const core = getQidaoRuntime();
194
+
195
+ const chatId = message.chatId.toString();
196
+ const chatType = message.chatType === 0 ? 'direct' : 'group';
197
+
198
+ // 解析路由信息
199
+ const route = core.channel.routing.resolveAgentRoute({
200
+ cfg,
201
+ channel: 'qidao',
202
+ accountId: account.accountId,
203
+ peer: {
204
+ kind: chatType,
205
+ id: chatId,
206
+ },
207
+ });
208
+
209
+ // 构建标准消息上下文(参考企业微信插件)
210
+ const ctxPayload = core.channel.reply.finalizeInboundContext({
211
+ Body: message.messageText,
212
+ RawBody: message.messageText,
213
+ CommandBody: message.messageText,
214
+ MessageSid: message.messageId?.toString() || `${Date.now()}`,
215
+ From: chatType === 'group' ? `qidao:group:${chatId}` : `qidao:${message.senderId}`,
216
+ To: `qidao:${chatId}`,
217
+ SenderId: message.senderId.toString(),
218
+ SessionKey: route.sessionKey,
219
+ AccountId: account.accountId,
220
+ ChatType: chatType,
221
+ ConversationLabel: chatType === 'group' ? `group:${chatId}` : `user:${message.senderId}`,
222
+ Timestamp: message.timestamp || Date.now(),
223
+ Provider: 'qidao',
224
+ Surface: 'qidao',
225
+ OriginatingChannel: 'qidao',
226
+ OriginatingTo: `qidao:${chatId}`,
227
+ CommandAuthorized: true,
228
+ });
229
+
230
+ // 使用 core.channel.reply.dispatchReplyWithBufferedBlockDispatcher 处理消息
231
+ await core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
232
+ ctx: ctxPayload,
233
+ cfg,
234
+ dispatcherOptions: {
235
+ deliver: async (payload, info) => {
236
+ runtime.log?.(`发送回复: ${payload.text.substring(0, 50)}...`);
237
+
238
+ // 通过 connection 发送回复
239
+ if (info.kind === 'final') {
240
+ await connection.sendMessage(message.chatId, payload.text);
241
+ }
242
+ },
243
+ onError: (err, info) => {
244
+ runtime.error?.(`回复失败 (${info.kind}): ${err.message}`);
245
+ },
246
+ },
247
+ });
248
+
249
+ runtime.log?.('✅ 消息处理完成');
250
+ } catch (error) {
251
+ runtime.error?.(`处理消息失败: ${error.message}`);
252
+ }
186
253
  }
187
254
  });
188
255
 
256
+ // 处理中止信号
257
+ if (abortSignal) {
258
+ abortSignal.addEventListener('abort', () => {
259
+ runtime.log?.('栖岛聊天连接被中止');
260
+ if (connection) {
261
+ connection.disconnect();
262
+ connection = null;
263
+ }
264
+ });
265
+ }
266
+
189
267
  await connection.connect();
190
268
 
191
- } catch (error) {
192
- api.logger.error(`栖岛聊天插件初始化失败: ${error.message}`);
193
- }
194
- });
269
+ // 返回 Promise,保持连接直到被中止
270
+ return new Promise((resolve) => {
271
+ if (abortSignal) {
272
+ abortSignal.addEventListener('abort', resolve);
273
+ }
274
+ });
275
+ },
276
+ },
277
+ };
278
+
279
+ export default function register(api) {
280
+ pluginApi = api;
281
+
282
+ // 设置 runtime(参考企业微信插件)
283
+ setQidaoRuntime(api.runtime);
284
+
285
+ api.logger.info('栖岛聊天插件加载中...');
286
+
287
+ api.registerChannel({ plugin: qidaoChannel });
288
+
289
+ // 注册认证命令
290
+ registerAuthCommand(api);
195
291
 
196
292
  api.logger.info('栖岛聊天插件加载完成');
197
293
  }