foliko 1.1.21 → 1.1.22

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.
@@ -204,7 +204,12 @@
204
204
  "Bash(node -e \"require\\('./src/core/agent-chat.js'\\); console.log\\('OK'\\)\" 2>&1)",
205
205
  "Bash(node -e \"const ai = require\\('ai'\\); console.log\\('tool type:', typeof ai.tool\\)\")",
206
206
  "Bash(taskkill //PID 26152 //F)",
207
- "Bash(netstat -ano | grep 3000)"
207
+ "Bash(netstat -ano | grep 3000)",
208
+ "Bash(node test-error.js 2>&1)",
209
+ "Bash(node -c src/core/agent-chat.js && node -c src/utils/chat-queue.js && node -c cli/src/ui/chat-ui.js && node -c plugins/weixin-plugin.js && node -c plugins/telegram-plugin.js)",
210
+ "Bash(node -c plugins/weixin-plugin.js && node -c plugins/telegram-plugin.js && node -c cli/src/ui/chat-ui.js)",
211
+ "Bash(node -c src/utils/chat-queue.js)",
212
+ "Bash(node -c src/utils/logger.js)"
208
213
  ]
209
214
  }
210
215
  }
@@ -126,7 +126,7 @@ async function runContinuousTest(agent, options) {
126
126
  } else if (chunk.type === 'tool-call') {
127
127
  console.log(`\n[工具调用] ${chunk.toolName} ${JSON.stringify(chunk.input)}`);
128
128
  } else if (chunk.type === 'error') {
129
- console.error(`\n[错误] ${chunk.error}`);
129
+ //console.error(`\n[错误] ${chunk.error}`);
130
130
  }
131
131
  }
132
132
  console.log('\n' + '-'.repeat(50));
@@ -136,6 +136,8 @@ class ChatUI {
136
136
 
137
137
  process.on('SIGINT', interruptHandler);
138
138
 
139
+ let hasError = false;
140
+
139
141
  try {
140
142
  const renderState = { inThink: false, inCodeBlock: false };
141
143
  const { sessionId } = this;
@@ -150,7 +152,6 @@ class ChatUI {
150
152
  // 创建 session scope 过滤事件
151
153
  const sessionScope = this.agent.createSessionScope(sessionId);
152
154
 
153
- // 监听流式数据
154
155
  sessionScope.on('stream:chunk', ({ chunk }) => {
155
156
  if (interrupted) return;
156
157
 
@@ -167,11 +168,15 @@ class ChatUI {
167
168
  console.log(renderLine(line, renderState));
168
169
  }
169
170
  }
170
- } else if (chunk.type === 'error') {
171
- console.error(`\n${colored('[错误]', RED)} ${chunk.error}`);
172
171
  }
173
172
  });
174
173
 
174
+ // 监听 queue:failed 事件(处理队列级别的最终错误)
175
+ sessionScope.on('queue:failed', ({ error }) => {
176
+ hasError = true;
177
+ console.error(`\n${colored('[错误]', RED)} ${error}`);
178
+ });
179
+
175
180
  // 调用 sendMessage 触发事件
176
181
  const res = await this.agent.sendMessage(message, { sessionId });
177
182
  // 清理
@@ -199,13 +204,14 @@ class ChatUI {
199
204
  }
200
205
  } catch (err) {
201
206
  // 只打印简洁错误消息,不打印堆栈
202
- const errName = err?.name || '';
203
- const isRetryError = errName === 'AI_RetryError' || errName === 'RetryError';
204
- const friendlyMessage = isRetryError
205
- ? 'AI 服务暂时不可用,请稍后重试'
206
- : (err.message || '未知错误').split('\n')[0];
207
+ // 避免与 message:error 事件重复打印
208
+ if (!hasError && !interrupted) {
209
+ const errName = err?.name || '';
210
+ const isRetryError = errName === 'AI_RetryError' || errName === 'RetryError';
211
+ const friendlyMessage = isRetryError
212
+ ? 'AI 服务暂时不可用,请稍后重试'
213
+ : (err.message || '未知错误').split('\n')[0];
207
214
 
208
- if (!interrupted) {
209
215
  console.error(`\n${colored('[错误]', RED)} ${friendlyMessage}\n`);
210
216
  }
211
217
  } finally {
@@ -89,7 +89,9 @@ async function main() {
89
89
  } else if (chunk.type === 'tool-result') {
90
90
  console.log('\n[工具结果]', JSON.stringify(chunk.result).substring(0, 100));
91
91
  } else if (chunk.type === 'error') {
92
- console.error('\n[错误]', chunk.error);
92
+ throw new Error('sdfsdf');
93
+
94
+ //console.error('\n[错误]', chunk.error);
93
95
  }
94
96
  }
95
97
 
@@ -110,6 +112,10 @@ async function main() {
110
112
  '请帮我注册一个 GET 路由:\n- 路径:/test\n- 处理函数:return "1232432"\n\n注册完成后,用 web_request 工具测试访问这个路由,确认返回 "1232432"'
111
113
  );
112
114
 
115
+ await ask(
116
+ '关闭服务'
117
+ );
118
+
113
119
  console.log('\n--- 测试完成,现在你可以继续对话 ---\n');
114
120
 
115
121
  // 主循环
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foliko",
3
- "version": "1.1.21",
3
+ "version": "1.1.22",
4
4
  "description": "简约的插件化 Agent 框架",
5
5
  "main": "src/index.js",
6
6
  "type": "commonjs",
@@ -329,6 +329,14 @@ class TelegramPlugin extends Plugin {
329
329
  }
330
330
  })
