codewave-openclaw-installer 2.1.12 → 2.2.0

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 (221) hide show
  1. package/bin/install-lib.mjs +11 -0
  2. package/bin/install.mjs +59 -1
  3. package/package.json +12 -1
  4. package/skills/acceptance-doc-entry/SKILL.md +54 -1
  5. package/skills/ppt-master/SKILL.md +17 -390
  6. package/skills/ppt-master/projects/.gitkeep +0 -0
  7. package/skills/ppt-master/references/executor-base.md +4 -3
  8. package/skills/ppt-master/references/image-generator.md +6 -93
  9. package/skills/ppt-master/references/strategist.md +5 -8
  10. package/skills/ppt-master/scripts/README.md +1 -1
  11. package/skills/ppt-master/scripts/finalize_svg.py +0 -14
  12. package/skills/ppt-master/scripts/svg_quality_checker.py +8 -169
  13. package/skills/ppt-master/scripts/svg_to_pptx/pptx_builder.py +1 -7
  14. package/skills/ppt-master/scripts/svg_to_pptx/pptx_cli.py +0 -14
  15. package/skills/risk-alert/SKILL.md +110 -0
  16. package/skills/risk-alert/config.example.json +21 -0
  17. package/skills/risk-alert/scheduler.sh +74 -0
  18. package/skills/risk-alert/scripts/analyze-risk.js +414 -0
  19. package/skills/risk-alert/scripts/config-manager.js +241 -0
  20. package/skills/risk-alert/scripts/push-beautiful.js +225 -0
  21. package/skills/risk-alert/scripts/risk-alert.js +136 -0
  22. package/skills/risk-alert/scripts/run-full.js +60 -0
  23. package/skills/risk-alert/trigger-risk-alert.sh +35 -0
  24. package/skills/ppt-master/.env.example +0 -35
  25. package/skills/ppt-master/README.md +0 -90
  26. package/skills/ppt-master/audit_report_model_perf.md +0 -213
  27. package/skills/ppt-master/audit_report_portability.md +0 -90
  28. package/skills/ppt-master/audit_report_robustness.md +0 -192
  29. package/skills/ppt-master/audit_report_workflow.md +0 -233
  30. package/skills/ppt-master/fix_report_bid.md +0 -51
  31. package/skills/ppt-master/fix_report_scripts.md +0 -41
  32. package/skills/ppt-master/references/bid-content-example-netease.md +0 -815
  33. package/skills/ppt-master/references/bid-content-template.md +0 -390
  34. package/skills/ppt-master/references/bid-example-netease.md +0 -339
  35. package/skills/ppt-master/references/bid-presentation.md +0 -384
  36. package/skills/ppt-master/references/bid-svgs/03_toc.svg +0 -38
  37. package/skills/ppt-master/references/bid-svgs/04_chapter_company.svg +0 -7
  38. package/skills/ppt-master/references/bid-svgs/05_company_overview.svg +0 -28
  39. package/skills/ppt-master/references/bid-svgs/06_vision_business.svg +0 -45
  40. package/skills/ppt-master/references/bid-svgs/07_product_system.svg +0 -52
  41. package/skills/ppt-master/references/bid-svgs/08_codewave_timeline.svg +0 -29
  42. package/skills/ppt-master/references/bid-svgs/09_certifications.svg +0 -33
  43. package/skills/ppt-master/references/bid-svgs/10_client_logos.svg +0 -25
  44. package/skills/ppt-master/references/bid-svgs/11_brand_mission.svg +0 -14
  45. package/skills/ppt-master/references/bid-svgs/12_chapter_demand.svg +0 -7
  46. package/skills/ppt-master/references/bid-svgs/16_chapter_advantages.svg +0 -7
  47. package/skills/ppt-master/references/bid-svgs/18_platform_arch.svg +0 -24
  48. package/skills/ppt-master/references/bid-svgs/19_function_matrix.svg +0 -35
  49. package/skills/ppt-master/references/bid-svgs/20_dev_arch.svg +0 -29
  50. package/skills/ppt-master/references/bid-svgs/21_source_code.svg +0 -25
  51. package/skills/ppt-master/references/bid-svgs/22_ai_dev.svg +0 -27
  52. package/skills/ppt-master/references/bid-svgs/23_asset_market.svg +0 -33
  53. package/skills/ppt-master/references/bid-svgs/24_multi_org.svg +0 -28
  54. package/skills/ppt-master/references/bid-svgs/25_i18n.svg +0 -22
  55. package/skills/ppt-master/references/bid-svgs/26_multi_tenant.svg +0 -25
  56. package/skills/ppt-master/references/bid-svgs/27_integration.svg +0 -27
  57. package/skills/ppt-master/references/bid-svgs/28_permission.svg +0 -31
  58. package/skills/ppt-master/references/bid-svgs/29_security.svg +0 -25
  59. package/skills/ppt-master/references/bid-svgs/30_ai_capabilities.svg +0 -21
  60. package/skills/ppt-master/references/bid-svgs/31_product_combo.svg +0 -25
  61. package/skills/ppt-master/references/bid-svgs/32_digital_transform.svg +0 -28
  62. package/skills/ppt-master/references/bid-svgs/33_chapter_cases.svg +0 -7
  63. package/skills/ppt-master/references/bid-svgs/34_cummins_case.svg +0 -23
  64. package/skills/ppt-master/references/bid-svgs/35_cpq_cost.svg +0 -22
  65. package/skills/ppt-master/references/bid-svgs/36_tech_platform.svg +0 -18
  66. package/skills/ppt-master/references/bid-svgs/37_platform_value.svg +0 -24
  67. package/skills/ppt-master/references/bid-svgs/38_operation_plan.svg +0 -25
  68. package/skills/ppt-master/references/bid-svgs/39_zpmc_case.svg +0 -21
  69. package/skills/ppt-master/references/bid-svgs/40_zpmc_apps.svg +0 -23
  70. package/skills/ppt-master/references/bid-svgs/41_chapter_service.svg +0 -7
  71. package/skills/ppt-master/references/bid-svgs/42_tech_support.svg +0 -24
  72. package/skills/ppt-master/references/bid-svgs/43_knowledge_transfer.svg +0 -25
  73. package/skills/ppt-master/references/bid-svgs/44_operation_promotion.svg +0 -20
  74. package/skills/ppt-master/references/bid-svgs/45_custom_dev_guide.svg +0 -24
  75. package/skills/ppt-master/references/bid-svgs/46_sla_guarantee.svg +0 -18
  76. package/skills/ppt-master/references/bid-svgs/47_strategic_cooperation.svg +0 -25
  77. package/skills/ppt-master/references/bid-svgs/48_ecosystem.svg +0 -24
  78. package/skills/ppt-master/references/bid-svgs/49_chapter_delivery.svg +0 -23
  79. package/skills/ppt-master/references/bid-svgs/53_pm_intro.svg +0 -24
  80. package/skills/ppt-master/references/bid-svgs/54_after_sales.svg +0 -27
  81. package/skills/ppt-master/references/bid-svgs/55_ending.svg +0 -8
  82. package/skills/ppt-master/references/company_intro.md +0 -93
  83. package/skills/ppt-master/references/company_intro_images/slide1_img00.png +0 -0
  84. package/skills/ppt-master/references/company_intro_images/slide1_img01.png +0 -0
  85. package/skills/ppt-master/references/company_intro_images/slide1_img02.png +0 -0
  86. package/skills/ppt-master/references/company_intro_images/slide1_img03.png +0 -0
  87. package/skills/ppt-master/references/company_intro_images/slide1_img04.png +0 -0
  88. package/skills/ppt-master/references/company_intro_images/slide1_img05.png +0 -0
  89. package/skills/ppt-master/references/company_intro_images/slide1_img06.png +0 -0
  90. package/skills/ppt-master/references/company_intro_images/slide1_img07.png +0 -0
  91. package/skills/ppt-master/references/company_intro_images/slide1_img08.png +0 -0
  92. package/skills/ppt-master/references/company_intro_images/slide1_img09.png +0 -0
  93. package/skills/ppt-master/references/company_intro_images/slide1_img10.png +0 -0
  94. package/skills/ppt-master/references/company_intro_images/slide1_img11.png +0 -0
  95. package/skills/ppt-master/references/company_intro_images/slide1_img12.png +0 -0
  96. package/skills/ppt-master/references/company_intro_images/slide1_img13.png +0 -0
  97. package/skills/ppt-master/references/company_intro_images/slide1_img14.png +0 -0
  98. package/skills/ppt-master/references/company_intro_images/slide1_img15.png +0 -0
  99. package/skills/ppt-master/references/company_intro_images/slide1_img16.png +0 -0
  100. package/skills/ppt-master/references/company_intro_images/slide1_img17.png +0 -0
  101. package/skills/ppt-master/references/company_intro_images/slide1_img18.png +0 -0
  102. package/skills/ppt-master/references/company_intro_images/slide1_img19.png +0 -0
  103. package/skills/ppt-master/references/company_intro_images/slide1_img20.png +0 -0
  104. package/skills/ppt-master/references/company_intro_images/slide1_img21.png +0 -0
  105. package/skills/ppt-master/references/company_intro_images/slide1_img22.png +0 -0
  106. package/skills/ppt-master/references/company_intro_images/slide1_img23.png +0 -0
  107. package/skills/ppt-master/references/company_intro_images/slide1_img24.png +0 -0
  108. package/skills/ppt-master/references/company_intro_images/slide1_img25.png +0 -0
  109. package/skills/ppt-master/references/company_intro_images/slide1_img26.png +0 -0
  110. package/skills/ppt-master/references/company_intro_images/slide1_img27.png +0 -0
  111. package/skills/ppt-master/references/company_intro_images/slide1_img28.png +0 -0
  112. package/skills/ppt-master/references/company_intro_images/slide1_img29.png +0 -0
  113. package/skills/ppt-master/references/company_intro_images/slide2_img30.png +0 -0
  114. package/skills/ppt-master/references/company_intro_images/slide2_img31.jpg +0 -0
  115. package/skills/ppt-master/references/company_intro_images/slide3_img32.png +0 -0
  116. package/skills/ppt-master/references/company_intro_images/slide3_img33.png +0 -0
  117. package/skills/ppt-master/references/drawio-integration.md +0 -144
  118. package/skills/ppt-master/requirements.txt +0 -19
  119. package/skills/ppt-master/resources/backgrounds/chapter_bg.png +0 -0
  120. package/skills/ppt-master/resources/backgrounds/company_bg.png +0 -0
  121. package/skills/ppt-master/resources/backgrounds/cover_bg.png +0 -0
  122. package/skills/ppt-master/resources/backgrounds/index.json +0 -40
  123. package/skills/ppt-master/resources/backgrounds/tech_bg.png +0 -0
  124. package/skills/ppt-master/scripts/__pycache__/config.cpython-314.pyc +0 -0
  125. package/skills/ppt-master/scripts/__pycache__/error_helper.cpython-314.pyc +0 -0
  126. package/skills/ppt-master/scripts/__pycache__/pptx_animations.cpython-314.pyc +0 -0
  127. package/skills/ppt-master/scripts/__pycache__/project_utils.cpython-314.pyc +0 -0
  128. package/skills/ppt-master/scripts/arch_diagram.py +0 -370
  129. package/skills/ppt-master/scripts/bid_init.py +0 -79
  130. package/skills/ppt-master/scripts/drawio_gen.py +0 -366
  131. package/skills/ppt-master/scripts/drawio_to_svg.py +0 -458
  132. package/skills/ppt-master/scripts/image_backends/__pycache__/__init__.cpython-314.pyc +0 -0
  133. package/skills/ppt-master/scripts/image_backends/__pycache__/backend_aigw_gemini.cpython-314.pyc +0 -0
  134. package/skills/ppt-master/scripts/image_backends/__pycache__/backend_common.cpython-314.pyc +0 -0
  135. package/skills/ppt-master/scripts/svg_finalize/__pycache__/__init__.cpython-314.pyc +0 -0
  136. package/skills/ppt-master/scripts/svg_finalize/__pycache__/crop_images.cpython-314.pyc +0 -0
  137. package/skills/ppt-master/scripts/svg_finalize/__pycache__/embed_icons.cpython-314.pyc +0 -0
  138. package/skills/ppt-master/scripts/svg_finalize/__pycache__/embed_images.cpython-314.pyc +0 -0
  139. package/skills/ppt-master/scripts/svg_finalize/__pycache__/fix_image_aspect.cpython-314.pyc +0 -0
  140. package/skills/ppt-master/scripts/svg_finalize/__pycache__/flatten_tspan.cpython-314.pyc +0 -0
  141. package/skills/ppt-master/scripts/svg_finalize/__pycache__/svg_rect_to_path.cpython-314.pyc +0 -0
  142. package/skills/ppt-master/scripts/svg_page_gen.py +0 -871
  143. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/__init__.cpython-314.pyc +0 -0
  144. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_context.cpython-314.pyc +0 -0
  145. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_converter.cpython-314.pyc +0 -0
  146. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_elements.cpython-314.pyc +0 -0
  147. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_paths.cpython-314.pyc +0 -0
  148. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_styles.cpython-314.pyc +0 -0
  149. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_utils.cpython-314.pyc +0 -0
  150. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_builder.cpython-314.pyc +0 -0
  151. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_cli.cpython-314.pyc +0 -0
  152. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_dimensions.cpython-314.pyc +0 -0
  153. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_discovery.cpython-314.pyc +0 -0
  154. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_media.cpython-314.pyc +0 -0
  155. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_notes.cpython-314.pyc +0 -0
  156. package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_slide_xml.cpython-314.pyc +0 -0
  157. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210 → 344/270/255/345/233/275/347/224/265/345/273/272_/345/270/270/350/247/204}/01_cover.svg" +0 -0
  158. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210 → 344/270/255/345/233/275/347/224/265/345/273/272_/345/270/270/350/247/204}/02_chapter.svg" +0 -0
  159. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210 → 344/270/255/345/233/275/347/224/265/345/273/272_/345/270/270/350/247/204}/02_toc.svg" +0 -0
  160. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210 → 344/270/255/345/233/275/347/224/265/345/273/272_/345/270/270/350/247/204}/03_content.svg" +0 -0
  161. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210 → 344/270/255/345/233/275/347/224/265/345/273/272_/345/270/270/350/247/204}/04_ending.svg" +0 -0
  162. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210 → 344/270/255/345/233/275/347/224/265/345/273/272_/345/270/270/350/247/204}/design_spec.md" +0 -0
  163. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210//316/243/342/225/225/302/241/317/203/302/242/342/225/234/302/265/342/226/221/342/224/244/317/203e/314/200i/314/201logo.png" → 344/270/255/345/233/275/347/224/265/345/273/272_/345/270/270/350/247/204//344/270/255/345/233/275/346/260/264/345/212/241logo.png"} +0 -0
  164. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210//317/203i/314/200A/314/210/316/243/342/225/225/302/243/316/230O/314/210o/314/201logo.png" → 344/270/255/345/233/275/347/224/265/345/273/272_/345/270/270/350/247/204//345/215/216/344/270/234/351/231/242logo.png"} +0 -0
  165. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210//302/265/342/226/221/342/224/244/317/204o/314/210/342/225/241/316/243/342/225/225e/314/210/317/203/342/226/222C/314/247logo.png" → 344/270/255/345/233/275/347/224/265/345/273/272_/345/270/270/350/247/204//346/260/264/347/224/265/344/270/211/345/261/200logo.png"} +0 -0
  166. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210//317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221logo.png" → 344/270/255/345/233/275/347/224/265/345/273/272_/345/270/270/350/247/204//347/224/265/345/273/272logo.png"} +0 -0
  167. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201 → 344/270/255/345/233/275/347/224/265/345/273/272_/347/216/260/344/273/243}/01_cover.svg" +0 -0
  168. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201 → 344/270/255/345/233/275/347/224/265/345/273/272_/347/216/260/344/273/243}/02_chapter.svg" +0 -0
  169. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201 → 344/270/255/345/233/275/347/224/265/345/273/272_/347/216/260/344/273/243}/02_toc.svg" +0 -0
  170. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201 → 344/270/255/345/233/275/347/224/265/345/273/272_/347/216/260/344/273/243}/03_content.svg" +0 -0
  171. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201 → 344/270/255/345/233/275/347/224/265/345/273/272_/347/216/260/344/273/243}/04_ending.svg" +0 -0
  172. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201 → 344/270/255/345/233/275/347/224/265/345/273/272_/347/216/260/344/273/243}/design_spec.md" +0 -0
  173. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201//316/243/342/225/225/302/241/317/203/302/242/342/225/234/302/265/342/226/221/342/224/244/317/203e/314/200i/314/201logo.png" → 344/270/255/345/233/275/347/224/265/345/273/272_/347/216/260/344/273/243//344/270/255/345/233/275/346/260/264/345/212/241logo.png"} +0 -0
  174. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201//317/203i/314/200A/314/210/316/243/342/225/225/302/243/316/230O/314/210o/314/201logo.png" → 344/270/255/345/233/275/347/224/265/345/273/272_/347/216/260/344/273/243//345/215/216/344/270/234/351/231/242logo.png"} +0 -0
  175. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201//302/265/342/226/221/342/224/244/317/204o/314/210/342/225/241/316/243/342/225/225e/314/210/317/203/342/226/222C/314/247logo.png" → 344/270/255/345/233/275/347/224/265/345/273/272_/347/216/260/344/273/243//346/260/264/347/224/265/344/270/211/345/261/200logo.png"} +0 -0
  176. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/317/203/302/242/342/225/234/317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201//317/204o/314/210/342/225/241/317/203/342/225/227/342/225/221logo.png" → 344/270/255/345/233/275/347/224/265/345/273/272_/347/216/260/344/273/243//347/224/265/345/273/272logo.png"} +0 -0
  177. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203o/314/200a/314/212/317/203e/314/200i/314/201 → 344/270/255/346/261/275/347/240/224_/345/225/206/345/212/241}/01_cover.svg" +0 -0
  178. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203o/314/200a/314/212/317/203e/314/200i/314/201 → 344/270/255/346/261/275/347/240/224_/345/225/206/345/212/241}/02_chapter.svg" +0 -0
  179. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203o/314/200a/314/212/317/203e/314/200i/314/201 → 344/270/255/346/261/275/347/240/224_/345/225/206/345/212/241}/02_toc.svg" +0 -0
  180. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203o/314/200a/314/212/317/203e/314/200i/314/201 → 344/270/255/346/261/275/347/240/224_/345/225/206/345/212/241}/03_content.svg" +0 -0
  181. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203o/314/200a/314/212/317/203e/314/200i/314/201 → 344/270/255/346/261/275/347/240/224_/345/225/206/345/212/241}/04_ending.svg" +0 -0
  182. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203o/314/200a/314/212/317/203e/314/200i/314/201 → 344/270/255/346/261/275/347/240/224_/345/225/206/345/212/241}/design_spec.md" +0 -0
  183. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203o/314/200a/314/212/317/203e/314/200i/314/201//317/203A/314/212/342/224/202/316/243/342/225/225e/314/200/316/246/302/272/303/206 logo.png" → 344/270/255/346/261/275/347/240/224_/345/225/206/345/212/241//345/217/263/344/270/212/350/247/222 logo.png"} +0 -0
  184. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203o/314/200a/314/212/317/203e/314/200i/314/201//317/203n/314/203/302/272/317/203/342/202/247i/314/210 logo.png" → 344/270/255/346/261/275/347/240/224_/345/225/206/345/212/241//345/244/247/345/236/213 logo.png"} +0 -0
  185. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210 → 344/270/255/346/261/275/347/240/224_/345/270/270/350/247/204}/01_cover.svg" +0 -0
  186. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210 → 344/270/255/346/261/275/347/240/224_/345/270/270/350/247/204}/02_chapter.svg" +0 -0
  187. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210 → 344/270/255/346/261/275/347/240/224_/345/270/270/350/247/204}/02_toc.svg" +0 -0
  188. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210 → 344/270/255/346/261/275/347/240/224_/345/270/270/350/247/204}/03_content.svg" +0 -0
  189. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210 → 344/270/255/346/261/275/347/240/224_/345/270/270/350/247/204}/04_ending.svg" +0 -0
  190. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210 → 344/270/255/346/261/275/347/240/224_/345/270/270/350/247/204}/design_spec.md" +0 -0
  191. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210//317/203A/314/212/342/224/202/316/243/342/225/225e/314/200/316/246/302/272/303/206 logo.png" → 344/270/255/346/261/275/347/240/224_/345/270/270/350/247/204//345/217/263/344/270/212/350/247/222 logo.png"} +0 -0
  192. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/203/342/225/225/342/225/225/316/246/302/272a/314/210//317/203n/314/203/302/272/317/203/342/202/247i/314/210 logo.png" → 344/270/255/346/261/275/347/240/224_/345/270/270/350/247/204//345/244/247/345/236/213 logo.png"} +0 -0
  193. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201 → 344/270/255/346/261/275/347/240/224_/347/216/260/344/273/243}/01_cover.svg" +0 -0
  194. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201 → 344/270/255/346/261/275/347/240/224_/347/216/260/344/273/243}/02_chapter.svg" +0 -0
  195. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201 → 344/270/255/346/261/275/347/240/224_/347/216/260/344/273/243}/02_toc.svg" +0 -0
  196. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201 → 344/270/255/346/261/275/347/240/224_/347/216/260/344/273/243}/03_content.svg" +0 -0
  197. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201 → 344/270/255/346/261/275/347/240/224_/347/216/260/344/273/243}/04_ending.svg" +0 -0
  198. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201 → 344/270/255/346/261/275/347/240/224_/347/216/260/344/273/243}/design_spec.md" +0 -0
  199. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201//317/203A/314/212/342/224/202/316/243/342/225/225e/314/200/316/246/302/272/303/206 logo.png" → 344/270/255/346/261/275/347/240/224_/347/216/260/344/273/243//345/217/263/344/270/212/350/247/222 logo.png"} +0 -0
  200. /package/skills/ppt-master/templates/layouts//{316/243/342/225/225/302/241/302/265/342/226/222/342/225/234/317/204a/314/201o/314/210_/317/204A/314/210/342/226/221/316/243/342/225/227u/314/201//317/203n/314/203/302/272/317/203/342/202/247i/314/210 logo.png" → 344/270/255/346/261/275/347/240/224_/347/216/260/344/273/243//345/244/247/345/236/213 logo.png"} +0 -0
  201. /package/skills/ppt-master/templates/layouts//{302/265i/314/210/302/242/317/203o/314/200a/314/212/316/230o/314/202/342/225/242/316/246i/314/201i/314/202 → 346/213/233/345/225/206/351/223/266/350/241/214}/01_cover.svg" +0 -0
  202. /package/skills/ppt-master/templates/layouts//{302/265i/314/210/302/242/317/203o/314/200a/314/212/316/230o/314/202/342/225/242/316/246i/314/201i/314/202 → 346/213/233/345/225/206/351/223/266/350/241/214}/02_chapter.svg" +0 -0
  203. /package/skills/ppt-master/templates/layouts//{302/265i/314/210/302/242/317/203o/314/200a/314/212/316/230o/314/202/342/225/242/316/246i/314/201i/314/202 → 346/213/233/345/225/206/351/223/266/350/241/214}/02_toc.svg" +0 -0
  204. /package/skills/ppt-master/templates/layouts//{302/265i/314/210/302/242/317/203o/314/200a/314/212/316/230o/314/202/342/225/242/316/246i/314/201i/314/202 → 346/213/233/345/225/206/351/223/266/350/241/214}/03_content.svg" +0 -0
  205. /package/skills/ppt-master/templates/layouts//{302/265i/314/210/302/242/317/203o/314/200a/314/212/316/230o/314/202/342/225/242/316/246i/314/201i/314/202 → 346/213/233/345/225/206/351/223/266/350/241/214}/04_ending.svg" +0 -0
  206. /package/skills/ppt-master/templates/layouts//{302/265i/314/210/302/242/317/203o/314/200a/314/212/316/230o/314/202/342/225/242/316/246i/314/201i/314/202 → 346/213/233/345/225/206/351/223/266/350/241/214}/design_spec.md" +0 -0
  207. /package/skills/ppt-master/templates/layouts//{302/265i/314/210/302/242/317/203o/314/200a/314/212/316/230o/314/202/342/225/242/316/246i/314/201i/314/202//302/265i/314/210/302/242/317/203o/314/200a/314/212/316/230o/314/202/342/225/242/316/246i/314/201i/314/202/317/203a/314/200/302/274/317/203A/314/212/342/225/225/316/230c/314/247/303/246/316/246/342/202/247i/314/200.png" → 346/213/233/345/225/206/351/223/266/350/241/214//346/213/233/345/225/206/351/223/266/350/241/214/345/205/254/345/217/270/351/207/221/350/236/215.png"} +0 -0
  208. /package/skills/ppt-master/templates/layouts//{317/204/302/272/303/246/302/265e/314/200C/314/247/316/246o/314/202/302/245/317/203o/314/200a/314/212/317/203e/314/200i/314/201 → 347/247/221/346/212/200/350/223/235/345/225/206/345/212/241}/01_cover.svg" +0 -0
  209. /package/skills/ppt-master/templates/layouts//{317/204/302/272/303/246/302/265e/314/200C/314/247/316/246o/314/202/302/245/317/203o/314/200a/314/212/317/203e/314/200i/314/201 → 347/247/221/346/212/200/350/223/235/345/225/206/345/212/241}/02_chapter.svg" +0 -0
  210. /package/skills/ppt-master/templates/layouts//{317/204/302/272/303/246/302/265e/314/200C/314/247/316/246o/314/202/302/245/317/203o/314/200a/314/212/317/203e/314/200i/314/201 → 347/247/221/346/212/200/350/223/235/345/225/206/345/212/241}/02_toc.svg" +0 -0
  211. /package/skills/ppt-master/templates/layouts//{317/204/302/272/303/246/302/265e/314/200C/314/247/316/246o/314/202/302/245/317/203o/314/200a/314/212/317/203e/314/200i/314/201 → 347/247/221/346/212/200/350/223/235/345/225/206/345/212/241}/03_content.svg" +0 -0
  212. /package/skills/ppt-master/templates/layouts//{317/204/302/272/303/246/302/265e/314/200C/314/247/316/246o/314/202/302/245/317/203o/314/200a/314/212/317/203e/314/200i/314/201 → 347/247/221/346/212/200/350/223/235/345/225/206/345/212/241}/04_ending.svg" +0 -0
  213. /package/skills/ppt-master/templates/layouts//{317/204/302/272/303/246/302/265e/314/200C/314/247/316/246o/314/202/302/245/317/203o/314/200a/314/212/317/203e/314/200i/314/201 → 347/247/221/346/212/200/350/223/235/345/225/206/345/212/241}/design_spec.md" +0 -0
  214. /package/skills/ppt-master/templates/layouts/{Technology Blue Business/01_cover.svg → /351/207/215/345/272/206/345/244/247/345/255/246/01_cover.svg"} +0 -0
  215. /package/skills/ppt-master/templates/layouts/{Technology Blue Business/02_chapter.svg → /351/207/215/345/272/206/345/244/247/345/255/246/02_chapter.svg"} +0 -0
  216. /package/skills/ppt-master/templates/layouts/{Technology Blue Business/02_toc.svg → /351/207/215/345/272/206/345/244/247/345/255/246/02_toc.svg"} +0 -0
  217. /package/skills/ppt-master/templates/layouts/{Technology Blue Business/03_content.svg → /351/207/215/345/272/206/345/244/247/345/255/246/03_content.svg"} +0 -0
  218. /package/skills/ppt-master/templates/layouts/{Technology Blue Business/04_ending.svg → /351/207/215/345/272/206/345/244/247/345/255/246/04_ending.svg"} +0 -0
  219. /package/skills/ppt-master/templates/layouts/{Technology Blue Business/design_spec.md → /351/207/215/345/272/206/345/244/247/345/255/246/design_spec.md"} +0 -0
  220. /package/skills/ppt-master/templates/layouts/{Technology Blue Business//316/230c/314/247i/314/200/317/203/342/225/221a/314/212/317/203n/314/203/302/272/317/203/302/241/302/252logo.png" → /351/207/215/345/272/206/345/244/247/345/255/246//351/207/215/345/272/206/345/244/247/345/255/246logo.png"} +0 -0
  221. /package/skills/ppt-master/templates/layouts/{Technology Blue Business//316/230c/314/247i/314/200/317/203/342/225/221a/314/212/317/203n/314/203/302/272/317/203/302/241/302/252logo2.png" → /351/207/215/345/272/206/345/244/247/345/255/246//351/207/215/345/272/206/345/244/247/345/255/246logo2.png"} +0 -0
@@ -0,0 +1,414 @@
1
+ #!/usr/bin/env node
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const { execSync } = require('child_process');
5
+ const { loadConfig, getCredentials, isConfigValid, runConfigWizard } = require('./config-manager');
6
+
7
+ // --- Load Config ---
8
+ const CONFIG = loadConfig();
9
+ const CREDENTIALS = getCredentials(CONFIG);
10
+
11
+ // --- State File ---
12
+ const STATE_FILE = path.join(__dirname, 'last-fetch-state.json');
13
+ const OUTPUT_MD = path.join(__dirname, 'raw-messages.md');
14
+ const OUTPUT_JSON = path.join(__dirname, 'raw-messages.json');
15
+
16
+ // --- Utils: Format Time ---
17
+ function formatTime(timestamp) {
18
+ const date = new Date(parseInt(timestamp));
19
+ return date.toLocaleString('zh-CN', {
20
+ year: 'numeric',
21
+ month: '2-digit',
22
+ day: '2-digit',
23
+ hour: '2-digit',
24
+ minute: '2-digit',
25
+ second: '2-digit'
26
+ });
27
+ }
28
+
29
+ function formatTimeShort(timestamp) {
30
+ const date = new Date(parseInt(timestamp));
31
+ return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}:${String(date.getSeconds()).padStart(2, '0')}`;
32
+ }
33
+
34
+ // --- Utils: Load/Save State ---
35
+ function loadState() {
36
+ try {
37
+ if (fs.existsSync(STATE_FILE)) {
38
+ return JSON.parse(fs.readFileSync(STATE_FILE, 'utf8'));
39
+ }
40
+ } catch (e) {
41
+ console.warn('Warning: Could not load state file, starting fresh.');
42
+ }
43
+ return null;
44
+ }
45
+
46
+ function saveState(state) {
47
+ fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2), 'utf8');
48
+ }
49
+
50
+ // --- Utils: Extract Message Content ---
51
+ function extractContent(msg) {
52
+ const body = msg.body || {};
53
+ const content = body.content ? JSON.parse(body.content) : {};
54
+
55
+ switch (msg.msg_type) {
56
+ case 'text':
57
+ return content.text || '';
58
+ case 'post':
59
+ if (content.content) {
60
+ return content.content.map(line =>
61
+ line.map(item => item.text || '').join('')
62
+ ).join('<br>');
63
+ }
64
+ return '[富文本消息]';
65
+ case 'interactive':
66
+ return '[卡片消息]';
67
+ case 'system':
68
+ return content.template || '[系统消息]';
69
+ default:
70
+ return `[${msg.msg_type}消息]`;
71
+ }
72
+ }
73
+
74
+ // --- Utils: Fetch User Name from Feishu API ---
75
+ const userNameCache = {};
76
+ function fetchUserName(userId, token) {
77
+ if (userNameCache[userId]) return userNameCache[userId];
78
+ if (!userId || !token) return null;
79
+
80
+ try {
81
+ const res = execSync(`curl -s -X GET 'https://open.feishu.cn/open-apis/contact/v3/users/${userId}' -H 'Authorization: Bearer ${token}'`);
82
+ const data = JSON.parse(res);
83
+ if (data.code === 0 && data.data && data.data.user) {
84
+ const name = data.data.user.name;
85
+ userNameCache[userId] = name;
86
+ return name;
87
+ }
88
+ } catch (e) {
89
+ // API 调用失败,返回 null
90
+ }
91
+ return null;
92
+ }
93
+
94
+ // --- Utils: Fetch Chat Members ---
95
+ const chatMembersCache = {};
96
+ function fetchChatMembers(chatId, token) {
97
+ if (chatMembersCache[chatId]) return chatMembersCache[chatId];
98
+ if (!chatId || !token) return {};
99
+
100
+ try {
101
+ const res = execSync(`curl -s -X GET 'https://open.feishu.cn/open-apis/im/v1/chats/${chatId}/members?page_size=100' -H 'Authorization: Bearer ${token}'`);
102
+ const data = JSON.parse(res);
103
+ if (data.code === 0 && data.data && data.data.items) {
104
+ const members = {};
105
+ data.data.items.forEach(member => {
106
+ if (member.member_id && member.name) {
107
+ members[member.member_id] = member.name;
108
+ }
109
+ });
110
+ chatMembersCache[chatId] = members;
111
+ return members;
112
+ }
113
+ } catch (e) {
114
+ // API 调用失败,返回空对象
115
+ }
116
+ return {};
117
+ }
118
+
119
+ // --- Utils: Get Sender Name ---
120
+ function getSenderName(msg, userMap, token) {
121
+ if (msg.sender.sender_type === 'app') {
122
+ return '机器人';
123
+ }
124
+ if (userMap && userMap[msg.sender.id]) {
125
+ return userMap[msg.sender.id];
126
+ }
127
+ if (msg.mentions && msg.mentions.length > 0) {
128
+ const mention = msg.mentions.find(m => m.id === msg.sender.id);
129
+ if (mention) return mention.name;
130
+ }
131
+ // Try to fetch from API
132
+ if (token && msg.sender.id) {
133
+ const apiName = fetchUserName(msg.sender.id, token);
134
+ if (apiName) return apiName;
135
+ }
136
+ return msg.sender.id ? `用户(${msg.sender.id.slice(-6)})` : '未知用户';
137
+ }
138
+
139
+ // --- Main Data Fetching Logic ---
140
+ async function main() {
141
+ // 检查配置
142
+ if (!isConfigValid(CONFIG)) {
143
+ console.log('⚠️ 首次使用,需要进行配置\n');
144
+ await runConfigWizard();
145
+ console.log('请重新运行脚本');
146
+ process.exit(0);
147
+ }
148
+
149
+ // 验证凭证
150
+ if (!CREDENTIALS.appId || !CREDENTIALS.appSecret) {
151
+ console.error('❌ 未配置飞书应用凭证');
152
+ console.log('请设置环境变量 FEISHU_APP_ID 和 FEISHU_APP_SECRET,或运行配置向导');
153
+ process.exit(1);
154
+ }
155
+
156
+ // Load previous state
157
+ const state = loadState();
158
+ const isFirstRun = !state || !state.lastFetchTime;
159
+
160
+ if (isFirstRun) {
161
+ console.log('🆕 首次运行,将拉取最近 ' + CONFIG.settings.days + ' 天的消息...');
162
+ } else {
163
+ const lastTime = formatTime(state.lastFetchTime);
164
+ console.log('📥 增量模式,上次拉取时间:' + lastTime);
165
+ console.log(' 将只拉取新消息...');
166
+ }
167
+
168
+ // 1. Get Access Token
169
+ let token;
170
+ try {
171
+ const response = execSync(`curl -s -X POST 'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal' -H 'Content-Type: application/json' -d '{"app_id":"${CREDENTIALS.appId}","app_secret":"${CREDENTIALS.appSecret}"}'`, { encoding: 'utf8' });
172
+ const data = JSON.parse(response);
173
+ if (data.code !== 0) throw new Error(data.msg || 'Token API error');
174
+ token = data.tenant_access_token;
175
+ } catch (e) {
176
+ console.error("Fatal: Could not obtain access token.", e.message);
177
+ process.exit(1);
178
+ }
179
+ console.log('✅ Access token obtained.');
180
+
181
+ // 2. 使用配置的监控群聊列表
182
+ const monitoredChats = CONFIG.monitoredChats;
183
+ if (monitoredChats.length === 0) {
184
+ console.error('❌ 未配置监控群聊');
185
+ process.exit(1);
186
+ }
187
+ console.log(`✅ 监控 ${monitoredChats.length} 个群聊.`);
188
+
189
+ // Calculate start time
190
+ let startTimeMs;
191
+ if (isFirstRun) {
192
+ startTimeMs = Date.now() - CONFIG.settings.days * 24 * 60 * 60 * 1000;
193
+ } else {
194
+ startTimeMs = state.lastFetchTime;
195
+ }
196
+
197
+ // Build user map from state or empty
198
+ let userMap = state ? (state.userMap || {}) : {};
199
+ const existingMessages = state ? (state.messages || {}) : {};
200
+ const messagesByChat = {}; // New messages only
201
+ let latestMessageTime = startTimeMs;
202
+ let totalNewMessages = 0;
203
+
204
+ // Pre-fetch chat members to build user map
205
+ console.log('\n👥 正在获取群成员信息...');
206
+ for (const chat of monitoredChats) {
207
+ const members = fetchChatMembers(chat.id, token);
208
+ Object.assign(userMap, members);
209
+ }
210
+ console.log(`✅ 已缓存 ${Object.keys(userMap).length} 个用户信息`);
211
+
212
+ for (const chat of monitoredChats) {
213
+ messagesByChat[chat.id] = [];
214
+
215
+ let messageCount = 0;
216
+ let pt = '';
217
+ console.log(`Fetching messages from "${chat.name}"...`);
218
+
219
+ while (true) {
220
+ try {
221
+ // Build URL with from_time for incremental fetch
222
+ let url = `https://open.feishu.cn/open-apis/im/v1/messages?container_id=${chat.id}&container_id_type=chat&page_size=50`;
223
+ if (pt) {
224
+ url += `&page_token=${pt}`;
225
+ }
226
+ // Note: Feishu API uses 'from_time' in seconds
227
+ url += `&from_time=${Math.floor(startTimeMs / 1000)}`;
228
+
229
+ const res = JSON.parse(execSync(`curl -s -X GET "${url}" -H 'Authorization: Bearer ${token}'`));
230
+ if (res.code !== 0) {
231
+ console.warn(`Warning: Could not fetch messages for ${chat.name}. Code: ${res.code}, Msg: ${res.msg}`);
232
+ break;
233
+ }
234
+ const items = res.data.items || [];
235
+ items.forEach(msg => {
236
+ // Skip if already exists
237
+ if (existingMessages[msg.message_id]) return;
238
+
239
+ const senderName = getSenderName(msg, userMap, token);
240
+ if (msg.sender.id && senderName !== '机器人' && !senderName.startsWith('用户(')) {
241
+ userMap[msg.sender.id] = senderName;
242
+ }
243
+
244
+ const msgTime = parseInt(msg.create_time);
245
+ if (msgTime > latestMessageTime) {
246
+ latestMessageTime = msgTime;
247
+ }
248
+
249
+ messagesByChat[chat.id].push({
250
+ message_id: msg.message_id,
251
+ time: msg.create_time,
252
+ sender_type: msg.sender.sender_type,
253
+ sender_id: msg.sender.id,
254
+ sender_name: senderName,
255
+ msg_type: msg.msg_type,
256
+ content: extractContent(msg),
257
+ parent_id: msg.parent_id || null
258
+ });
259
+
260
+ // Mark as existing
261
+ existingMessages[msg.message_id] = true;
262
+ });
263
+ messageCount += items.length;
264
+ if (!res.data.has_more) break;
265
+ pt = res.data.page_token;
266
+ } catch(e) {
267
+ console.error(`Error fetching messages for ${chat.name}:`, e.message);
268
+ break;
269
+ }
270
+ }
271
+ console.log(` -> Fetched ${messageCount} messages (${messagesByChat[chat.id].length} new).`);
272
+ totalNewMessages += messagesByChat[chat.id].length;
273
+ }
274
+
275
+ console.log(`\n📊 本次共获取 ${totalNewMessages} 条新消息`);
276
+
277
+ if (totalNewMessages === 0) {
278
+ console.log('✅ 没有新消息,无需更新。');
279
+ // Still update last fetch time
280
+ saveState({
281
+ lastFetchTime: latestMessageTime,
282
+ userMap: userMap,
283
+ messages: existingMessages,
284
+ chatList: monitoredChats
285
+ });
286
+ return;
287
+ }
288
+
289
+ // 3. Generate/Update Markdown Report
290
+ let mdContent;
291
+ let allMessagesByChat = {};
292
+
293
+ if (isFirstRun || !fs.existsSync(OUTPUT_MD)) {
294
+ // First run - create new file
295
+ mdContent = `# 飞书群聊消息原始数据\n\n`;
296
+ mdContent += `**首次生成时间:** ${new Date().toLocaleString('zh-CN')}\n\n`;
297
+ mdContent += `**群聊数量:** ${monitoredChats.length} 个\n\n`;
298
+ mdContent += `**模式:** 增量拉取(首次全量)\n\n`;
299
+ mdContent += `---\n\n`;
300
+
301
+ // Use fetched messages as all messages
302
+ for (const chatId in messagesByChat) {
303
+ allMessagesByChat[chatId] = messagesByChat[chatId];
304
+ }
305
+ } else {
306
+ // Incremental - need to merge with existing
307
+ console.log('\n📝 合并新消息到现有报告...');
308
+
309
+ // Parse existing markdown to extract messages (simplified approach)
310
+ // For now, we'll append new sections
311
+ mdContent = fs.readFileSync(OUTPUT_MD, 'utf8');
312
+
313
+ // Remove trailing --- if exists
314
+ mdContent = mdContent.replace(/---\n\n$/, '');
315
+
316
+ // Add update timestamp
317
+ mdContent += `\n\n## 🔄 增量更新 ${new Date().toLocaleString('zh-CN')}\n\n`;
318
+ mdContent += `**本次新增消息:** ${totalNewMessages} 条\n\n`;
319
+
320
+ allMessagesByChat = messagesByChat; // Only show new messages in this update section
321
+ }
322
+
323
+ // Generate table for each chat
324
+ for (const chatId in allMessagesByChat) {
325
+ const chat = monitoredChats.find(c => c.id === chatId);
326
+ const chatName = chat?.name || '未知群聊';
327
+ let messages = allMessagesByChat[chatId];
328
+
329
+ if (messages.length === 0) continue;
330
+
331
+ messages.sort((a, b) => parseInt(a.time) - parseInt(b.time));
332
+
333
+ if (isFirstRun) {
334
+ mdContent += `## 📁 ${chatName}\n\n`;
335
+ mdContent += `**群聊ID:** \`${chatId}\`\n\n`;
336
+ } else {
337
+ mdContent += `### 📁 ${chatName}(新增)\n\n`;
338
+ }
339
+
340
+ mdContent += `| 序号 | 时间 | 发送者 | 类型 | 消息内容 |\n`;
341
+ mdContent += `|------|------|--------|------|----------|\n`;
342
+
343
+ // Group consecutive messages from same sender
344
+ const groupedMessages = [];
345
+ let currentGroup = null;
346
+
347
+ messages.forEach((msg, index) => {
348
+ const timeShort = formatTimeShort(msg.time);
349
+ const content = msg.content.replace(/\|/g, '\\|').replace(/\n/g, '<br>');
350
+
351
+ if (msg.sender_type === 'user' && msg.msg_type === 'text' &&
352
+ currentGroup && currentGroup.sender_id === msg.sender_id &&
353
+ index > 0 && messages[index - 1].sender_id === msg.sender_id) {
354
+ currentGroup.messages.push({ time: timeShort, content });
355
+ } else {
356
+ currentGroup = {
357
+ sender_id: msg.sender_id,
358
+ sender_name: msg.sender_name,
359
+ msg_type: msg.msg_type,
360
+ messages: [{ time: timeShort, content }]
361
+ };
362
+ groupedMessages.push(currentGroup);
363
+ }
364
+ });
365
+
366
+ groupedMessages.forEach((group, groupIndex) => {
367
+ if (group.messages.length === 1) {
368
+ const msg = group.messages[0];
369
+ mdContent += `| ${groupIndex + 1} | ${msg.time} | ${group.sender_name} | ${group.msg_type} | ${msg.content} |\n`;
370
+ } else {
371
+ const times = group.messages.map(m => m.time).join(' → ');
372
+ const contents = group.messages.map(m => m.content).join(' → ');
373
+ mdContent += `| ${groupIndex + 1} | ${times} | ${group.sender_name} | ${group.msg_type} | ${contents} |\n`;
374
+ }
375
+ });
376
+
377
+ const userMessages = messages.filter(m => m.sender_type === 'user' && m.msg_type === 'text');
378
+ if (userMessages.length > 1) {
379
+ const timeLine = userMessages.map(m => formatTimeShort(m.time)).join(' → ');
380
+ mdContent += `| ⏱️ | **时间线** | - | - | ${timeLine} |\n`;
381
+ }
382
+
383
+ mdContent += `\n**消息数量:** ${messages.length} 条\n\n`;
384
+ mdContent += `---\n\n`;
385
+ }
386
+
387
+ // Save Markdown
388
+ fs.writeFileSync(OUTPUT_MD, mdContent, 'utf8');
389
+ console.log(`✅ Markdown report saved to ${OUTPUT_MD}`);
390
+
391
+ // Save state
392
+ saveState({
393
+ lastFetchTime: latestMessageTime,
394
+ userMap: userMap,
395
+ messages: existingMessages,
396
+ chatList: monitoredChats
397
+ });
398
+ console.log(`✅ State saved (last fetch: ${formatTime(latestMessageTime)})`);
399
+
400
+ // Also save JSON for backward compatibility
401
+ const allMessages = Object.values(messagesByChat).flat();
402
+ const jsonOutput = {
403
+ updateTime: new Date().toISOString(),
404
+ isIncremental: !isFirstRun,
405
+ newMessagesCount: totalNewMessages,
406
+ chats: monitoredChats,
407
+ messages: allMessages
408
+ };
409
+ fs.writeFileSync(OUTPUT_JSON, JSON.stringify(jsonOutput, null, 2), 'utf8');
410
+
411
+ console.log('\n✅ 增量拉取完成!');
412
+ }
413
+
414
+ main();
@@ -0,0 +1,241 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { execSync } = require('child_process');
4
+ const readline = require('readline');
5
+
6
+ const CONFIG_FILE = path.join(__dirname, '..', 'config.json');
7
+
8
+ // 默认配置
9
+ const DEFAULT_CONFIG = {
10
+ feishu: {
11
+ appId: '',
12
+ appSecret: ''
13
+ },
14
+ monitoredChats: [], // { id, name }
15
+ pushTarget: {
16
+ type: 'chat', // 'chat' | 'user' | 'both'
17
+ chatId: '',
18
+ userId: ''
19
+ },
20
+ settings: {
21
+ days: 7,
22
+ incrementMode: true
23
+ }
24
+ };
25
+
26
+ // 加载配置
27
+ function loadConfig() {
28
+ try {
29
+ if (fs.existsSync(CONFIG_FILE)) {
30
+ return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
31
+ }
32
+ } catch (e) {
33
+ console.warn('⚠️ 配置文件读取失败,使用默认配置');
34
+ }
35
+ return { ...DEFAULT_CONFIG };
36
+ }
37
+
38
+ // 保存配置
39
+ function saveConfig(config) {
40
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf8');
41
+ console.log(`✅ 配置已保存到: ${CONFIG_FILE}`);
42
+ }
43
+
44
+ // 检查配置是否完整
45
+ function isConfigValid(config) {
46
+ return config.feishu.appId &&
47
+ config.feishu.appSecret &&
48
+ config.monitoredChats.length > 0 &&
49
+ (config.pushTarget.chatId || config.pushTarget.userId);
50
+ }
51
+
52
+ // 获取环境变量或配置的凭证
53
+ function getCredentials(config) {
54
+ return {
55
+ appId: process.env.FEISHU_APP_ID || config.feishu.appId,
56
+ appSecret: process.env.FEISHU_APP_SECRET || config.feishu.appSecret
57
+ };
58
+ }
59
+
60
+ // 交互式配置向导
61
+ async function runConfigWizard() {
62
+ const rl = readline.createInterface({
63
+ input: process.stdin,
64
+ output: process.stdout
65
+ });
66
+
67
+ const question = (prompt) => new Promise(resolve => rl.question(prompt, resolve));
68
+
69
+ console.log('\n🚀 Risk Alert 首次配置向导\n');
70
+ console.log('═══════════════════════════════════════\n');
71
+
72
+ const config = loadConfig();
73
+ const credentials = getCredentials(config);
74
+
75
+ // Step 1: 配置飞书应用凭证
76
+ console.log('📋 Step 1: 飞书应用凭证');
77
+ console.log('─────────────────────────────────────');
78
+
79
+ if (!credentials.appId) {
80
+ config.feishu.appId = await question('请输入飞书 App ID: ');
81
+ } else {
82
+ console.log(`✓ App ID: ${credentials.appId}`);
83
+ const change = await question('是否修改? (y/N): ');
84
+ if (change.toLowerCase() === 'y') {
85
+ config.feishu.appId = await question('请输入飞书 App ID: ');
86
+ } else {
87
+ config.feishu.appId = credentials.appId;
88
+ }
89
+ }
90
+
91
+ if (!credentials.appSecret) {
92
+ config.feishu.appSecret = await question('请输入飞书 App Secret: ');
93
+ } else {
94
+ console.log(`✓ App Secret: ${'*'.repeat(credentials.appSecret.length)}`);
95
+ const change = await question('是否修改? (y/N): ');
96
+ if (change.toLowerCase() === 'y') {
97
+ config.feishu.appSecret = await question('请输入飞书 App Secret: ');
98
+ } else {
99
+ config.feishu.appSecret = credentials.appSecret;
100
+ }
101
+ }
102
+
103
+ // 测试凭证有效性
104
+ console.log('\n🔄 正在验证凭证...');
105
+ const token = await testCredentials(config.feishu.appId, config.feishu.appSecret);
106
+ if (!token) {
107
+ console.error('❌ 凭证验证失败,请检查 App ID 和 App Secret');
108
+ rl.close();
109
+ process.exit(1);
110
+ }
111
+ console.log('✅ 凭证验证通过\n');
112
+
113
+ // Step 2: 选择监控群聊
114
+ console.log('📋 Step 2: 选择监控群聊');
115
+ console.log('─────────────────────────────────────');
116
+
117
+ const chats = await fetchChatList(token);
118
+ if (chats.length === 0) {
119
+ console.error('❌ 机器人未加入任何群聊');
120
+ rl.close();
121
+ process.exit(1);
122
+ }
123
+
124
+ console.log(`\n📋 发现机器人已加入 ${chats.length} 个群聊:`);
125
+ chats.forEach((chat, idx) => {
126
+ console.log(` ${idx + 1}. ${chat.name} (${chat.chat_id})`);
127
+ });
128
+
129
+ // 默认监控所有群聊,但让用户确认
130
+ console.log(`\n📋 默认将监控以上所有 ${chats.length} 个群聊`);
131
+ const selected = await question('\n请确认或修改:\n - 直接回车确认监控所有群聊\n - 输入序号选择特定群聊(多个用逗号分隔)\n - 输入 none 取消选择\n\n您的选择 [默认: all]: ');
132
+
133
+ if (selected.trim() === '' || selected.toLowerCase() === 'all') {
134
+ // 默认选择所有
135
+ config.monitoredChats = chats.map(c => ({ id: c.chat_id, name: c.name }));
136
+ console.log(`✅ 已选择所有 ${config.monitoredChats.length} 个群聊进行监控\n`);
137
+ } else if (selected.toLowerCase() === 'none') {
138
+ config.monitoredChats = [];
139
+ console.log('⚠️ 未选择任何群聊\n');
140
+ } else {
141
+ // 选择特定群聊
142
+ const indices = selected.split(',').map(s => parseInt(s.trim()) - 1).filter(i => i >= 0 && i < chats.length);
143
+ config.monitoredChats = indices.map(i => ({ id: chats[i].chat_id, name: chats[i].name }));
144
+ console.log(`✅ 已选择 ${config.monitoredChats.length} 个群聊进行监控\n`);
145
+ }
146
+
147
+ // Step 3: 选择推送目标
148
+ console.log('📋 Step 3: 选择推送目标');
149
+ console.log('─────────────────────────────────────');
150
+ console.log('1. 推送到指定群聊');
151
+ console.log('2. 推送到指定用户');
152
+ console.log('3. 同时推送');
153
+
154
+ const pushType = await question('\n请输入选择(1/2/3):');
155
+
156
+ switch (pushType) {
157
+ case '1':
158
+ config.pushTarget.type = 'chat';
159
+ const chatIdx = await question('请选择推送群聊(输入序号):');
160
+ const idx = parseInt(chatIdx) - 1;
161
+ if (idx >= 0 && idx < chats.length) {
162
+ config.pushTarget.chatId = chats[idx].chat_id;
163
+ console.log(`✅ 将推送到群聊: ${chats[idx].name}`);
164
+ }
165
+ break;
166
+ case '2':
167
+ config.pushTarget.type = 'user';
168
+ config.pushTarget.userId = await question('请输入用户 Open ID: ');
169
+ console.log(`✅ 将推送到用户: ${config.pushTarget.userId}`);
170
+ break;
171
+ case '3':
172
+ config.pushTarget.type = 'both';
173
+ const chatIdx2 = await question('请选择推送群聊(输入序号):');
174
+ const idx2 = parseInt(chatIdx2) - 1;
175
+ if (idx2 >= 0 && idx2 < chats.length) {
176
+ config.pushTarget.chatId = chats[idx2].chat_id;
177
+ }
178
+ config.pushTarget.userId = await question('请输入用户 Open ID: ');
179
+ console.log(`✅ 将同时推送到群聊和用户`);
180
+ break;
181
+ default:
182
+ console.log('⚠️ 无效选择,默认推送到第一个监控群聊');
183
+ config.pushTarget.type = 'chat';
184
+ config.pushTarget.chatId = config.monitoredChats[0]?.id || '';
185
+ }
186
+
187
+ // Step 4: 其他设置
188
+ console.log('\n📋 Step 4: 其他设置');
189
+ console.log('─────────────────────────────────────');
190
+ const days = await question(`首次拉取天数(默认 ${config.settings.days}):`);
191
+ if (days) config.settings.days = parseInt(days) || 7;
192
+
193
+ rl.close();
194
+
195
+ // 保存配置
196
+ saveConfig(config);
197
+
198
+ console.log('\n═══════════════════════════════════════');
199
+ console.log('✅ 配置完成!可以开始使用 Risk Alert 了。');
200
+ console.log('═══════════════════════════════════════\n');
201
+
202
+ return config;
203
+ }
204
+
205
+ // 测试凭证
206
+ async function testCredentials(appId, appSecret) {
207
+ try {
208
+ const res = execSync(`curl -s -X POST 'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal' \
209
+ -H 'Content-Type: application/json' \
210
+ -d '{"app_id":"${appId}","app_secret":"${appSecret}"}'`, { encoding: 'utf8' });
211
+ const data = JSON.parse(res);
212
+ return data.code === 0 ? data.tenant_access_token : null;
213
+ } catch (e) {
214
+ return null;
215
+ }
216
+ }
217
+
218
+ // 获取群聊列表
219
+ async function fetchChatList(token) {
220
+ try {
221
+ const res = execSync(`curl -s -X GET 'https://open.feishu.cn/open-apis/im/v1/chats?page_size=100' \
222
+ -H 'Authorization: Bearer ${token}'`, { encoding: 'utf8' });
223
+ const data = JSON.parse(res);
224
+ if (data.code === 0 && data.data && data.data.items) {
225
+ return data.data.items.filter(c => c.name);
226
+ }
227
+ } catch (e) {
228
+ console.error('获取群聊列表失败:', e.message);
229
+ }
230
+ return [];
231
+ }
232
+
233
+ module.exports = {
234
+ loadConfig,
235
+ saveConfig,
236
+ isConfigValid,
237
+ getCredentials,
238
+ runConfigWizard,
239
+ testCredentials,
240
+ fetchChatList
241
+ };