qidao-openclaw-plugin 1.3.3 → 1.3.4

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 +158 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qidao-openclaw-plugin",
3
- "version": "1.3.3",
3
+ "version": "1.3.4",
4
4
  "type": "module",
5
5
  "main": "./src/index.js",
6
6
  "description": "OpenClaw 栖岛聊天 Channel 插件 - 连接到栖岛聊天服务",
package/src/index.js CHANGED
@@ -170,8 +170,164 @@ const qidaoChannel = {
170
170
  }
171
171
 
172
172
  if (!account.chatId) {
173
- runtime.log?.('栖岛聊天缺少chatId配置');
174
- return;
173
+ runtime.log?.('');
174
+ runtime.log?.('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
175
+ runtime.log?.('');
176
+ runtime.log?.(' 📱 栖岛聊天需要配置');
177
+ runtime.log?.('');
178
+ runtime.log?.(' 正在启动二维码认证...');
179
+ runtime.log?.('');
180
+ runtime.log?.('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
181
+ runtime.log?.('');
182
+
183
+ try {
184
+ // 动态导入认证模块
185
+ const ws = (await import('ws')).default;
186
+ const { spawn } = await import('child_process');
187
+
188
+ // 创建临时连接到 Server 进行认证
189
+ const tempWs = new ws('wss://oc.qidao.chat/ws');
190
+
191
+ const chatId = await new Promise((resolve, reject) => {
192
+ const timeout = setTimeout(() => {
193
+ tempWs.close();
194
+ reject(new Error('连接超时'));
195
+ }, 10000);
196
+
197
+ tempWs.on('open', () => {
198
+ clearTimeout(timeout);
199
+ runtime.log?.('✅ 已连接到认证服务器');
200
+ runtime.log?.('');
201
+
202
+ // 请求生成二维码
203
+ tempWs.send(JSON.stringify({ action: 'startAuth' }));
204
+ });
205
+
206
+ tempWs.on('message', async (data) => {
207
+ try {
208
+ const message = JSON.parse(data.toString());
209
+
210
+ // 兼容Server的响应格式
211
+ if ((message.code === 1 && message.data && message.data.qrUrl) ||
212
+ (message.action === 'authStarted')) {
213
+ const code = message.data?.code || message.code;
214
+ const qrUrl = message.data?.qrUrl || message.qrUrl;
215
+
216
+ runtime.log?.('📱 请使用栖岛 APP 扫描二维码:');
217
+ runtime.log?.('');
218
+ runtime.log?.(` ${qrUrl}`);
219
+ runtime.log?.('');
220
+ runtime.log?.('正在打开浏览器...');
221
+ runtime.log?.('');
222
+
223
+ // 打开浏览器显示二维码
224
+ const openCommand = process.platform === 'win32' ? 'start' :
225
+ process.platform === 'darwin' ? 'open' : 'xdg-open';
226
+ spawn(openCommand, [qrUrl], { shell: true, detached: true });
227
+
228
+ runtime.log?.('⏳ 等待扫码...');
229
+ runtime.log?.('');
230
+
231
+ // 开始轮询扫码状态
232
+ const pollInterval = setInterval(() => {
233
+ tempWs.send(JSON.stringify({ action: 'checkAuthStatus', code }));
234
+ }, 2000);
235
+
236
+ // 保存轮询定时器以便后续清理
237
+ tempWs._pollInterval = pollInterval;
238
+ tempWs._code = code;
239
+
240
+ } else if ((message.code === 1 && message.data && message.data.status) ||
241
+ (message.action === 'authStatus')) {
242
+ const status = message.data?.status || message.status;
243
+ const uid = message.data?.uid || message.uid;
244
+
245
+ if (status === 'success') {
246
+ runtime.log?.('✅ 扫码成功!');
247
+ runtime.log?.('');
248
+
249
+ // 请求完成认证获取 chatId
250
+ tempWs.send(JSON.stringify({
251
+ action: 'completeAuth',
252
+ uid,
253
+ code: tempWs._code
254
+ }));
255
+
256
+ } else if (status === 'waiting') {
257
+ // 继续等待
258
+ }
259
+
260
+ } else if ((message.code === 1 && message.data && message.data.chatId) ||
261
+ (message.action === 'authCompleted')) {
262
+ // 清理轮询定时器
263
+ if (tempWs._pollInterval) {
264
+ clearInterval(tempWs._pollInterval);
265
+ }
266
+
267
+ const receivedChatId = message.data?.chatId || message.chatId;
268
+ runtime.log?.(`🎉 认证完成!您的 chatId: ${receivedChatId}`);
269
+ runtime.log?.('');
270
+
271
+ tempWs.close();
272
+ resolve(receivedChatId);
273
+
274
+ } else if (message.code === 0 || message.action === 'error') {
275
+ if (tempWs._pollInterval) {
276
+ clearInterval(tempWs._pollInterval);
277
+ }
278
+ tempWs.close();
279
+ const errorMsg = message.msg || message.message || '认证失败';
280
+ reject(new Error(errorMsg));
281
+ }
282
+ } catch (err) {
283
+ runtime.error?.('处理消息失败:', err);
284
+ reject(err);
285
+ }
286
+ });
287
+
288
+ tempWs.on('error', (error) => {
289
+ clearTimeout(timeout);
290
+ if (tempWs._pollInterval) {
291
+ clearInterval(tempWs._pollInterval);
292
+ }
293
+ reject(error);
294
+ });
295
+ });
296
+
297
+ // 自动保存配置
298
+ runtime.log?.('正在保存配置...');
299
+ runtime.log?.('');
300
+
301
+ const { execSync } = await import('child_process');
302
+ execSync(`openclaw config set channels.qidao.chatId ${chatId}`, { stdio: 'inherit' });
303
+
304
+ runtime.log?.('');
305
+ runtime.log?.('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
306
+ runtime.log?.('');
307
+ runtime.log?.(' ✅ 配置完成!Gateway 将自动重新连接...');
308
+ runtime.log?.('');
309
+ runtime.log?.('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
310
+ runtime.log?.('');
311
+
312
+ // 更新 account 对象
313
+ account.chatId = chatId;
314
+
315
+ } catch (error) {
316
+ runtime.error?.('');
317
+ runtime.error?.('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
318
+ runtime.error?.('');
319
+ runtime.error?.(` ❌ 认证失败: ${error.message}`);
320
+ runtime.error?.('');
321
+ runtime.error?.(' 请运行以下命令重新认证:');
322
+ runtime.error?.('');
323
+ runtime.error?.(' openclaw qidao-auth');
324
+ runtime.error?.('');
325
+ runtime.error?.('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
326
+ runtime.error?.('');
327
+
328
+ // 抛出错误以停止自动重试
329
+ throw new Error('栖岛聊天缺少chatId配置,请运行 openclaw qidao-auth 完成认证');
330
+ }
175
331
  }
176
332
 
177
333
  runtime.log?.(`连接栖岛聊天 (chatId: ${account.chatId})`);
@@ -198,7 +354,6 @@ const qidaoChannel = {
198
354
  if (message.type === 'new_message') {
199
355
  try {
200
356
  // 过滤掉机器人自己发送的消息,避免循环
201
- // 栖岛消息中,senderId=1 是畅小猪,otherUserId=175 是机器人
202
357
  if (message.senderId === 175 || message.otherUserId === 1) {
203
358
  runtime.log?.(`跳过机器人自己的消息`);
204
359
  return;
@@ -211,7 +366,6 @@ const qidaoChannel = {
211
366
  const chatId = message.chatId.toString();
212
367
  const chatType = message.chatType === 0 ? 'direct' : 'group';
213
368
 
214
- // 解析路由信息
215
369
  const route = core.channel.routing.resolveAgentRoute({
216
370
  cfg,
217
371
  channel: 'qidao',
@@ -222,7 +376,6 @@ const qidaoChannel = {
222
376
  },
223
377
  });
224
378
 
225
- // 构建标准消息上下文(参考企业微信插件)
226
379
  const ctxPayload = core.channel.reply.finalizeInboundContext({
227
380
  Body: message.messageText,
228
381
  RawBody: message.messageText,
@@ -243,7 +396,6 @@ const qidaoChannel = {
243
396
  CommandAuthorized: true,
244
397
  });
245
398
 
246
- // 使用 core.channel.reply.dispatchReplyWithBufferedBlockDispatcher 处理消息
247
399
  await core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
248
400
  ctx: ctxPayload,
249
401
  cfg,
@@ -251,7 +403,6 @@ const qidaoChannel = {
251
403
  deliver: async (payload, info) => {
252
404
  runtime.log?.(`发送回复: ${payload.text.substring(0, 50)}...`);
253
405
 
254
- // 通过 connection 发送回复
255
406
  if (info.kind === 'final') {
256
407
  await connection.sendMessage(message.chatId, payload.text);
257
408
  }
@@ -269,7 +420,6 @@ const qidaoChannel = {
269
420
  }
270
421
  });
271
422
 
272
- // 处理中止信号
273
423
  if (abortSignal) {
274
424
  abortSignal.addEventListener('abort', () => {
275
425
  runtime.log?.('栖岛聊天连接被中止');
@@ -282,7 +432,6 @@ const qidaoChannel = {
282
432
 
283
433
  await connection.connect();
284
434
 
285
- // 返回 Promise,保持连接直到被中止
286
435
  return new Promise((resolve) => {
287
436
  if (abortSignal) {
288
437
  abortSignal.addEventListener('abort', resolve);