331
331
 
332
+ // 监听错误事件
333
+ sessionScope.on('queue:failed', async ({ error }) => {
334
+ await this._bot.editMessageText(escapeMarkdown(`❌ ${error}`), {
335
+ chat_id: chatId,
336
+ message_id: thinkingMsg.message_id
337
+ })
338
+ })
339
+
332
340
  await agent.sendMessage(text, { sessionId })
333
341
 
334
342
  sessionScope.removeAllListeners()
@@ -537,8 +537,6 @@ class WeixinPlugin extends Plugin {
537
537
  message+=colored(`[工具开始](${chunk.toolName})`,YELLOW)
538
538
  }else if(chunk.type==='tool-result'){
539
539
  message+=colored(`[工具结束](${chunk.toolName})`,YELLOW)
540
- }else if(chunk.type==='error'){
541
- message+=colored(`[错误](${chunk.error})`,RED)
542
540
  }
543
541
  while (message.includes('\n')) {
544
542
  const nlIndex = message.indexOf('\n');
@@ -564,7 +562,7 @@ class WeixinPlugin extends Plugin {
564
562
  lineBuffer = ''
565
563
  message=''
566
564
  })
567
- sessionScope.on('message:error', async ({error}) => {
565
+ sessionScope.on('queue:failed', async ({error}) => {
568
566
  this.stopTypingInterval()
569
567
  await this._sendMessageBatch(originalMsg, userId, error, true)
570
568
  lineBuffer = ''
@@ -56,7 +56,7 @@ class AgentChatHandler extends EventEmitter {
56
56
  agent,
57
57
  maxConcurrent: config.maxConcurrent || 1,
58
58
  retryAttempts: config.retryAttempts || 3,
59
- retryDelay: config.retryDelay || 1000,
59
+ retryDelay: config.retryDelay || 10000,
60
60
  });
61
61
 
62
62
  // ToolExecutor: 工具管理
@@ -438,8 +438,13 @@ class AgentChatHandler extends EventEmitter {
438
438
  prepareStep: this._createPrepareStep(),
439
439
  });
440
440
 
441
+ // AI SDK 错误回调:仅记录日志,不在这里处理(统一由 catch/yield 处理)
442
+ const handleSDKError = ({ error }) => {
443
+ logger.debug(`[AgentChat] SDK onError: ${error?.message}`);
444
+ };
445
+
441
446
  const result = await framework.runInSession(sessionId, {}, async () => {
442
- return agent.stream({ messages, ...this.providerOptions });
447
+ return agent.stream({ messages, ...this.providerOptions, onError: handleSDKError });
443
448
  });
444
449
 
445
450
  const stream = result.fullStream;
@@ -458,7 +463,9 @@ class AgentChatHandler extends EventEmitter {
458
463
  } else if (part.type === 'tool-result') {
459
464
  yield { type: 'tool-result', toolName: part.toolName, result: part.output };
460
465
  } else if (part.type === 'error') {
461
- yield { type: 'error', error: part.error };
466
+ // 统一错误消息
467
+ const errMsg = part.error?.message || 'AI 服务错误';
468
+ yield { type: 'error', error: errMsg.split('\n')[0] };
462
469
  }
463
470
  }
464
471
 
@@ -472,35 +479,10 @@ class AgentChatHandler extends EventEmitter {
472
479
  const userMsg = messages[messages.length - finishMessages.length - 1];
473
480
  this.emit('message', { content: fullText, sessionId: sessionId, userMessage: userMsg });
474
481
  } catch (err) {
475
- // 提取简洁的错误消息(不包含堆栈)
476
- const simpleMessage = err?.message?.split('\n')[0] || '未知错误';
477
- const errName = err?.name || '';
478
-
479
- // 使用 AI SDK 的错误类型判断
480
- const isRetryError =
481
- RetryError.isInstance?.(err) ||
482
- errName === 'AI_RetryError' ||
483
- errName === 'RetryError' ||
484
- err?.reason === 'maxRetriesExceeded';
485
-
486
- // AI SDK 的错误 map
487
- const errorMessages = {
488
- AI_RetryError: 'AI 服务暂时不可用,请稍后重试',
489
- AI_APICallError: err?.isRetryable ? 'AI 服务暂时不可用,请稍后重试' : simpleMessage,
490
- AI_NoContentGeneratedError: 'AI 未生成有效内容,请重试',
491
- AI_NoOutputGeneratedError: 'AI 未生成有效内容,请重试',
492
- AI_NoSuchModelError: '指定的 AI 模型不存在',
493
- AI_NoSuchProviderError: 'AI 提供商配置错误',
494
- AI_LoadAPIKeyError: 'AI API 密钥配置错误',
495
- AI_InvalidPromptError: '输入内容格式错误',
496
- AI_ToolCallRepairError: '工具调用处理失败,请重试',
497
- };
498
-
499
- const friendlyMessage =
500
- errorMessages[errName] || (isRetryError ? 'AI 服务暂时不可用,请稍后重试' : simpleMessage);
482
+ // 统一错误处理:提取简洁消息并通过 yield 传递
483
+ const friendlyMessage = err?.message?.split('\n')[0] || '未知错误';
501
484
 
502
- //logger.error(`[AgentChat] Error [${errName}]: ${simpleMessage}`);
503
- this.emit('message:error', { sessionId, error: friendlyMessage });
485
+ this.emit('message:error', { sessionId, error: friendlyMessage, originalError: err });
504
486
  yield { type: 'error', error: friendlyMessage };
505
487
  } finally {
506
488
  const messageStore = this._getSessionMessageStore(sessionId);
@@ -1,5 +1,7 @@
1
1
  const { EventEmitter } = require('./event-emitter');
2
2
  const { cleanResponse } = require('./index');
3
+ const { logger } = require('./logger');
4
+ const log = logger.child('ChatQueue');
3
5
  // ChatQueueManager.js
4
6
  class ChatQueueManager extends EventEmitter {
5
7
  constructor(options = {}) {
@@ -8,8 +10,9 @@ class ChatQueueManager extends EventEmitter {
8
10
  this.isProcessing = false;
9
11
  this.maxConcurrent = options.maxConcurrent || 1;
10
12
  this.activeCount = 0;
11
- this.retryAttempts = options.retryAttempts || 5;
13
+ this.retryAttempts = options.retryAttempts || 3;
12
14
  this.retryDelay = options.retryDelay || 10000;
15
+
13
16
  }
14
17
 
15
18
  /**
@@ -72,13 +75,8 @@ class ChatQueueManager extends EventEmitter {
72
75
  const result = await this.executeWithRetry(item);
73
76
  // 检查 result 是否有错误(而不是通过 try/catch)
74
77
  if (result.error) {
75
- console.log('[ChatQueue] Rejecting with error from result:', result.error.message);
76
- item.reject(result.error);
77
- this.emit('queue:failed', {
78
- requestId: item.id,
79
- sessionId: item.sessionId,
80
- error: result.error.message,
81
- });
78
+ //console.log('[ChatQueue] Rejecting with error from result:', result.error.message);
79
+ throw result.error
82
80
  } else {
83
81
  item.resolve(result);
84
82
  this.emit('queue:completed', {
@@ -88,7 +86,7 @@ class ChatQueueManager extends EventEmitter {
88
86
  });
89
87
  }
90
88
  } catch (error) {
91
- console.log('[ChatQueue] Rejecting with thrown error:', error.message);
89
+ log.info('[ChatQueue] Rejecting Error:', error.message);
92
90
  item.reject(error);
93
91
  this.emit('queue:failed', {
94
92
  requestId: item.id,
@@ -119,28 +117,21 @@ class ChatQueueManager extends EventEmitter {
119
117
 
120
118
  // 检查是否有错误(通过返回的 result.error)
121
119
  if (result.error) {
122
- console.log(
123
- '[ChatQueue] executeWithRetry: attempt',
124
- attempt,
125
- 'got error:',
126
- result.error.message
127
- );
128
120
  lastError = result.error;
129
121
  if (attempt < this.retryAttempts && this.isRetryableError(lastError)) {
130
122
  await this.sleep(this.retryDelay * Math.pow(2, attempt - 1));
131
123
  continue;
132
124
  }
133
- // 返回 result,让 processQueue 处理错误
134
- console.log('[ChatQueue] executeWithRetry: returning result with error, no more retries');
135
- return result;
125
+ // 重试耗尽,直接抛出 result.error
126
+ throw lastError;
136
127
  }
137
128
 
138
129
  return result;
139
130
  } catch (error) {
140
- console.log(
141
- '[ChatQueue] executeWithRetry: attempt',
131
+ log.info(
132
+ '[ChatQueue] executeWithRetry: ',
142
133
  attempt,
143
- 'threw error:',
134
+ 'error:',
144
135
  error.message
145
136
  );
146
137
  lastError = error;
@@ -161,7 +152,7 @@ class ChatQueueManager extends EventEmitter {
161
152
 
162
153
  const friendlyError = new Error(friendlyMessage);
163
154
  friendlyError.originalError = lastError;
164
- console.log('[ChatQueue] executeWithRetry: throwing friendly error:', friendlyMessage);
155
+ //log.info('[ChatQueue] executeWithRetry: throwing friendly error:', friendlyMessage);
165
156
  throw friendlyError;
166
157
  }
167
158
 
@@ -196,8 +187,8 @@ class ChatQueueManager extends EventEmitter {
196
187
  ? 'AI 服务暂时不可用,请稍后重试'
197
188
  : (err.message || err.toString()).split('\n')[0];
198
189
 
199
- console.log(
200
- '[ChatQueue] executeStream caught error, converting to friendly:',
190
+ log.info(
191
+ '[ChatQueue] executeStream Error:',
201
192
  friendlyMessage
202
193
  );
203
194
  chunks.push({ type: 'error', error: friendlyMessage });
@@ -217,7 +208,7 @@ class ChatQueueManager extends EventEmitter {
217
208
  const error = new Error(errorChunk.error || 'Stream error');
218
209
  error.chunks = chunks;
219
210
  error.isStreamError = true;
220
- console.log('[ChatQueue] executeStream returning result with error:', error.message);
211
+ log.info('[ChatQueue] executeStream Error:', error.message);
221
212
  return {
222
213
  chunks,
223
214
  content: cleanResponse(''),
@@ -33,8 +33,9 @@ class Logger {
33
33
  info: '\x1b[32m', // 绿色
34
34
  warn: '\x1b[33m', // 黄色
35
35
  error: '\x1b[31m', // 红色
36
+ grey: '\x1b[90m', // 暗灰色
36
37
  reset: '\x1b[0m',
37
- dim: '\x1b[2m', // 灰色
38
+ dim: '\x1b[2m', // 暗灰色
38
39
  };
39
40
  }
40
41
 
@@ -72,6 +73,7 @@ class Logger {
72
73
  const color = this.colors[levelName.toLowerCase()] || '';
73
74
  const reset = this.colors.reset;
74
75
  const dim = this.colors.dim;
76
+ const grey=this.colors.grey;
75
77
 
76
78
  const parts = [];
77
79
 
@@ -91,7 +93,8 @@ class Logger {
91
93
  parts.push(levelName.padEnd(5));
92
94
  }
93
95
 
94
- // 消息
96
+ // 消息(根据级别着色)
97
+ const msgColor = this.enableColors ? color : '';
95
98
  const message = args
96
99
  .map((arg) => {
97
100
  if (arg instanceof Error) {
@@ -108,7 +111,7 @@ class Logger {
108
111
  })
109
112
  .join(' ');
110
113
 
111
- parts.push(message);
114
+ parts.push(`${grey}${message}${reset}`);
112
115
  return parts.join(' ');
113
116
  }
114
117
 
package/nul DELETED
@@ -1,3 +0,0 @@
1
- dir: cannot access '/s': No such file or directory
2
- dir: cannot access '/b': No such file or directory
3
- dir: cannot access '*.png': No such file or directory
package/undefined.svg DELETED
@@ -1,8 +0,0 @@
1
- <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="802" height="NaN" viewBox="-1,NaN,802,NaN"><defs><linearGradient x1="400" y1="600" x2="1000" y2="600" gradientUnits="userSpaceOnUse" id="color-1"><stop offset="0" stop-color="#f5f0e6"></stop><stop offset="1" stop-color="#ede8dc"></stop></linearGradient></defs><g fill="none" fill-rule="nonzero" stroke="none" stroke-width="none" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><path d="M0,1200v-1200h800v1200z" fill="url(#color-1)" stroke="none" stroke-width="1"></path><path d="M0,1200v-1200h800v1200z" fill="none" stroke="#000000" stroke-width="2"></path><path d="M40,1140v-1100h720v1100z" fill-opacity="0" fill="#000000" stroke="none" stroke-width="1"></path><text x="400" y="55" fill="#6b6459" stroke="none" stroke-width="1" font-family="Microsoft YaHei, SimHei, KaiTi, seguiemj, sans-serif" font-weight="normal" font-size="14" text-anchor="middle">━━━━━━━━━━━━━━━ VOL.01 ━━━━━━━━━━━━━━━</text><text x="400" y="130" fill="#b85450" stroke="none" stroke-width="1" font-family="Microsoft YaHei, SimHei, KaiTi, seguiemj, sans-serif" font-weight="normal" font-size="52" text-anchor="middle">微信 iLink 机器人</text><text x="400" y="195" fill="#6b6459" stroke="none" stroke-width="1" font-family="Microsoft YaHei, SimHei, KaiTi, seguiemj, sans-serif" font-weight="normal" font-size="18" text-anchor="middle">零依赖 · 极简接入 · 高效稳定</text><text x="400" y="235" fill="#1a1a1a" stroke="none" stroke-width="1" font-family="Microsoft YaHei, SimHei, KaiTi, seguiemj, sans-serif" font-weight="normal" font-size="16" text-anchor="middle">WEIXIN iLINK BOT SDK FOR NODE.JS</text><path d="M100,270h600" fill="none" stroke="#ffffff" stroke-width="2"></path><path d="M100,NaNc0,NaN 0,NaN 0,NaNh290c0,0 0,NaN 0,NaNc0,NaN -290,NaN -290,NaNz" fill="#ffffff" stroke="none" stroke-width="1"></path><text x="110" y="454" fill="#000000" stroke="none" stroke-width="1" font-family="Microsoft YaHei, msyh, PatuaOne-Regular, seguiemj, seguisym, SegUIVar, wryh, Segoe UI Emoji, sans-serif" font-weight="normal" font-size="24" text-anchor="start">[object Object]</text><text x="110" y="480" fill="#666666" stroke="none" stroke-width="1" font-family="Microsoft YaHei, msyh, PatuaOne-Regular, seguiemj, seguisym, SegUIVar, wryh, Segoe UI Emoji, sans-serif" font-weight="normal" font-size="16" text-anchor="start">[object Object]</text><path d="M105,480c-2.76142,0 -5,-2.23858 -5,-5v-50c0,-2.76142 2.23858,-5 5,-5h280c2.76142,0 5,2.23858 5,5v50c0,2.76142 -2.23858,5 -5,5z" fill="#ffffff" stroke="none" stroke-width="1"></path><text x="245" y="435" fill="#b85450" stroke="none" stroke-width="1" font-family="Microsoft YaHei, SimHei, KaiTi, seguiemj, sans-serif" font-weight="normal" font-size="20" text-anchor="middle">ZERO DEPENDENCY</text><text x="245" y="460" fill="#6b6459" stroke="none" stroke-width="1" font-family="Microsoft YaHei, SimHei, KaiTi, seguiemj, sans-serif" font-weight="normal" font-size="12" text-anchor="middle">零外部依赖 · 轻量安装</text><path d="M415,480c-2.76142,0 -5,-2.23858 -5,-5v-50c0,-2.76142 2.23858,-5 5,-5h280c2.76142,0 5,2.23858 5,5v50c0,2.76142 -2.23858,5 -5,5z" fill="#ffffff" stroke="none" stroke-width="1"></path><text x="555" y="435" fill="#b85450" stroke="none" stroke-width="1" font-family="Microsoft YaHei, SimHei, KaiTi, seguiemj, sans-serif" font-weight="normal" font-size="20" text-anchor="middle">QR CODE AUTH</text><text x="555" y="460" fill="#6b6459" stroke="none" stroke-width="1" font-family="Microsoft YaHei, SimHei, KaiTi, seguiemj, sans-serif" font-weight="normal" font-size="12" text-anchor="middle">扫码登录 · 安全认证</text><path d="M105,555c-2.76142,0 -5,-2.23858 -5,-5v-50c0,-2.76142 2.23858,-5 5,-5h280c2.76142,0 5,2.23858 5,5v50c0,2.76142 -2.23858,5 -5,5z" fill="#ffffff" stroke="none" stroke-width="1"></path><text x="245" y="510" fill="#b85450" stroke="none" stroke-width="1" font-family="Microsoft YaHei, SimHei, KaiTi, seguiemj, sans-serif" font-weight="normal" font-size="20" text-anchor="middle">EVENT DRIVEN</text><text x="245" y="535" fill="#6b6459" stroke="none" stroke-width="1" font-family="Microsoft YaHei, SimHei, KaiTi, seguiemj, sans-serif" font-weight="normal" font-size="12" text-anchor="middle">事件驱动 · 高效响应</text><path d="M415,555c-2.76142,0 -5,-2.23858 -5,-5v-50c0,-2.76142 2.23858,-5 5,-5h280c2.76142,0 5,2.23858 5,5v50c0,2.76142 -2.23858,5 -5,5z" fill="#ffffff" stroke="none" stroke-width="1"></path><text x="555" y="510" fill="#b85450" stroke="none" stroke-width="1" font-family="Microsoft YaHei, SimHei, KaiTi, seguiemj, sans-serif" font-weight="normal" font-size="20" text-anchor="middle">RICH MEDIA</text><text x="555" y="535" fill="#6b6459" stroke="none" stroke-width="1" font-family="Microsoft YaHei, SimHei, KaiTi, seguiemj, sans-serif" font-weight="normal" font-size="12" text-anchor="middle">多媒体消息 · 丰富交互</text><path d="M100,580h600" fill="none" stroke="#ffffff" stroke-width="2"></path><text x="400" y="600" fill="#1a1a1a" stroke="none" stroke-width="1" font-family="Microsoft YaHei, SimHei, KaiTi, seguiemj, sans-serif" font-weight="normal" font-size="16" text-anchor="middle">QUICK START</text><path d="M108,830c-4.41828,0 -8,-3.58172 -8,-8v-184c0,-4.41828 3.58172,-8 8,-8h584c4.41828,0 8,3.58172 8,8v184c0,4.41828 -3.58172,8 -8,8z" fill="#2d2d2d" stroke="none" stroke-width="1"></path><text x="120" y="645" fill="#f8f8f2" stroke="none" stroke-width="1" font-family="Consolas, Microsoft YaHei, seguiemj, sans-serif" font-weight="normal" font-size="13" text-anchor="start">import { WeixinBot } from '@chnak/weixin-bot';
2
-
3
- const bot = new WeixinBot({
4
- onQRCode: (qr) =&gt; console.log(qr),
5
- onMessage: (msg) =&gt; bot.sendText(msg.from, 'Hello!')
6
- });
7
-
8
- bot.start();</text><path d="M100,850h600" fill="none" stroke="#ffffff" stroke-width="2"></path><path d="M106,920c-3.31371,0 -6,-2.68629 -6,-6v-38c0,-3.31371 2.68629,-6 6,-6h588c3.31371,0 6,2.68629 6,6v38c0,3.31371 -2.68629,6 -6,6z" fill="#2d2d2d" stroke="none" stroke-width="1"></path><text x="400" y="885" fill="#50fa7b" stroke="none" stroke-width="1" font-family="Consolas, Microsoft YaHei, seguiemj, sans-serif" font-weight="normal" font-size="18" text-anchor="middle">$ npm install @chnak/weixin-bot</text><text x="400" y="950" fill="#6b6459" stroke="none" stroke-width="1" font-family="Microsoft YaHei, SimHei, KaiTi, seguiemj, sans-serif" font-weight="normal" font-size="14" text-anchor="middle">v1.2.9 · MIT License · Node.js &gt;= 14.0.0</text><text x="400" y="980" fill="#b85450" stroke="none" stroke-width="1" font-family="Microsoft YaHei, SimHei, KaiTi, seguiemj, sans-serif" font-weight="normal" font-size="14" text-anchor="middle">npmjs.com/package/@chnak/weixin-bot</text><text x="400" y="1020" fill="#6b6459" stroke="none" stroke-width="1" font-family="Microsoft YaHei, SimHei, KaiTi, seguiemj, sans-serif" font-weight="normal" font-size="14" text-anchor="middle">━━━━━━━━━━━━━━━ ◆ ━━━━━━━━━━━━━━━</text><text x="400" y="1045" fill="#6b6459" stroke="none" stroke-width="1" font-family="Microsoft YaHei, SimHei, KaiTi, seguiemj, sans-serif" font-weight="normal" font-size="12" text-anchor="middle">❖ ❖</text></g></svg>
Binary file
Binary file