@cloud411716/fancy-webnovel 0.1.27 → 0.1.29

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloud411716/fancy-webnovel",
3
- "version": "0.1.27",
3
+ "version": "0.1.29",
4
4
  "type": "module",
5
5
  "main": "plugins/fancy-bootstrap/index.js",
6
6
  "exports": {
@@ -275,13 +275,48 @@ async function applyScan(ctx) {
275
275
  ? (result.error.message || JSON.stringify(result.error))
276
276
  : String(result.error || '未知错误');
277
277
  notifyScan(session, '❌ 采集失败:' + errMsg);
278
- } else {
279
- let files = [];
280
- try { files = JSON.parse(result.output).files || []; } catch (_) {}
281
- const fileList = files.length > 0 ? '\n\n📁 生成文件:\n' + files.map(f => ' - ' + f).join('\n') : '';
282
- notifyScan(session, `✅ ${platformLabel(platform)}(${length === 'long' ? '长篇' : '短篇'})采集完成${fileList}\n\n如需扫其他平台,请再次调用 /fancy-scan`);
278
+ return { kind: 'success', text: '' };
279
+ }
280
+
281
+ // Step 7: 解析 receipt,发送分析任务给 LLM
282
+ let receipt;
283
+ try { receipt = JSON.parse(result.output); } catch (_) {
284
+ notifyScan(session, '❌ 采集结果解析失败');
285
+ return { kind: 'success', text: '' };
283
286
  }
284
287
 
288
+ const files = receipt.scan_files || [];
289
+ const reportFile = receipt.report_file || '';
290
+ const topicFile = receipt.topic_decision || '';
291
+ const fileList = files.map(f => '- ' + f.replace(projectRoot + '/', '')).join('\n');
292
+
293
+ // Step 8-9: 通知 LLM 执行分析
294
+ notifyScan(session,
295
+ `📊 采集完成,正在将数据交给 LLM 进行分析...\n` +
296
+ `📁 生成文件:\n${fileList}\n` +
297
+ `📄 报告:${reportFile.replace(projectRoot + '/', '')}\n` +
298
+ `💡 分析报告将写入:${topicFile.replace(projectRoot + '/', '')}`
299
+ );
300
+
301
+ // 发消息给 LLM,让它读取原始文件并生成分析
302
+ const analysisPrompt =
303
+ `## 扫榜数据分析任务\n\n` +
304
+ `平台:${PLATFORM_CN[platform] || platform}(${length === 'long' ? '长篇' : '短篇'})\n` +
305
+ `时间:${receipt.date || ''}\n\n` +
306
+ `已生成的文件:\n${fileList}\n\n` +
307
+ `请执行以下步骤:\n` +
308
+ `1. 读取上述生成的原始数据文件(扫榜结果目录下的 .md 文件)\n` +
309
+ `2. 分析爆款书籍的题材、卖点、节奏、人设等特征\n` +
310
+ `3. 生成扫榜报告(包含:市场趋势、热门题材分析、用户画像、竞争度评估)\n` +
311
+ `4. 给出 3-5 个可行的选题方向建议\n` +
312
+ `5. 将完整分析报告追加写入:${topicFile}\n\n` +
313
+ `报告要求:\n- 客观分析数据,不臆测\n- 选题建议要有差异化竞争力\n- 报告语言:中文`;
314
+
315
+ session.append('user/message', {
316
+ content: [{ type: 'text', text: analysisPrompt }],
317
+ source: { kind: 'user' }
318
+ }, { surfaceOp: 'append' });
319
+
285
320
  return { kind: 'success', text: '' };
286
321
  }
287
322
  });
@@ -129,7 +129,7 @@ async function scrapeQidian(outDir) {
129
129
  const outFile = join(outDir, `${PLATFORM_CN.qidian}${rt.label}_${today}.md`);
130
130
  const md = renderQidianMarkdown(rt.label, url, books);
131
131
  writeFileSync(outFile, md, 'utf-8');
132
- console.log(` ✅ ${rt.label}: ${books.length} 本 → ${outFile}`);
132
+ console.error(` ✅ ${rt.label}: ${books.length} 本 → ${outFile}`);
133
133
  results.push({ label: rt.label, count: books.length, file: outFile });
134
134
  }
135
135
 
