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.
- package/bin/install-lib.mjs +11 -0
- package/bin/install.mjs +59 -1
- package/package.json +12 -1
- package/skills/acceptance-doc-entry/SKILL.md +54 -1
- package/skills/ppt-master/SKILL.md +17 -390
- package/skills/ppt-master/projects/.gitkeep +0 -0
- package/skills/ppt-master/references/executor-base.md +4 -3
- package/skills/ppt-master/references/image-generator.md +6 -93
- package/skills/ppt-master/references/strategist.md +5 -8
- package/skills/ppt-master/scripts/README.md +1 -1
- package/skills/ppt-master/scripts/finalize_svg.py +0 -14
- package/skills/ppt-master/scripts/svg_quality_checker.py +8 -169
- package/skills/ppt-master/scripts/svg_to_pptx/pptx_builder.py +1 -7
- package/skills/ppt-master/scripts/svg_to_pptx/pptx_cli.py +0 -14
- package/skills/risk-alert/SKILL.md +110 -0
- package/skills/risk-alert/config.example.json +21 -0
- package/skills/risk-alert/scheduler.sh +74 -0
- package/skills/risk-alert/scripts/analyze-risk.js +414 -0
- package/skills/risk-alert/scripts/config-manager.js +241 -0
- package/skills/risk-alert/scripts/push-beautiful.js +225 -0
- package/skills/risk-alert/scripts/risk-alert.js +136 -0
- package/skills/risk-alert/scripts/run-full.js +60 -0
- package/skills/risk-alert/trigger-risk-alert.sh +35 -0
- package/skills/ppt-master/.env.example +0 -35
- package/skills/ppt-master/README.md +0 -90
- package/skills/ppt-master/audit_report_model_perf.md +0 -213
- package/skills/ppt-master/audit_report_portability.md +0 -90
- package/skills/ppt-master/audit_report_robustness.md +0 -192
- package/skills/ppt-master/audit_report_workflow.md +0 -233
- package/skills/ppt-master/fix_report_bid.md +0 -51
- package/skills/ppt-master/fix_report_scripts.md +0 -41
- package/skills/ppt-master/references/bid-content-example-netease.md +0 -815
- package/skills/ppt-master/references/bid-content-template.md +0 -390
- package/skills/ppt-master/references/bid-example-netease.md +0 -339
- package/skills/ppt-master/references/bid-presentation.md +0 -384
- package/skills/ppt-master/references/bid-svgs/03_toc.svg +0 -38
- package/skills/ppt-master/references/bid-svgs/04_chapter_company.svg +0 -7
- package/skills/ppt-master/references/bid-svgs/05_company_overview.svg +0 -28
- package/skills/ppt-master/references/bid-svgs/06_vision_business.svg +0 -45
- package/skills/ppt-master/references/bid-svgs/07_product_system.svg +0 -52
- package/skills/ppt-master/references/bid-svgs/08_codewave_timeline.svg +0 -29
- package/skills/ppt-master/references/bid-svgs/09_certifications.svg +0 -33
- package/skills/ppt-master/references/bid-svgs/10_client_logos.svg +0 -25
- package/skills/ppt-master/references/bid-svgs/11_brand_mission.svg +0 -14
- package/skills/ppt-master/references/bid-svgs/12_chapter_demand.svg +0 -7
- package/skills/ppt-master/references/bid-svgs/16_chapter_advantages.svg +0 -7
- package/skills/ppt-master/references/bid-svgs/18_platform_arch.svg +0 -24
- package/skills/ppt-master/references/bid-svgs/19_function_matrix.svg +0 -35
- package/skills/ppt-master/references/bid-svgs/20_dev_arch.svg +0 -29
- package/skills/ppt-master/references/bid-svgs/21_source_code.svg +0 -25
- package/skills/ppt-master/references/bid-svgs/22_ai_dev.svg +0 -27
- package/skills/ppt-master/references/bid-svgs/23_asset_market.svg +0 -33
- package/skills/ppt-master/references/bid-svgs/24_multi_org.svg +0 -28
- package/skills/ppt-master/references/bid-svgs/25_i18n.svg +0 -22
- package/skills/ppt-master/references/bid-svgs/26_multi_tenant.svg +0 -25
- package/skills/ppt-master/references/bid-svgs/27_integration.svg +0 -27
- package/skills/ppt-master/references/bid-svgs/28_permission.svg +0 -31
- package/skills/ppt-master/references/bid-svgs/29_security.svg +0 -25
- package/skills/ppt-master/references/bid-svgs/30_ai_capabilities.svg +0 -21
- package/skills/ppt-master/references/bid-svgs/31_product_combo.svg +0 -25
- package/skills/ppt-master/references/bid-svgs/32_digital_transform.svg +0 -28
- package/skills/ppt-master/references/bid-svgs/33_chapter_cases.svg +0 -7
- package/skills/ppt-master/references/bid-svgs/34_cummins_case.svg +0 -23
- package/skills/ppt-master/references/bid-svgs/35_cpq_cost.svg +0 -22
- package/skills/ppt-master/references/bid-svgs/36_tech_platform.svg +0 -18
- package/skills/ppt-master/references/bid-svgs/37_platform_value.svg +0 -24
- package/skills/ppt-master/references/bid-svgs/38_operation_plan.svg +0 -25
- package/skills/ppt-master/references/bid-svgs/39_zpmc_case.svg +0 -21
- package/skills/ppt-master/references/bid-svgs/40_zpmc_apps.svg +0 -23
- package/skills/ppt-master/references/bid-svgs/41_chapter_service.svg +0 -7
- package/skills/ppt-master/references/bid-svgs/42_tech_support.svg +0 -24
- package/skills/ppt-master/references/bid-svgs/43_knowledge_transfer.svg +0 -25
- package/skills/ppt-master/references/bid-svgs/44_operation_promotion.svg +0 -20
- package/skills/ppt-master/references/bid-svgs/45_custom_dev_guide.svg +0 -24
- package/skills/ppt-master/references/bid-svgs/46_sla_guarantee.svg +0 -18
- package/skills/ppt-master/references/bid-svgs/47_strategic_cooperation.svg +0 -25
- package/skills/ppt-master/references/bid-svgs/48_ecosystem.svg +0 -24
- package/skills/ppt-master/references/bid-svgs/49_chapter_delivery.svg +0 -23
- package/skills/ppt-master/references/bid-svgs/53_pm_intro.svg +0 -24
- package/skills/ppt-master/references/bid-svgs/54_after_sales.svg +0 -27
- package/skills/ppt-master/references/bid-svgs/55_ending.svg +0 -8
- package/skills/ppt-master/references/company_intro.md +0 -93
- package/skills/ppt-master/references/company_intro_images/slide1_img00.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img01.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img02.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img03.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img04.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img05.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img06.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img07.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img08.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img09.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img10.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img11.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img12.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img13.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img14.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img15.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img16.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img17.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img18.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img19.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img20.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img21.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img22.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img23.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img24.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img25.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img26.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img27.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img28.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide1_img29.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide2_img30.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide2_img31.jpg +0 -0
- package/skills/ppt-master/references/company_intro_images/slide3_img32.png +0 -0
- package/skills/ppt-master/references/company_intro_images/slide3_img33.png +0 -0
- package/skills/ppt-master/references/drawio-integration.md +0 -144
- package/skills/ppt-master/requirements.txt +0 -19
- package/skills/ppt-master/resources/backgrounds/chapter_bg.png +0 -0
- package/skills/ppt-master/resources/backgrounds/company_bg.png +0 -0
- package/skills/ppt-master/resources/backgrounds/cover_bg.png +0 -0
- package/skills/ppt-master/resources/backgrounds/index.json +0 -40
- package/skills/ppt-master/resources/backgrounds/tech_bg.png +0 -0
- package/skills/ppt-master/scripts/__pycache__/config.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/__pycache__/error_helper.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/__pycache__/pptx_animations.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/__pycache__/project_utils.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/arch_diagram.py +0 -370
- package/skills/ppt-master/scripts/bid_init.py +0 -79
- package/skills/ppt-master/scripts/drawio_gen.py +0 -366
- package/skills/ppt-master/scripts/drawio_to_svg.py +0 -458
- package/skills/ppt-master/scripts/image_backends/__pycache__/__init__.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/image_backends/__pycache__/backend_aigw_gemini.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/image_backends/__pycache__/backend_common.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_finalize/__pycache__/__init__.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_finalize/__pycache__/crop_images.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_finalize/__pycache__/embed_icons.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_finalize/__pycache__/embed_images.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_finalize/__pycache__/fix_image_aspect.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_finalize/__pycache__/flatten_tspan.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_finalize/__pycache__/svg_rect_to_path.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_page_gen.py +0 -871
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/__init__.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_context.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_converter.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_elements.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_paths.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_styles.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/drawingml_utils.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_builder.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_cli.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_dimensions.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_discovery.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_media.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_notes.cpython-314.pyc +0 -0
- package/skills/ppt-master/scripts/svg_to_pptx/__pycache__/pptx_slide_xml.cpython-314.pyc +0 -0
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
|
+
};
|