diff --git a/.gitignore b/.gitignore index 0221013..f555324 100644 --- a/.gitignore +++ b/.gitignore @@ -133,6 +133,7 @@ cython_debug/ # Custom logs/ logs2/ +logs3/ .claude/ # Project documentation diff --git a/config/token_cache.json b/config/token_cache.json index d1ac217..86626e0 100644 --- a/config/token_cache.json +++ b/config/token_cache.json @@ -1,4 +1,4 @@ { - "access_token": "aAYJlq4Ueq_gF7-Rx8vfQixphAVb716T9Y6Baixp5zM-3tdA0dDXpEtacGI4DRSuDqzNC5x6Dq0NomwJvo5Glq6lwMvi9gCxV9Eu3FxGpk6AELfduZ8wRPTMJrOy4Y4-q0Xk_MLbUTtVlps5TXuMiDFNYVnCu6-DKONMgKarVPJQrlLa84QO1muTJIk_mg-BLCpf5XLEWKNgwbtyLTrO39RBoAtCRNYcqxFywJUrxIs", - "fetch_time": 1773296159.0088081 + "access_token": "28gECNTAJqbnb4w4V8SfXKq6EIBaamsIFwrqNy0KkABSCPzZz5BWERID2zg9JXGKnr48Ug3GDqAy46EdY047BQocXTUNdMgFCmzHZHgPJxfKtbqr_4pAq_tGvX5TfWVWbQxu-ZjnNsNLoGef7nLW4E-ykYzRQ-I7qqIZwePYevo6-Q_ezF0ImXDyD9I_LOXmQZ9vuGFWsjYGFddKBQ8bSfePtudJvio5ILiuRgwUsXc", + "fetch_time": 1773373351.2013075 } \ No newline at end of file diff --git a/config3/config.ini b/config3/config.ini index dc0c583..6ad6072 100644 --- a/config3/config.ini +++ b/config3/config.ini @@ -6,7 +6,11 @@ push_time = 15:35 skip_weekend = true [WeWork] +# 测试 webhook_url = https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=026670e0-9e8d-4f61-9b3f-1f81365954ff +# 实际 +# webhook_url = https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=20dd49fb-7663-4e16-98fe-f0e71d8126fb + [Smartsheet] # 技术组成员配置表 diff --git a/logs3/api_log_2026-03-12.jsonl b/logs3/api_log_2026-03-12.jsonl index dc95c01..b5bb8aa 100644 --- a/logs3/api_log_2026-03-12.jsonl +++ b/logs3/api_log_2026-03-12.jsonl @@ -95,3 +95,27 @@ {"event_type": "api_call", "timestamp": "2026-03-12 15:35:03", "task": "task3", "sync_id": "task3_20260312_153500_8f56d85c", "module": "tapd", "operation": "bugs", "success": true, "request": {"workspace_id": "58335167", "current_owner": "星渊", "fields": "id,title,deadline,status"}, "response": {"status": 1, "data": [{"Bug": {"id": "1158335167002296797", "title": "优化性能,分辨率提升为1080P,静态帧率基本稳定60fps", "deadline": "2025-12-22", "status": "closed"}}], "info": "success"}, "error_message": null, "duration_ms": null, "extra": {}} {"event_type": "api_call", "timestamp": "2026-03-12 15:35:03", "task": "task3", "sync_id": "task3_20260312_153500_8f56d85c", "module": "wework", "operation": "webhook/send", "success": true, "request": {"msgtype": "markdown", "markdown": {"content": "⏰ TAPD 过期单提醒(2026-03-12)\n\n\n<@xingyuan>(13 条过期)\n1.【需求】AS脚本Bind扩展 | 过期 37 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839302)\n2.【需求】尝试AS写Validator | 过期 37 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839301)\n3.【需求】Data Validator框架 | 过期 37 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839234)\n4.【需求】材质Mask模式不受motion blur影响 | 过期 35 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839323)\n5.【需求】Kawaii,Magic,AnimVelet调研 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839272)\n6.【需求】Chaos布料算法调研 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839270)\n7.【需求】编译Android版 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839267)\n8.【需求】对GPU解算进行改近 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839266)\n9.【需求】理解GPU算法 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839259)\n10.【需求】调试和理解NvCloth算法 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839246)\n11.【需求】NvCloth调研 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839241)\n12.【需求】布料解算基建 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839239)\n13.【需求】3C-基础移动-Bug-Dodge卡帧 | 过期 2 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004866823)\n\n\n========================\n共 13 条过期单,请今日内更新状态 🙏"}, "mentioned_list": ["xingyuan"]}, "response": {"errcode": 0, "errmsg": "ok"}, "error_message": null, "duration_ms": null, "extra": {}} {"event_type": "end_sync", "timestamp": "2026-03-12 15:35:03", "task": "task3", "sync_id": "task3_20260312_153500_8f56d85c", "success": true, "stats": {"overdue_count": 13}, "error_message": null, "extra": {}} +{"event_type": "start_sync", "timestamp": "2026-03-12 16:07:51", "task": "task3", "sync_id": "task3_20260312_160751_c8a45221", "trigger": "manual", "metadata": {}} +{"event_type": "api_call", "timestamp": "2026-03-12 16:07:53", "task": "task3", "sync_id": "task3_20260312_160751_c8a45221", "module": "tapd", "operation": "stories", "success": true, "request": {"workspace_id": "58335167", "owner": "星渊", "fields": "id,name,due,status"}, "response": {"status": 1, "data": [{"Story": {"id": "1158335167004866823", "name": "3C-基础移动-Bug-Dodge卡帧", "due": "2026-03-10", "status": "status_9"}}, {"Story": {"id": "1158335167004839323", "name": "材质Mask模式不受motion blur影响", "due": "2026-02-05", "status": "status_9"}}, {"Story": {"id": "1158335167004839302", "name": "AS脚本Bind扩展", "due": "2026-02-03", "status": "status_10"}}, {"Story": {"id": "1158335167004839301", "name": "尝试AS写Validator", "due": "2026-02-03", "status": "status_10"}}, {"Story": {"id": "1158335167004839299", "name": "简单Sample跑下流程", "due": "2026-02-03", "status": "status_8"}}, {"Story": {"id": "1158335167004839298", "name": "文档阅读", "due": "2026-02-03", "status": "status_8"}}, {"Story": {"id": "1158335167004839272", "name": "Kawaii,Magic,AnimVelet调研", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839270", "name": "Chaos布料算法调研", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839267", "name": "编译Android版", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839266", "name": "对GPU解算进行改近", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839263", "name": "育碧GPU布料结算的GDC Paper", "due": "2026-02-13", "status": "status_8"}}, {"Story": {"id": "1158335167004839259", "name": "理解GPU算法", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839251", "name": "增加Profiler", "due": "2026-02-13", "status": "status_8"}}, {"Story": {"id": "1158335167004839246", "name": "调试和理解NvCloth算法", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839242", "name": "编译NvCloth", "due": "2026-02-13", "status": "status_8"}}, {"Story": {"id": "1158335167004839241", "name": "NvCloth调研", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839239", "name": "布料解算基建", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839234", "name": "Data Validator框架", "due": "2026-02-03", "status": "status_5"}}, {"Story": {"id": "1158335167004834466", "name": "Horde相关-持续开发项", "due": "2026-05-30", "status": "status_5"}}, {"Story": {"id": "1158335167004834018", "name": "【引擎/TA】2026年1月 - 凡人各组月度产出梳理", "due": "2026-01-28", "status": "status_8"}}, {"Story": {"id": "1158335167004828309", "name": "技术基建-基础可过滤编辑器控件开发", "due": null, "status": "status_8"}}, {"Story": {"id": "1158335167004802628", "name": "技术基建-XianDebugger-法术场Debugger", "due": null, "status": "status_9"}}, {"Story": {"id": "1158335167004802626", "name": "技术基建-XianDebugger", "due": null, "status": "status_5"}}, {"Story": {"id": "1158335167004800837", "name": "VAT贴图Foramt以及各种选项的意义", "due": null, "status": "status_7"}}, {"Story": {"id": "1158335167004800834", "name": "VAT贴图尺寸和种类优化", "due": null, "status": "status_8"}}, {"Story": {"id": "1158335167004800832", "name": "VAT内存优化", "due": null, "status": "status_7"}}, {"Story": {"id": "1158335167004800829", "name": "内存Profiling", "due": null, "status": "status_8"}}, {"Story": {"id": "1158335167004800828", "name": "内存优化", "due": null, "status": "status_8"}}, {"Story": {"id": "1158335167004800819", "name": "Async优化Lumen反射", "due": null, "status": "status_8"}}, {"Story": {"id": "1158335167004800816", "name": "优化Lumen反射,包括水的反射性能", "due": null, "status": "status_8"}}], "info": "success"}, "error_message": null, "duration_ms": null, "extra": {}} +{"event_type": "api_call", "timestamp": "2026-03-12 16:07:54", "task": "task3", "sync_id": "task3_20260312_160751_c8a45221", "module": "tapd", "operation": "bugs", "success": true, "request": {"workspace_id": "58335167", "current_owner": "星渊", "fields": "id,title,deadline,status"}, "response": {"status": 1, "data": [{"Bug": {"id": "1158335167002296797", "title": "优化性能,分辨率提升为1080P,静态帧率基本稳定60fps", "deadline": "2025-12-22", "status": "closed"}}], "info": "success"}, "error_message": null, "duration_ms": null, "extra": {}} +{"event_type": "api_call", "timestamp": "2026-03-12 16:07:54", "task": "task3", "sync_id": "task3_20260312_160751_c8a45221", "module": "wework", "operation": "webhook/send", "success": true, "request": {"msgtype": "markdown", "markdown": {"content": "⏰ TAPD 过期单提醒(2026-03-12)\n\n\n<@xingyuan>(13 条过期)\n1.【需求】AS脚本Bind扩展 | 过期 37 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839302)\n2.【需求】尝试AS写Validator | 过期 37 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839301)\n3.【需求】Data Validator框架 | 过期 37 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839234)\n4.【需求】材质Mask模式不受motion blur影响 | 过期 35 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839323)\n5.【需求】Kawaii,Magic,AnimVelet调研 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839272)\n6.【需求】Chaos布料算法调研 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839270)\n7.【需求】编译Android版 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839267)\n8.【需求】对GPU解算进行改近 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839266)\n9.【需求】理解GPU算法 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839259)\n10.【需求】调试和理解NvCloth算法 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839246)\n11.【需求】NvCloth调研 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839241)\n12.【需求】布料解算基建 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839239)\n13.【需求】3C-基础移动-Bug-Dodge卡帧 | 过期 2 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004866823)\n\n\n========================\n共 13 条过期单,请今日内更新状态 🙏"}, "mentioned_list": ["xingyuan"]}, "response": {"errcode": 0, "errmsg": "ok"}, "error_message": null, "duration_ms": null, "extra": {}} +{"event_type": "end_sync", "timestamp": "2026-03-12 16:07:54", "task": "task3", "sync_id": "task3_20260312_160751_c8a45221", "success": true, "stats": {"overdue_count": 13}, "error_message": null, "extra": {}} +{"event_type": "start_sync", "timestamp": "2026-03-12 16:22:57", "task": "task3", "sync_id": "task3_20260312_162257_935477a8", "trigger": "manual", "metadata": {}} +{"event_type": "api_call", "timestamp": "2026-03-12 16:23:00", "task": "task3", "sync_id": "task3_20260312_162257_935477a8", "module": "tapd", "operation": "stories", "success": true, "request": {"workspace_id": "58335167", "owner": "星渊", "fields": "id,name,due,status"}, "response": {"status": 1, "data": [{"Story": {"id": "1158335167004866823", "name": "3C-基础移动-Bug-Dodge卡帧", "due": "2026-03-10", "status": "status_9"}}, {"Story": {"id": "1158335167004839323", "name": "材质Mask模式不受motion blur影响", "due": "2026-02-05", "status": "status_9"}}, {"Story": {"id": "1158335167004839302", "name": "AS脚本Bind扩展", "due": "2026-02-03", "status": "status_10"}}, {"Story": {"id": "1158335167004839301", "name": "尝试AS写Validator", "due": "2026-02-03", "status": "status_10"}}, {"Story": {"id": "1158335167004839299", "name": "简单Sample跑下流程", "due": "2026-02-03", "status": "status_8"}}, {"Story": {"id": "1158335167004839298", "name": "文档阅读", "due": "2026-02-03", "status": "status_8"}}, {"Story": {"id": "1158335167004839272", "name": "Kawaii,Magic,AnimVelet调研", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839270", "name": "Chaos布料算法调研", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839267", "name": "编译Android版", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839266", "name": "对GPU解算进行改近", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839263", "name": "育碧GPU布料结算的GDC Paper", "due": "2026-02-13", "status": "status_8"}}, {"Story": {"id": "1158335167004839259", "name": "理解GPU算法", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839251", "name": "增加Profiler", "due": "2026-02-13", "status": "status_8"}}, {"Story": {"id": "1158335167004839246", "name": "调试和理解NvCloth算法", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839242", "name": "编译NvCloth", "due": "2026-02-13", "status": "status_8"}}, {"Story": {"id": "1158335167004839241", "name": "NvCloth调研", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839239", "name": "布料解算基建", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839234", "name": "Data Validator框架", "due": "2026-02-03", "status": "status_5"}}, {"Story": {"id": "1158335167004834466", "name": "Horde相关-持续开发项", "due": "2026-05-30", "status": "status_5"}}, {"Story": {"id": "1158335167004834018", "name": "【引擎/TA】2026年1月 - 凡人各组月度产出梳理", "due": "2026-01-28", "status": "status_8"}}, {"Story": {"id": "1158335167004828309", "name": "技术基建-基础可过滤编辑器控件开发", "due": null, "status": "status_8"}}, {"Story": {"id": "1158335167004802628", "name": "技术基建-XianDebugger-法术场Debugger", "due": null, "status": "status_9"}}, {"Story": {"id": "1158335167004802626", "name": "技术基建-XianDebugger", "due": null, "status": "status_5"}}, {"Story": {"id": "1158335167004800837", "name": "VAT贴图Foramt以及各种选项的意义", "due": null, "status": "status_7"}}, {"Story": {"id": "1158335167004800834", "name": "VAT贴图尺寸和种类优化", "due": null, "status": "status_8"}}, {"Story": {"id": "1158335167004800832", "name": "VAT内存优化", "due": null, "status": "status_7"}}, {"Story": {"id": "1158335167004800829", "name": "内存Profiling", "due": null, "status": "status_8"}}, {"Story": {"id": "1158335167004800828", "name": "内存优化", "due": null, "status": "status_8"}}, {"Story": {"id": "1158335167004800819", "name": "Async优化Lumen反射", "due": null, "status": "status_8"}}, {"Story": {"id": "1158335167004800816", "name": "优化Lumen反射,包括水的反射性能", "due": null, "status": "status_8"}}], "info": "success"}, "error_message": null, "duration_ms": null, "extra": {}} +{"event_type": "api_call", "timestamp": "2026-03-12 16:23:00", "task": "task3", "sync_id": "task3_20260312_162257_935477a8", "module": "tapd", "operation": "bugs", "success": true, "request": {"workspace_id": "58335167", "current_owner": "星渊", "fields": "id,title,deadline,status"}, "response": {"status": 1, "data": [{"Bug": {"id": "1158335167002296797", "title": "优化性能,分辨率提升为1080P,静态帧率基本稳定60fps", "deadline": "2025-12-22", "status": "closed"}}], "info": "success"}, "error_message": null, "duration_ms": null, "extra": {}} +{"event_type": "api_call", "timestamp": "2026-03-12 16:23:01", "task": "task3", "sync_id": "task3_20260312_162257_935477a8", "module": "wework", "operation": "webhook/send", "success": true, "request": {"msgtype": "markdown", "markdown": {"content": "⏰ TAPD 过期单提醒(2026-03-12)\n\n\n<@xingyuan>(13 条过期)\n1.【需求】AS脚本Bind扩展 | 过期 37 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839302)\n2.【需求】尝试AS写Validator | 过期 37 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839301)\n3.【需求】Data Validator框架 | 过期 37 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839234)\n4.【需求】材质Mask模式不受motion blur影响 | 过期 35 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839323)\n5.【需求】Kawaii,Magic,AnimVelet调研 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839272)\n6.【需求】Chaos布料算法调研 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839270)\n7.【需求】编译Android版 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839267)\n8.【需求】对GPU解算进行改近 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839266)\n9.【需求】理解GPU算法 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839259)\n10.【需求】调试和理解NvCloth算法 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839246)\n11.【需求】NvCloth调研 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839241)\n12.【需求】布料解算基建 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839239)\n13.【需求】3C-基础移动-Bug-Dodge卡帧 | 过期 2 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004866823)\n\n\n========================\n共 13 条过期单,请今日内更新状态 🙏"}, "mentioned_list": ["xingyuan"]}, "response": {"errcode": 0, "errmsg": "ok"}, "error_message": null, "duration_ms": null, "extra": {}} +{"event_type": "end_sync", "timestamp": "2026-03-12 16:23:01", "task": "task3", "sync_id": "task3_20260312_162257_935477a8", "success": true, "stats": {"overdue_count": 13}, "error_message": null, "extra": {}} +{"event_type": "start_sync", "timestamp": "2026-03-12 16:23:23", "task": "task3", "sync_id": "task3_20260312_162323_eea7a0d6", "trigger": "manual", "metadata": {}} +{"event_type": "api_call", "timestamp": "2026-03-12 16:23:25", "task": "task3", "sync_id": "task3_20260312_162323_eea7a0d6", "module": "tapd", "operation": "stories", "success": true, "request": {"workspace_id": "58335167", "owner": "星渊", "fields": "id,name,due,status"}, "response": {"status": 1, "data": [{"Story": {"id": "1158335167004866823", "name": "3C-基础移动-Bug-Dodge卡帧", "due": "2026-03-10", "status": "status_9"}}, {"Story": {"id": "1158335167004839323", "name": "材质Mask模式不受motion blur影响", "due": "2026-02-05", "status": "status_9"}}, {"Story": {"id": "1158335167004839302", "name": "AS脚本Bind扩展", "due": "2026-02-03", "status": "status_10"}}, {"Story": {"id": "1158335167004839301", "name": "尝试AS写Validator", "due": "2026-02-03", "status": "status_10"}}, {"Story": {"id": "1158335167004839299", "name": "简单Sample跑下流程", "due": "2026-02-03", "status": "status_8"}}, {"Story": {"id": "1158335167004839298", "name": "文档阅读", "due": "2026-02-03", "status": "status_8"}}, {"Story": {"id": "1158335167004839272", "name": "Kawaii,Magic,AnimVelet调研", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839270", "name": "Chaos布料算法调研", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839267", "name": "编译Android版", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839266", "name": "对GPU解算进行改近", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839263", "name": "育碧GPU布料结算的GDC Paper", "due": "2026-02-13", "status": "status_8"}}, {"Story": {"id": "1158335167004839259", "name": "理解GPU算法", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839251", "name": "增加Profiler", "due": "2026-02-13", "status": "status_8"}}, {"Story": {"id": "1158335167004839246", "name": "调试和理解NvCloth算法", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839242", "name": "编译NvCloth", "due": "2026-02-13", "status": "status_8"}}, {"Story": {"id": "1158335167004839241", "name": "NvCloth调研", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839239", "name": "布料解算基建", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839234", "name": "Data Validator框架", "due": "2026-02-03", "status": "status_5"}}, {"Story": {"id": "1158335167004834466", "name": "Horde相关-持续开发项", "due": "2026-05-30", "status": "status_5"}}, {"Story": {"id": "1158335167004834018", "name": "【引擎/TA】2026年1月 - 凡人各组月度产出梳理", "due": "2026-01-28", "status": "status_8"}}, {"Story": {"id": "1158335167004828309", "name": "技术基建-基础可过滤编辑器控件开发", "due": null, "status": "status_8"}}, {"Story": {"id": "1158335167004802628", "name": "技术基建-XianDebugger-法术场Debugger", "due": null, "status": "status_9"}}, {"Story": {"id": "1158335167004802626", "name": "技术基建-XianDebugger", "due": null, "status": "status_5"}}, {"Story": {"id": "1158335167004800837", "name": "VAT贴图Foramt以及各种选项的意义", "due": null, "status": "status_7"}}, {"Story": {"id": "1158335167004800834", "name": "VAT贴图尺寸和种类优化", "due": null, "status": "status_8"}}, {"Story": {"id": "1158335167004800832", "name": "VAT内存优化", "due": null, "status": "status_7"}}, {"Story": {"id": "1158335167004800829", "name": "内存Profiling", "due": null, "status": "status_8"}}, {"Story": {"id": "1158335167004800828", "name": "内存优化", "due": null, "status": "status_8"}}, {"Story": {"id": "1158335167004800819", "name": "Async优化Lumen反射", "due": null, "status": "status_8"}}, {"Story": {"id": "1158335167004800816", "name": "优化Lumen反射,包括水的反射性能", "due": null, "status": "status_8"}}], "info": "success"}, "error_message": null, "duration_ms": null, "extra": {}} +{"event_type": "api_call", "timestamp": "2026-03-12 16:23:25", "task": "task3", "sync_id": "task3_20260312_162323_eea7a0d6", "module": "tapd", "operation": "bugs", "success": true, "request": {"workspace_id": "58335167", "current_owner": "星渊", "fields": "id,title,deadline,status"}, "response": {"status": 1, "data": [{"Bug": {"id": "1158335167002296797", "title": "优化性能,分辨率提升为1080P,静态帧率基本稳定60fps", "deadline": "2025-12-22", "status": "closed"}}], "info": "success"}, "error_message": null, "duration_ms": null, "extra": {}} +{"event_type": "api_call", "timestamp": "2026-03-12 16:23:26", "task": "task3", "sync_id": "task3_20260312_162323_eea7a0d6", "module": "wework", "operation": "webhook/send", "success": true, "request": {"msgtype": "markdown", "markdown": {"content": "⏰ TAPD 过期单提醒(2026-03-12)\n\n\n<@xingyuan>(13 条过期)\n1.【需求】AS脚本Bind扩展 | 过期 37 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839302)\n2.【需求】尝试AS写Validator | 过期 37 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839301)\n3.【需求】Data Validator框架 | 过期 37 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839234)\n4.【需求】材质Mask模式不受motion blur影响 | 过期 35 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839323)\n5.【需求】Kawaii,Magic,AnimVelet调研 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839272)\n6.【需求】Chaos布料算法调研 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839270)\n7.【需求】编译Android版 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839267)\n8.【需求】对GPU解算进行改近 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839266)\n9.【需求】理解GPU算法 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839259)\n10.【需求】调试和理解NvCloth算法 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839246)\n11.【需求】NvCloth调研 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839241)\n12.【需求】布料解算基建 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839239)\n13.【需求】3C-基础移动-Bug-Dodge卡帧 | 过期 2 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004866823)\n\n\n========================\n共 13 条过期单,请今日内更新状态 🙏"}, "mentioned_list": ["xingyuan"]}, "response": {"errcode": 0, "errmsg": "ok"}, "error_message": null, "duration_ms": null, "extra": {}} +{"event_type": "end_sync", "timestamp": "2026-03-12 16:23:26", "task": "task3", "sync_id": "task3_20260312_162323_eea7a0d6", "success": true, "stats": {"overdue_count": 13}, "error_message": null, "extra": {}} +{"event_type": "start_sync", "timestamp": "2026-03-12 16:27:11", "task": "task3", "sync_id": "task3_20260312_162711_4f28fc16", "trigger": "manual", "metadata": {}} +{"event_type": "api_call", "timestamp": "2026-03-12 16:27:14", "task": "task3", "sync_id": "task3_20260312_162711_4f28fc16", "module": "tapd", "operation": "stories", "success": true, "request": {"workspace_id": "58335167", "owner": "星渊", "fields": "id,name,due,status"}, "response": {"status": 1, "data": [{"Story": {"id": "1158335167004866823", "name": "3C-基础移动-Bug-Dodge卡帧", "due": "2026-03-10", "status": "status_9"}}, {"Story": {"id": "1158335167004839323", "name": "材质Mask模式不受motion blur影响", "due": "2026-02-05", "status": "status_9"}}, {"Story": {"id": "1158335167004839302", "name": "AS脚本Bind扩展", "due": "2026-02-03", "status": "status_10"}}, {"Story": {"id": "1158335167004839301", "name": "尝试AS写Validator", "due": "2026-02-03", "status": "status_10"}}, {"Story": {"id": "1158335167004839299", "name": "简单Sample跑下流程", "due": "2026-02-03", "status": "status_8"}}, {"Story": {"id": "1158335167004839298", "name": "文档阅读", "due": "2026-02-03", "status": "status_8"}}, {"Story": {"id": "1158335167004839272", "name": "Kawaii,Magic,AnimVelet调研", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839270", "name": "Chaos布料算法调研", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839267", "name": "编译Android版", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839266", "name": "对GPU解算进行改近", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839263", "name": "育碧GPU布料结算的GDC Paper", "due": "2026-02-13", "status": "status_8"}}, {"Story": {"id": "1158335167004839259", "name": "理解GPU算法", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839251", "name": "增加Profiler", "due": "2026-02-13", "status": "status_8"}}, {"Story": {"id": "1158335167004839246", "name": "调试和理解NvCloth算法", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839242", "name": "编译NvCloth", "due": "2026-02-13", "status": "status_8"}}, {"Story": {"id": "1158335167004839241", "name": "NvCloth调研", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839239", "name": "布料解算基建", "due": "2026-02-13", "status": "status_5"}}, {"Story": {"id": "1158335167004839234", "name": "Data Validator框架", "due": "2026-02-03", "status": "status_5"}}, {"Story": {"id": "1158335167004834466", "name": "Horde相关-持续开发项", "due": "2026-05-30", "status": "status_5"}}, {"Story": {"id": "1158335167004834018", "name": "【引擎/TA】2026年1月 - 凡人各组月度产出梳理", "due": "2026-01-28", "status": "status_8"}}, {"Story": {"id": "1158335167004828309", "name": "技术基建-基础可过滤编辑器控件开发", "due": null, "status": "status_8"}}, {"Story": {"id": "1158335167004802628", "name": "技术基建-XianDebugger-法术场Debugger", "due": null, "status": "status_9"}}, {"Story": {"id": "1158335167004802626", "name": "技术基建-XianDebugger", "due": null, "status": "status_5"}}, {"Story": {"id": "1158335167004800837", "name": "VAT贴图Foramt以及各种选项的意义", "due": null, "status": "status_7"}}, {"Story": {"id": "1158335167004800834", "name": "VAT贴图尺寸和种类优化", "due": null, "status": "status_8"}}, {"Story": {"id": "1158335167004800832", "name": "VAT内存优化", "due": null, "status": "status_7"}}, {"Story": {"id": "1158335167004800829", "name": "内存Profiling", "due": null, "status": "status_8"}}, {"Story": {"id": "1158335167004800828", "name": "内存优化", "due": null, "status": "status_8"}}, {"Story": {"id": "1158335167004800819", "name": "Async优化Lumen反射", "due": null, "status": "status_8"}}, {"Story": {"id": "1158335167004800816", "name": "优化Lumen反射,包括水的反射性能", "due": null, "status": "status_8"}}], "info": "success"}, "error_message": null, "duration_ms": null, "extra": {}} +{"event_type": "api_call", "timestamp": "2026-03-12 16:27:14", "task": "task3", "sync_id": "task3_20260312_162711_4f28fc16", "module": "tapd", "operation": "stories", "success": true, "request": {"workspace_id": "58335167", "owner": "v_linzelong", "fields": "id,name,due,status"}, "response": {"status": 1, "data": [{"Story": {"id": "1158335167004820451", "name": "自动化测试单", "due": null, "status": "status_12"}}], "info": "success"}, "error_message": null, "duration_ms": null, "extra": {}} +{"event_type": "api_call", "timestamp": "2026-03-12 16:27:14", "task": "task3", "sync_id": "task3_20260312_162711_4f28fc16", "module": "tapd", "operation": "bugs", "success": true, "request": {"workspace_id": "58335167", "current_owner": "星渊", "fields": "id,title,deadline,status"}, "response": {"status": 1, "data": [{"Bug": {"id": "1158335167002296797", "title": "优化性能,分辨率提升为1080P,静态帧率基本稳定60fps", "deadline": "2025-12-22", "status": "closed"}}], "info": "success"}, "error_message": null, "duration_ms": null, "extra": {}} +{"event_type": "api_call", "timestamp": "2026-03-12 16:27:14", "task": "task3", "sync_id": "task3_20260312_162711_4f28fc16", "module": "tapd", "operation": "bugs", "success": true, "request": {"workspace_id": "58335167", "current_owner": "v_linzelong", "fields": "id,title,deadline,status"}, "response": {"status": 1, "data": [{"Bug": {"id": "1158335167002296876", "title": "测试状态回写", "deadline": null, "status": "closed"}}, {"Bug": {"id": "1158335167002299210", "title": "测试状态回写", "deadline": null, "status": "rejected"}}, {"Bug": {"id": "1158335167002299743", "title": "多子表测试", "deadline": "2025-12-29", "status": "closed"}}, {"Bug": {"id": "1158335167002300470", "title": "测试描述", "deadline": "2025-12-30", "status": "rejected"}}], "info": "success"}, "error_message": null, "duration_ms": null, "extra": {}} +{"event_type": "api_call", "timestamp": "2026-03-12 16:27:15", "task": "task3", "sync_id": "task3_20260312_162711_4f28fc16", "module": "wework", "operation": "webhook/send", "success": true, "request": {"msgtype": "markdown", "markdown": {"content": "⏰ TAPD 过期单提醒(2026-03-12)\n\n\n<@xingyuan>(13 条过期)\n1.【需求】AS脚本Bind扩展 | 过期 37 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839302)\n2.【需求】尝试AS写Validator | 过期 37 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839301)\n3.【需求】Data Validator框架 | 过期 37 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839234)\n4.【需求】材质Mask模式不受motion blur影响 | 过期 35 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839323)\n5.【需求】Kawaii,Magic,AnimVelet调研 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839272)\n6.【需求】Chaos布料算法调研 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839270)\n7.【需求】编译Android版 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839267)\n8.【需求】对GPU解算进行改近 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839266)\n9.【需求】理解GPU算法 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839259)\n10.【需求】调试和理解NvCloth算法 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839246)\n11.【需求】NvCloth调研 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839241)\n12.【需求】布料解算基建 | 过期 27 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004839239)\n13.【需求】3C-基础移动-Bug-Dodge卡帧 | 过期 2 天 | [查看](https://www.tapd.cn/58335167/prong/stories/view/1158335167004866823)\n\n\n========================\n共 13 条过期单,请今日内更新状态 🙏"}, "mentioned_list": ["xingyuan"]}, "response": {"errcode": 0, "errmsg": "ok"}, "error_message": null, "duration_ms": null, "extra": {}} +{"event_type": "end_sync", "timestamp": "2026-03-12 16:27:15", "task": "task3", "sync_id": "task3_20260312_162711_4f28fc16", "success": true, "stats": {"overdue_count": 13}, "error_message": null, "extra": {}} +{"event_type": "start_sync", "timestamp": "2026-03-12 16:41:15", "task": "task3", "sync_id": "task3_20260312_164115_81d6cac5", "trigger": "manual", "metadata": {}} +{"event_type": "end_sync", "timestamp": "2026-03-12 16:41:16", "task": "task3", "sync_id": "task3_20260312_164115_81d6cac5", "success": false, "stats": {}, "error_message": "白名单错误: 智能表格中没有启用的技术组成员", "extra": {}} diff --git a/src3/main.py b/src3/main.py index c9b9a98..60749bd 100644 --- a/src3/main.py +++ b/src3/main.py @@ -14,6 +14,36 @@ from src3.message_formatter import MessageFormatter from src3.webhook_sender import WebhookSender +def _send_error_notification(webhook_url: str, logger): + """发送错误通知""" + import requests + payload = { + "msgtype": "markdown", + "markdown": { + "content": "⚠️ 今日过期单提醒获取失败,请人工检查" + } + } + try: + requests.post(webhook_url, json=payload, timeout=10) + except: + pass + + +def _send_no_overdue_message(webhook_url: str, logger): + """发送无过期单消息""" + import requests + payload = { + "msgtype": "markdown", + "markdown": { + "content": "✅ 今日程序组无过期单,大家保持!" + } + } + try: + requests.post(webhook_url, json=payload, timeout=10) + except: + pass + + def run_once(): """执行一次过期单推送""" logger = get_task3_logger() @@ -23,31 +53,58 @@ def run_once(): print("任务三:TAPD过期单推送") # 1. 加载配置 - config = Task3ConfigManager() - workspace_id = config.get_workspace_id() - webhook_url = config.get_webhook_url() - tech_team = config.get_tech_team_config() - member_list = tech_team['member_list'] - user_mapping = tech_team['user_mapping'] + try: + config = Task3ConfigManager() + workspace_id = config.get_workspace_id() + webhook_url = config.get_webhook_url() + except ValueError as e: + print(f"✗ 配置错误: {e}") + logger.end_sync_with_stats({}, False, f"配置错误: {e}", sync_id=sync_id) + return + + # 2. 加载技术组配置 + try: + tech_team = config.get_tech_team_config() + member_list = tech_team['member_list'] + user_mapping = tech_team['user_mapping'] + except ValueError as e: + print(f"✗ 白名单为空: {e}") + logger.end_sync_with_stats({}, False, f"白名单错误: {e}", sync_id=sync_id) + return + except Exception as e: + print(f"✗ 获取技术组配置失败: {e}") + logger.end_sync_with_stats({}, False, f"配置加载失败: {e}", sync_id=sync_id) + return + print(f"技术组成员: {member_list}") - # 2. 获取过期单 - fetcher = OverdueFetcher(workspace_id, logger) - items = fetcher.fetch_all_overdue(member_list) + # 3. 获取过期单 + try: + fetcher = OverdueFetcher(workspace_id, logger) + items = fetcher.fetch_all_overdue(member_list) + except Exception as e: + print(f"✗ TAPD API调用失败: {e}") + _send_error_notification(webhook_url, logger) + logger.end_sync_with_stats({}, False, f"TAPD API失败: {e}", sync_id=sync_id) + return if not items: - print("没有过期单") + print("✅ 今日程序组无过期单") + _send_no_overdue_message(webhook_url, logger) logger.end_sync_with_stats({"overdue_count": 0}, True, sync_id=sync_id) return print(f"获取到 {len(items)} 条过期单") - # 3. 格式化消息 - formatter = MessageFormatter() + # 4. 格式化消息 + formatter = MessageFormatter(logger) today = datetime.now().strftime('%Y-%m-%d') content, mentioned_list = formatter.format_message(items, user_mapping, today) - # 4. 发送消息 + if formatter.unmapped_users: + print(f"⚠️ 未映射用户: {', '.join(formatter.unmapped_users)}") + + # 5. 发送消息 sender = WebhookSender(webhook_url, logger) success = sender.send_markdown(content, mentioned_list) @@ -59,9 +116,8 @@ def run_once(): logger.end_sync_with_stats({"overdue_count": len(items)}, False, "推送失败", sync_id=sync_id) except Exception as e: - print(f"执行失败: {e}") - logger.end_sync_with_stats({}, False, str(e), sync_id=sync_id) - raise + print(f"✗ 未知错误: {e}") + logger.end_sync_with_stats({}, False, f"未知错误: {e}", sync_id=sync_id) if __name__ == "__main__": diff --git a/src3/message_formatter.py b/src3/message_formatter.py index 753818a..b4ce48d 100644 --- a/src3/message_formatter.py +++ b/src3/message_formatter.py @@ -6,6 +6,10 @@ from datetime import datetime class MessageFormatter: """消息格式化器""" + def __init__(self, logger=None): + self.logger = logger + self.unmapped_users = set() + def format_message(self, items: List[dict], user_mapping: Dict[str, str], date: str = None) -> tuple: """格式化消息 返回: (markdown_content, mentioned_list) @@ -29,10 +33,17 @@ class MessageFormatter: item_index = 1 for owner, owner_items in sorted_groups: - wework_id = user_mapping.get(owner, owner) - mentioned_list.append(wework_id) + wework_id = user_mapping.get(owner) - lines.append(f"<@{wework_id}>({len(owner_items)} 条过期)") + if wework_id: + mentioned_list.append(wework_id) + lines.append(f"<@{wework_id}>({len(owner_items)} 条过期)") + else: + # 处理人不在映射表中 + self.unmapped_users.add(owner) + lines.append(f"@{owner}({len(owner_items)} 条过期)") + if self.logger: + self.logger.log_api_call("formatter", "unmapped_user", {"owner": owner}, {}, False, "用户未在映射表中") for item in owner_items: type_label = "需求" if item['type'] == 'story' else "缺陷" diff --git a/src3/scheduler.py b/src3/scheduler.py index 25a7d1c..b59eea2 100644 --- a/src3/scheduler.py +++ b/src3/scheduler.py @@ -51,16 +51,23 @@ class Task3Scheduler: try: run_once() self.success_count += 1 + print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] ✓ 执行成功") except Exception as e: self.fail_count += 1 - print(f"执行失败: {e}") + print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] ✗ 执行失败: {e}") + import traceback + traceback.print_exc() def start(self): """启动调度器""" from src3.config import Task3ConfigManager - config = Task3ConfigManager() - push_time = config.get_push_time() + try: + config = Task3ConfigManager() + push_time = config.get_push_time() + except Exception as e: + print(f"✗ 配置加载失败: {e}") + return print("="*50) print("任务三:TAPD过期单推送调度器") diff --git a/src3/tapd_api.py b/src3/tapd_api.py index 8ce18f5..8bb92a7 100644 --- a/src3/tapd_api.py +++ b/src3/tapd_api.py @@ -47,6 +47,18 @@ class TAPDUnifiedApi: self.logger.log_api_call("tapd", endpoint, params, {}, False, str(e)) raise + def _make_request_with_retry(self, endpoint: str, params: Dict, retries=2, delay=30) -> Dict: + """带重试的TAPD API请求""" + import time + for attempt in range(retries + 1): + try: + return self._make_request(endpoint, params) + except Exception as e: + if attempt < retries: + time.sleep(delay) + else: + return None + def get_overdue_stories(self, owner_list: List[str]) -> List[dict]: """获取过期需求""" today = datetime.now().strftime('%Y-%m-%d') @@ -59,27 +71,25 @@ class TAPDUnifiedApi: 'fields': 'id,name,due,status' } - try: - result = self._make_request("stories", params) - data = result.get('data', []) + result = self._make_request_with_retry("stories", params) + if not result: + continue - for item in data: - story = item.get('Story', {}) - due_date = story.get('due', '') - status = story.get('status', '') + data = result.get('data', []) + for item in data: + story = item.get('Story', {}) + due_date = story.get('due', '') + status = story.get('status', '') - # 过滤:有截止日期、已过期、非终态 - if due_date and due_date < today and status not in self.STORY_TERMINAL_STATUSES: - all_stories.append({ - 'id': story.get('id'), - 'name': story.get('name'), - 'owner': owner, - 'due': due_date, - 'status': status, - 'type': 'story' - }) - except Exception as e: - pass + if due_date and due_date < today and status not in self.STORY_TERMINAL_STATUSES: + all_stories.append({ + 'id': story.get('id'), + 'name': story.get('name'), + 'owner': owner, + 'due': due_date, + 'status': status, + 'type': 'story' + }) return all_stories @@ -95,27 +105,25 @@ class TAPDUnifiedApi: 'fields': 'id,title,deadline,status' } - try: - result = self._make_request("bugs", params) - data = result.get('data', []) + result = self._make_request_with_retry("bugs", params) + if not result: + continue - for item in data: - bug = item.get('Bug', {}) - deadline = bug.get('deadline', '') - status = bug.get('status', '') + data = result.get('data', []) + for item in data: + bug = item.get('Bug', {}) + deadline = bug.get('deadline', '') + status = bug.get('status', '') - # 过滤:有截止日期、已过期、非终态 - if deadline and deadline < today and status not in self.BUG_TERMINAL_STATUSES: - all_bugs.append({ - 'id': bug.get('id'), - 'title': bug.get('title'), - 'owner': owner, - 'due': deadline, - 'status': status, - 'type': 'bug' - }) - except Exception as e: - pass + if deadline and deadline < today and status not in self.BUG_TERMINAL_STATUSES: + all_bugs.append({ + 'id': bug.get('id'), + 'title': bug.get('title'), + 'owner': owner, + 'due': deadline, + 'status': status, + 'type': 'bug' + }) return all_bugs diff --git a/src3/webhook_sender.py b/src3/webhook_sender.py index 9391759..30a7e70 100644 --- a/src3/webhook_sender.py +++ b/src3/webhook_sender.py @@ -7,12 +7,48 @@ from typing import List class WebhookSender: """企微Webhook推送器""" + MAX_BYTES = 4096 # 企微消息最大字节数 + def __init__(self, webhook_url: str, logger): self.webhook_url = webhook_url self.logger = logger def send_markdown(self, content: str, mentioned_list: List[str]) -> bool: - """发送Markdown消息""" + """发送Markdown消息,自动分段""" + messages = self._split_by_bytes(content) + + for msg in messages: + if not self._send_single(msg, mentioned_list): + return False + time.sleep(1) # 避免频率限制 + + return True + + def _split_by_bytes(self, content: str) -> List[str]: + """按字节长度分段,不切断单个用户的过期单""" + lines = content.split('\n') + messages = [] + current = [] + current_bytes = 0 + + for line in lines: + line_bytes = len(line.encode('utf-8')) + 1 # +1 for \n + + if current_bytes + line_bytes > self.MAX_BYTES and current: + messages.append('\n'.join(current)) + current = [line] + current_bytes = line_bytes + else: + current.append(line) + current_bytes += line_bytes + + if current: + messages.append('\n'.join(current)) + + return messages + + def _send_single(self, content: str, mentioned_list: List[str]) -> bool: + """发送单条消息""" payload = { "msgtype": "markdown", "markdown": {