@@ -181,8 +181,8 @@ function scrapeBrowserStub(platform, length, outDir) {
181
181
  const instructions = getBrowserInstructions(platform, length);
182
182
 
183
183
  writeFileSync(instrFile, JSON.stringify(instructions, null, 2), 'utf-8');
184
- console.log(` ℹ ${pcn}(${lenStr})需要 browser_* 工具,已生成指令文件:${instrFile}`);
185
- console.log(` ℹ 请让 LLM 读取并执行该指令文件中的 browser 操作`);
184
+ console.error(` ℹ ${pcn}(${lenStr})需要 browser_* 工具,已生成指令文件:${instrFile}`);
185
+ console.error(` ℹ 请让 LLM 读取并执行该指令文件中的 browser 操作`);
186
186
  return { file: instrFile, instructions };
187
187
  }
188
188
 
@@ -315,7 +315,7 @@ function generateReport(platform, length, scanFiles, outDir) {
315
315
 
316
316
  const reportFile = join(outDir, `${pcn}${lenStr}扫榜报告_${today}.md`);
317
317
  writeFileSync(reportFile, report, 'utf-8');
318
- console.log(` ✅ 扫榜报告 → ${reportFile}`);
318
+ console.error(` ✅ 扫榜报告 → ${reportFile}`);
319
319
  return reportFile;
320
320
  }
321
321
 
@@ -354,7 +354,7 @@ function appendTopicDecision(platform, length, reportFile, outDir) {
354
354
  const header = `# 选题决策:${today}\n\n---\n\n`;
355
355
  writeFileSync(decisionFile, header + section + '\n', 'utf-8');
356
356
  }
357
- console.log(` ✅ topic_decision 追加 → ${decisionFile}`);
357
+ console.error(` ✅ topic_decision 追加 → ${decisionFile}`);
358
358
  return decisionFile;
359
359
  }
360
360
 
@@ -406,7 +406,7 @@ async function main() {
406
406
 
407
407
  if (platform === 'qidian') {
408
408
  // 起点:HTTP mobile SSR
409
- console.log('→ 采集 起点(mobile SSR)...');
409
+ console.error('→ 采集 起点(mobile SSR)...');
410
410
  scrapeFiles = await scrapeQidian(scanDir);
411
411
  if (!scrapeFiles.length) {
412
412
  console.error(' ❌ 起点采集失败');
@@ -415,15 +415,15 @@ async function main() {
415
415
  scanFiles = scrapeFiles.map(f => f.file);
416
416
  } else {
417
417
  // 其他平台:browser_* 工具(LLM 读取指令文件后执行)
418
- console.log(`→ ${PLATFORM_CN[platform]}(browser_* 工具)...`);
418
+ console.error(`→ ${PLATFORM_CN[platform]}(browser_* 工具)...`);
419
419
  const { file, instructions } = scrapeBrowserStub(platform, length, scanDir);
420
420
  scrapeFiles.push({ file });
421
421
  scanFiles.push(file);
422
422
 
423
423
  // 输出指令供 LLM 读取
424
- console.log('\n=== 浏览器采集指令 ===');
425
- console.log(JSON.stringify(instructions, null, 2));
426
- console.log('=== 浏览器采集指令 END ===\n');
424
+ console.error('\n=== 浏览器采集指令 ===');
425
+ console.error(JSON.stringify(instructions, null, 2));
426
+ console.error('=== 浏览器采集指令 END ===\n');
427
427
  }
428
428
 
429
429
  // 生成扫榜报告
@@ -431,6 +431,17 @@ async function main() {
431
431
  const reportFile = generateReport(platform, length, mdFiles, scanDir);
432
432
  appendTopicDecision(platform, length, reportFile, scanDir);
433
433
 
434
+ // Step 6: 收集原始数据
435
+ const rawData = {};
436
+ for (const f of scanFiles) {
437
+ try {
438
+ const name = f.replace(/^.*\//, '').replace(/\.md$/, '');
439
+ rawData[name] = { path: f, content: '' };
440
+ const { readFileSync } = await import('fs');
441
+ rawData[name].content = readFileSync(f, 'utf-8');
442
+ } catch (_) {}
443
+ }
444
+
434
445
  const receipt = {
435
446
  ok: true,
436
447
  operation: 'scan',
@@ -440,11 +451,11 @@ async function main() {
440
451
  scan_files: scanFiles,
441
452
  report_file: reportFile,
442
453
  topic_decision: join(scanDir, `topic_decision_${today}.md`),
454
+ raw_data: rawData,
443
455
  summary: `${PLATFORM_CN[platform]}(${length})采集完成,共 ${scanFiles.length} 个文件`,
444
456
  };
445
457
 
446
- console.log('\n=== RECEIPT ===');
447
- console.log(JSON.stringify(receipt, null, 2));
458
+ console.log(JSON.stringify(receipt));
448
459
  }
449
460
 
450
461
  main().catch(e => {