diff --git a/.gitignore b/.gitignore index 39c342b..764fa20 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,27 @@ # 依赖 node_modules/ -# 大文件(视频/PPT/媒体) +# 大文件 videos/ dc_files/ -# 临时/中间文件 +# 临时文件 *.tmp *.bak *.log -# 原始消息数据(可通过脚本重新拉取) +# 原始消息数据 data/raw-messages/ -# 下载的飞书文档(可通过 lark-cli 重新下载) +# 飞书文档和图片 output/feishu-docs/ +output/feishu-images/ +output/downloaded-files/ + +# 生成文件 +output/invitations/ +output/reports/ +output/group_members_*.html # Python __pycache__/ @@ -29,8 +36,22 @@ venv/ # 系统文件 Thumbs.db Desktop.ini -.DS_Store +.DS_Store -# 下载的 CLI 工具(通过 setup_dws.py 自动下载) +# CLI工具 tools/ - + +# 临时脚本 +scripts/_*.py + +# 数据文件 +data/*.xlsx +data/*.ttf +data/bg*.png +data/bg*.txt +data/qiange_preview.json +data/send_*.json +data/today_bug_*.json +data/invitation_*.json +data/group_members_*.json +data/group_members_*.csv diff --git a/data/links/all_feishu_content.json b/data/links/all_feishu_content.json index 6f16818..dcf5d12 100644 --- a/data/links/all_feishu_content.json +++ b/data/links/all_feishu_content.json @@ -1,6 +1,6 @@ { "date": "2026-06-08", - "total": 179, + "total": 202, "documents": [ { "url": "https://fcnlycv6dd0w.feishu.cn/docx/KlGYddZoOo2UD9xIEVgcNqQ3n5c", @@ -2379,6 +2379,282 @@ "images_downloaded": 2, "fetched": true, "fetched_at": "2026-06-08T10:00:10.926087" + }, + { + "url": "https://dianchukeji.feishu.cn/docx/MHgSdgo1TonJDDx1leDct9kWnce", + "doc_id": "MHgSdgo1TonJDDx1leDct9kWnce", + "title": "MHgSdgo1TonJDDx1leDct9kWnce", + "sender": "黄静雯", + "group": "dc战略问题研究院", + "time": "2026-06-09 02:26:56", + "content_length": 5707, + "image_blocks": 0, + "images_downloaded": 0, + "fetched": true + }, + { + "url": "https://ocnmca6f1o0p.feishu.cn/wiki/BuTXwr0boidXf8kUmv4cYAYSnKf", + "doc_id": "BuTXwr0boidXf8kUmv4cYAYSnKf", + "title": "2026.6.8-日报-张家振", + "sender": "张家振", + "group": "dc战略问题研究院", + "time": "2026-06-09 01:17:19", + "content_length": 1325, + "image_blocks": 0, + "images_downloaded": 0, + "fetched": true + }, + { + "url": "https://fcnlycv6dd0w.feishu.cn/wiki/O68jwDBCCie0QCkRrDKcdyC6nTf", + "doc_id": "O68jwDBCCie0QCkRrDKcdyC6nTf", + "title": "2026.06.08-日报-莫润麟", + "sender": "莫润麟", + "group": "dc战略问题研究院", + "time": "2026-06-09 00:30:41", + "content_length": 3562, + "image_blocks": 2, + "images_downloaded": 2, + "fetched": true + }, + { + "url": "https://fcnlycv6dd0w.feishu.cn/docx/NwXIdPqYhooCKlxf14jc0M3DnOh", + "doc_id": "NwXIdPqYhooCKlxf14jc0M3DnOh", + "title": "NwXIdPqYhooCKlxf14jc0M3DnOh", + "sender": "夏莲", + "group": "dc战略问题研究院", + "time": "2026-06-09 00:30:40", + "content_length": 7810, + "image_blocks": 1, + "images_downloaded": 1, + "fetched": true + }, + { + "url": "https://dianchukeji.feishu.cn/wiki/L6K3wxI54iLqBEkyD5zcWNxFn1g", + "doc_id": "L6K3wxI54iLqBEkyD5zcWNxFn1g", + "title": "2026.6.8-日报-王雨默", + "sender": "王雨默", + "group": "创新组", + "time": "2026-06-09 00:04:40", + "content_length": 5257, + "image_blocks": 0, + "images_downloaded": 0, + "fetched": true + }, + { + "url": "https://dianchukeji.feishu.cn/wiki/Hy4Vw8qvtiNG2mkS0d5c5T06ntd", + "doc_id": "Hy4Vw8qvtiNG2mkS0d5c5T06ntd", + "title": "2026.06.08日报-刘鹏", + "sender": "刘鹏", + "group": "创新组", + "time": "2026-06-08 23:17:38", + "content_length": 3647, + "image_blocks": 6, + "images_downloaded": 6, + "fetched": true + }, + { + "url": "https://dianchukeji.feishu.cn/wiki/UOuzwnxCuiS06BkPaNIconpnnXj", + "doc_id": "UOuzwnxCuiS06BkPaNIconpnnXj", + "title": "260608日报-韦译", + "sender": "韦译", + "group": "创新组", + "time": "2026-06-08 23:06:55", + "content_length": 3485, + "image_blocks": 2, + "images_downloaded": 2, + "fetched": true + }, + { + "url": "https://dianchukeji.feishu.cn/docx/Bkiodh65eoWffGxiyGZcd3gfn1f", + "doc_id": "Bkiodh65eoWffGxiyGZcd3gfn1f", + "title": "一、工作内容概述", + "sender": "黄静雯", + "group": "dc战略问题研究院", + "time": "2026-06-10 09:26:20", + "content_length": 4211, + "image_blocks": 0, + "images_downloaded": 0, + "fetched": true + }, + { + "url": "https://dianchukeji.feishu.cn/docx/HrTqdLydWo4QJrxPb40c5EvUnbr", + "doc_id": "HrTqdLydWo4QJrxPb40c5EvUnbr", + "title": "一、工作内容概述", + "sender": "陈楚真", + "group": "dc战略问题研究院", + "time": "2026-06-10 00:41:07", + "content_length": 7825, + "image_blocks": 0, + "images_downloaded": 0, + "fetched": true + }, + { + "url": "https://fcnlycv6dd0w.feishu.cn/docx/GBpOdqv3FowObmxeNR0cJGlGnkb", + "doc_id": "GBpOdqv3FowObmxeNR0cJGlGnkb", + "title": "", + "sender": "夏莲", + "group": "dc战略问题研究院", + "time": "2026-06-10 00:01:04", + "content_length": 5122, + "image_blocks": 2, + "images_downloaded": 2, + "fetched": true + }, + { + "url": "https://ocnmca6f1o0p.feishu.cn/wiki/QlkHwnISsiFSLRk6Q2PcyIn3ntc", + "doc_id": "QlkHwnISsiFSLRk6Q2PcyIn3ntc", + "title": "2026.6.9-日报-张家振", + "sender": "张家振", + "group": "dc战略问题研究院", + "time": "2026-06-10 00:01:02", + "content_length": 1798, + "image_blocks": 0, + "images_downloaded": 0, + "fetched": true + }, + { + "url": "https://fcnlycv6dd0w.feishu.cn/wiki/FJCowQGEbibDkakfJOacwaLmnV3", + "doc_id": "FJCowQGEbibDkakfJOacwaLmnV3", + "title": "2026.06.09-日报-莫润麟", + "sender": "莫润麟", + "group": "dc战略问题研究院", + "time": "2026-06-10 00:00:57", + "content_length": 3359, + "image_blocks": 0, + "images_downloaded": 0, + "fetched": true + }, + { + "url": "https://dianchukeji.feishu.cn/wiki/NKBVwPk7MiPg5XksndtcIU7nnWb", + "doc_id": "NKBVwPk7MiPg5XksndtcIU7nnWb", + "title": "2026.6.9-日报-王雨默", + "sender": "王雨默", + "group": "创新组", + "time": "2026-06-10 02:13:17", + "content_length": 5765, + "image_blocks": 0, + "images_downloaded": 0, + "fetched": true + }, + { + "url": "https://dianchukeji.feishu.cn/wiki/NLKrw7OJsirClOkhlHFcVeUCn5c", + "doc_id": "NLKrw7OJsirClOkhlHFcVeUCn5c", + "title": "2026.06.09日报-刘鹏", + "sender": "刘鹏", + "group": "创新组", + "time": "2026-06-09 23:41:13", + "content_length": 3486, + "image_blocks": 4, + "images_downloaded": 4, + "fetched": true + }, + { + "url": "https://dianchukeji.feishu.cn/wiki/JirGw7bynivIo4k76EqcsHCUnqb", + "doc_id": "JirGw7bynivIo4k76EqcsHCUnqb", + "title": "260609日报-韦译", + "sender": "韦译", + "group": "创新组", + "time": "2026-06-09 23:15:41", + "content_length": 4749, + "image_blocks": 6, + "images_downloaded": 6, + "fetched": true + }, + { + "url": "https://ocnmca6f1o0p.feishu.cn/wiki/SxSfwITX3iXoeKknVxickYWUnEf", + "doc_id": "SxSfwITX3iXoeKknVxickYWUnEf", + "title": "2026.6.10-日报-张家振", + "sender": "张家振", + "group": "dc战略问题研究院", + "time": "2026-06-11 01:29:02", + "content_length": 2091, + "image_blocks": 0, + "images_downloaded": 0, + "fetched": true + }, + { + "url": "https://fcnlycv6dd0w.feishu.cn/wiki/RW59wv667iwqnyk7EBScwJKMn1c", + "doc_id": "RW59wv667iwqnyk7EBScwJKMn1c", + "title": "2026.06.10-日报-莫润麟", + "sender": "莫润麟", + "group": "dc战略问题研究院", + "time": "2026-06-11 00:31:32", + "content_length": 2367, + "image_blocks": 0, + "images_downloaded": 0, + "fetched": true + }, + { + "url": "https://fcnlycv6dd0w.feishu.cn/docx/NNcgdMsI9ohaDDxDD32cfw7onUc", + "doc_id": "NNcgdMsI9ohaDDxDD32cfw7onUc", + "title": "", + "sender": "夏莲", + "group": "dc战略问题研究院", + "time": "2026-06-11 00:31:29", + "content_length": 6718, + "image_blocks": 0, + "images_downloaded": 0, + "fetched": true + }, + { + "url": "https://dianchukeji.feishu.cn/docx/S4bSd1MU3ofarpxOuUGcot1vnXd", + "doc_id": "S4bSd1MU3ofarpxOuUGcot1vnXd", + "title": "一、工作内容概述", + "sender": "陈楚真", + "group": "dc战略问题研究院", + "time": "2026-06-11 00:29:51", + "content_length": 5879, + "image_blocks": 6, + "images_downloaded": 6, + "fetched": true + }, + { + "url": "https://dianchukeji.feishu.cn/docx/A9lFdJXz5olCDvxsAwHcgSLGnad", + "doc_id": "A9lFdJXz5olCDvxsAwHcgSLGnad", + "title": "一、工作内容概述", + "sender": "黄静雯", + "group": "dc战略问题研究院", + "time": "2026-06-11 00:29:25", + "content_length": 3115, + "image_blocks": 0, + "images_downloaded": 0, + "fetched": true + }, + { + "url": "https://dianchukeji.feishu.cn/wiki/UAicweFWFi4jK0kvTCzcowIynkh", + "doc_id": "UAicweFWFi4jK0kvTCzcowIynkh", + "title": "2026.6.10-日报-王雨默", + "sender": "王雨默", + "group": "创新组", + "time": "2026-06-11 01:36:00", + "content_length": 898, + "image_blocks": 0, + "images_downloaded": 0, + "fetched": true + }, + { + "url": "https://dianchukeji.feishu.cn/wiki/BKigwCCEvixk1vkftyHc4Stonxf", + "doc_id": "BKigwCCEvixk1vkftyHc4Stonxf", + "title": "2026.06.10日报-刘鹏", + "sender": "刘鹏", + "group": "创新组", + "time": "2026-06-10 23:02:07", + "content_length": 3487, + "image_blocks": 11, + "images_downloaded": 11, + "fetched": true + }, + { + "url": "https://dianchukeji.feishu.cn/wiki/WOTowPY5nibWUKkaEKecw1FCnBh", + "doc_id": "WOTowPY5nibWUKkaEKecw1FCnBh", + "title": "260610日报-韦译", + "sender": "韦译", + "group": "创新组", + "time": "2026-06-10 22:40:42", + "content_length": 4533, + "image_blocks": 15, + "images_downloaded": 15, + "fetched": true } ] } \ No newline at end of file diff --git a/data/links/all_feishu_links.json b/data/links/all_feishu_links.json index 3423255..f6cde42 100644 --- a/data/links/all_feishu_links.json +++ b/data/links/all_feishu_links.json @@ -1,6 +1,6 @@ { "date": "2026-06-06", - "total": 215, + "total": 238, "links": [ { "url": "https://fcnlycv6dd0w.feishu.cn/docx/ErwhdZhR3oJjoNxGB85cQBicnge", @@ -1721,6 +1721,190 @@ "sender": "夏莲", "time": "2026-06-06 23:28:33", "group": "dc战略问题研究院" + }, + { + "url": "https://dianchukeji.feishu.cn/docx/MHgSdgo1TonJDDx1leDct9kWnce", + "doc_id": "MHgSdgo1TonJDDx1leDct9kWnce", + "type": "docx", + "sender": "黄静雯", + "time": "2026-06-09 02:26:56", + "group": "dc战略问题研究院" + }, + { + "url": "https://ocnmca6f1o0p.feishu.cn/wiki/BuTXwr0boidXf8kUmv4cYAYSnKf", + "doc_id": "BuTXwr0boidXf8kUmv4cYAYSnKf", + "type": "wiki", + "sender": "张家振", + "time": "2026-06-09 01:17:19", + "group": "dc战略问题研究院" + }, + { + "url": "https://fcnlycv6dd0w.feishu.cn/wiki/O68jwDBCCie0QCkRrDKcdyC6nTf", + "doc_id": "O68jwDBCCie0QCkRrDKcdyC6nTf", + "type": "wiki", + "sender": "莫润麟", + "time": "2026-06-09 00:30:41", + "group": "dc战略问题研究院" + }, + { + "url": "https://fcnlycv6dd0w.feishu.cn/docx/NwXIdPqYhooCKlxf14jc0M3DnOh", + "doc_id": "NwXIdPqYhooCKlxf14jc0M3DnOh", + "type": "docx", + "sender": "夏莲", + "time": "2026-06-09 00:30:40", + "group": "dc战略问题研究院" + }, + { + "url": "https://dianchukeji.feishu.cn/wiki/L6K3wxI54iLqBEkyD5zcWNxFn1g", + "doc_id": "L6K3wxI54iLqBEkyD5zcWNxFn1g", + "type": "wiki", + "sender": "王雨默", + "time": "2026-06-09 00:04:40", + "group": "创新组" + }, + { + "url": "https://dianchukeji.feishu.cn/wiki/Hy4Vw8qvtiNG2mkS0d5c5T06ntd", + "doc_id": "Hy4Vw8qvtiNG2mkS0d5c5T06ntd", + "type": "wiki", + "sender": "刘鹏", + "time": "2026-06-08 23:17:38", + "group": "创新组" + }, + { + "url": "https://dianchukeji.feishu.cn/wiki/UOuzwnxCuiS06BkPaNIconpnnXj", + "doc_id": "UOuzwnxCuiS06BkPaNIconpnnXj", + "type": "wiki", + "sender": "韦译", + "time": "2026-06-08 23:06:55", + "group": "创新组" + }, + { + "url": "https://dianchukeji.feishu.cn/docx/Bkiodh65eoWffGxiyGZcd3gfn1f", + "doc_id": "Bkiodh65eoWffGxiyGZcd3gfn1f", + "type": "docx", + "sender": "黄静雯", + "time": "2026-06-10 09:26:20", + "group": "dc战略问题研究院" + }, + { + "url": "https://dianchukeji.feishu.cn/docx/HrTqdLydWo4QJrxPb40c5EvUnbr", + "doc_id": "HrTqdLydWo4QJrxPb40c5EvUnbr", + "type": "docx", + "sender": "陈楚真", + "time": "2026-06-10 00:41:07", + "group": "dc战略问题研究院" + }, + { + "url": "https://fcnlycv6dd0w.feishu.cn/docx/GBpOdqv3FowObmxeNR0cJGlGnkb", + "doc_id": "GBpOdqv3FowObmxeNR0cJGlGnkb", + "type": "docx", + "sender": "夏莲", + "time": "2026-06-10 00:01:04", + "group": "dc战略问题研究院" + }, + { + "url": "https://ocnmca6f1o0p.feishu.cn/wiki/QlkHwnISsiFSLRk6Q2PcyIn3ntc", + "doc_id": "QlkHwnISsiFSLRk6Q2PcyIn3ntc", + "type": "wiki", + "sender": "张家振", + "time": "2026-06-10 00:01:02", + "group": "dc战略问题研究院" + }, + { + "url": "https://fcnlycv6dd0w.feishu.cn/wiki/FJCowQGEbibDkakfJOacwaLmnV3", + "doc_id": "FJCowQGEbibDkakfJOacwaLmnV3", + "type": "wiki", + "sender": "莫润麟", + "time": "2026-06-10 00:00:57", + "group": "dc战略问题研究院" + }, + { + "url": "https://dianchukeji.feishu.cn/wiki/NKBVwPk7MiPg5XksndtcIU7nnWb", + "doc_id": "NKBVwPk7MiPg5XksndtcIU7nnWb", + "type": "wiki", + "sender": "王雨默", + "time": "2026-06-10 02:13:17", + "group": "创新组" + }, + { + "url": "https://dianchukeji.feishu.cn/wiki/NLKrw7OJsirClOkhlHFcVeUCn5c", + "doc_id": "NLKrw7OJsirClOkhlHFcVeUCn5c", + "type": "wiki", + "sender": "刘鹏", + "time": "2026-06-09 23:41:13", + "group": "创新组" + }, + { + "url": "https://dianchukeji.feishu.cn/wiki/JirGw7bynivIo4k76EqcsHCUnqb", + "doc_id": "JirGw7bynivIo4k76EqcsHCUnqb", + "type": "wiki", + "sender": "韦译", + "time": "2026-06-09 23:15:41", + "group": "创新组" + }, + { + "url": "https://ocnmca6f1o0p.feishu.cn/wiki/SxSfwITX3iXoeKknVxickYWUnEf", + "doc_id": "SxSfwITX3iXoeKknVxickYWUnEf", + "type": "wiki", + "sender": "张家振", + "time": "2026-06-11 01:29:02", + "group": "dc战略问题研究院" + }, + { + "url": "https://fcnlycv6dd0w.feishu.cn/wiki/RW59wv667iwqnyk7EBScwJKMn1c", + "doc_id": "RW59wv667iwqnyk7EBScwJKMn1c", + "type": "wiki", + "sender": "莫润麟", + "time": "2026-06-11 00:31:32", + "group": "dc战略问题研究院" + }, + { + "url": "https://fcnlycv6dd0w.feishu.cn/docx/NNcgdMsI9ohaDDxDD32cfw7onUc", + "doc_id": "NNcgdMsI9ohaDDxDD32cfw7onUc", + "type": "docx", + "sender": "夏莲", + "time": "2026-06-11 00:31:29", + "group": "dc战略问题研究院" + }, + { + "url": "https://dianchukeji.feishu.cn/docx/S4bSd1MU3ofarpxOuUGcot1vnXd", + "doc_id": "S4bSd1MU3ofarpxOuUGcot1vnXd", + "type": "docx", + "sender": "陈楚真", + "time": "2026-06-11 00:29:51", + "group": "dc战略问题研究院" + }, + { + "url": "https://dianchukeji.feishu.cn/docx/A9lFdJXz5olCDvxsAwHcgSLGnad", + "doc_id": "A9lFdJXz5olCDvxsAwHcgSLGnad", + "type": "docx", + "sender": "黄静雯", + "time": "2026-06-11 00:29:25", + "group": "dc战略问题研究院" + }, + { + "url": "https://dianchukeji.feishu.cn/wiki/UAicweFWFi4jK0kvTCzcowIynkh", + "doc_id": "UAicweFWFi4jK0kvTCzcowIynkh", + "type": "wiki", + "sender": "王雨默", + "time": "2026-06-11 01:36:00", + "group": "创新组" + }, + { + "url": "https://dianchukeji.feishu.cn/wiki/BKigwCCEvixk1vkftyHc4Stonxf", + "doc_id": "BKigwCCEvixk1vkftyHc4Stonxf", + "type": "wiki", + "sender": "刘鹏", + "time": "2026-06-10 23:02:07", + "group": "创新组" + }, + { + "url": "https://dianchukeji.feishu.cn/wiki/WOTowPY5nibWUKkaEKecw1FCnBh", + "doc_id": "WOTowPY5nibWUKkaEKecw1FCnBh", + "type": "wiki", + "sender": "韦译", + "time": "2026-06-10 22:40:42", + "group": "创新组" } ] } \ No newline at end of file diff --git a/scripts/batch_gen_invitations.py b/scripts/batch_gen_invitations.py new file mode 100644 index 0000000..e42d56e --- /dev/null +++ b/scripts/batch_gen_invitations.py @@ -0,0 +1,97 @@ +"""批量生成邀请函图片:读取 invitation_config.json 模板 + group_members_with_account.csv""" + +import csv +import json +import os +import sys + +from PIL import Image, ImageDraw, ImageFont + +DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data") +BG_PATH = os.path.join(DATA_DIR, "bg.png") +FONT_DIR = DATA_DIR +CONFIG_PATH = os.path.join(DATA_DIR, "invitation_config.json") +CSV_PATH = os.path.join(DATA_DIR, "group_members_with_account.csv") +OUTPUT_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "output", "invitations") + + +def hex_to_rgb(h): + h = h.lstrip('#') + return tuple(int(h[i:i+2], 16) for i in (0, 2, 4)) + + +def resolve_font(name): + local = os.path.join(FONT_DIR, name) + if os.path.isfile(local): + return local + sys_font = os.path.join(r"C:\Windows\Fonts", name) + if os.path.isfile(sys_font): + return sys_font + return os.path.join(FONT_DIR, "WenDaoGeTeSong.ttf") + + +def generate_image(cfg, name, account, password): + img = Image.open(BG_PATH).copy() + draw = ImageDraw.Draw(img) + font_path = resolve_font(cfg.get("font", "WenDaoGeTeSong.ttf")) + sw = cfg.get("stroke_width", 3) + sc = hex_to_rgb(cfg.get("stroke_color", "#000000")) + + size = cfg.get("size", 30) + font = ImageFont.truetype(font_path, size) + color = hex_to_rgb(cfg.get("color", "#ffffff")) + x = cfg.get("x", 510) + y = cfg.get("y", 1055) + + text = cfg.get("text", "") + text = text.replace("{name}", name).replace("{account}", account).replace("{password}", password) + + line_height = cfg.get("line_height", 1.5) + spacing = int(size * (line_height - 1.0)) + + align = cfg.get("align", "center") + anchor_map = {"left": "lt", "center": "mt", "right": "rt"} + anchor = anchor_map.get(align, "mt") + + lines = text.split("\n") + cur_y = y + for line in lines: + draw.text( + (x, cur_y), line, fill=color, font=font, anchor=anchor, + stroke_width=sw, stroke_fill=sc + ) + cur_y += size + spacing + return img + + +def main(): + with open(CONFIG_PATH, encoding="utf-8") as f: + cfg = json.load(f) + + with open(CSV_PATH, encoding="utf-8") as f: + rows = list(csv.DictReader(f)) + + os.makedirs(OUTPUT_DIR, exist_ok=True) + + total = 0 + skipped = 0 + for row in rows: + name = row.get("姓名", "").strip() + account = row.get("账号", "").strip() + password = row.get("密码", "").strip() + + if not account or not password: + skipped += 1 + continue + + img = generate_image(cfg, name, account, password) + output_path = os.path.join(OUTPUT_DIR, f"invite_{account}.jpg") + img.convert("RGB").save(output_path, format="JPEG", quality=75) + total += 1 + + print(f"Done. Generated {total} images, skipped {skipped} (missing account/password).") + print(f"Output: {OUTPUT_DIR}") + + +if __name__ == "__main__": + main() diff --git a/scripts/extract_group_members.py b/scripts/extract_group_members.py new file mode 100644 index 0000000..fe60b60 --- /dev/null +++ b/scripts/extract_group_members.py @@ -0,0 +1,167 @@ +"""从钉钉群拉取全部成员并查询工号/邮箱/手机等详细信息,输出 CSV""" + +import csv +import json +import os +import subprocess +import sys +import time + +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)))) +from config import DWS + +GROUP_ID = "cidrLgqKZUgbJ9PdVHgQQIS9g==" +OUTPUT_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data") +OUTPUT_CSV = os.path.join(OUTPUT_DIR, "group_members_detail.csv") +OUTPUT_JSON = os.path.join(OUTPUT_DIR, "group_members_all.json") + + +def run_dws(args, timeout=30): + cmd = [DWS] + args + ["-f", "json"] + result = subprocess.run(cmd, capture_output=True, timeout=timeout) + out = result.stdout.decode("utf-8", errors="replace") + for i, ch in enumerate(out): + if ch == "{": + return json.loads(out[i:]) + return None + + +def fetch_all_members(): + all_members = [] + cursor = "0" + page = 0 + while True: + page += 1 + args = ["chat", "group", "members", "list", "--id", GROUP_ID, "--cursor", cursor] + data = run_dws(args, timeout=60) + if not data or not data.get("success"): + print(f" page {page} failed: {data}") + break + result = data.get("result", {}) + members = result.get("list", []) + all_members.extend(members) + print(f" page {page}: {len(members)} members (total {len(all_members)})") + if not result.get("hasMore"): + break + cursor = str(result.get("nextCursor", "")) + if not cursor: + break + time.sleep(0.3) + return all_members + + +def search_user_id(name, open_dingtalk_id): + """通过姓名搜索获取 userId""" + args = ["contact", "user", "search", "--query", name] + data = run_dws(args, timeout=15) + if data and data.get("success"): + results = data.get("result", []) + for r in results: + if r.get("openDingTalkId") == open_dingtalk_id: + return r.get("userId", "") + if r.get("name") == name: + return r.get("userId", "") + if results: + return results[0].get("userId", "") + return "" + + +def batch_get_user_details(user_ids): + """批量获取用户详情""" + profiles = {} + batch_size = 20 + for i in range(0, len(user_ids), batch_size): + batch = [uid for uid in user_ids[i:i+batch_size] if uid] + if not batch: + continue + ids_str = ",".join(batch) + args = ["contact", "user", "get", "--ids", ids_str] + data = run_dws(args, timeout=30) + if data and data.get("success"): + result = data.get("result", []) + if isinstance(result, list): + for u in result: + model = u.get("orgEmployeeModel", {}) + uid = model.get("orgUserId", "") + if uid: + profiles[uid] = model + print(f" batch {i//batch_size+1}/{(len(user_ids)+batch_size-1)//batch_size}: got {len(profiles)} profiles") + time.sleep(0.3) + return profiles + + +def main(): + os.makedirs(OUTPUT_DIR, exist_ok=True) + + print("=" * 50) + print("Step 1: Fetch all group members") + print("=" * 50) + members = fetch_all_members() + print(f"\nTotal: {len(members)} members") + + with open(OUTPUT_JSON, "w", encoding="utf-8") as f: + json.dump(members, f, ensure_ascii=False, indent=2) + + print("\n" + "=" * 50) + print("Step 2: Resolve userId for each member via search") + print("=" * 50) + user_id_map = {} # openDingtalkId -> userId + for idx, m in enumerate(members, 1): + name = m.get("memberEmpName", "") + oid = m.get("openDingtalkId", "") + if name and oid: + uid = search_user_id(name, oid) + if uid: + user_id_map[oid] = uid + if idx % 50 == 0: + print(f" resolved {len(user_id_map)}/{idx} userIds") + time.sleep(0.15) + print(f" resolved {len(user_id_map)}/{len(members)} userIds total") + + print("\n" + "=" * 50) + print("Step 3: Batch query user profiles") + print("=" * 50) + all_user_ids = list(user_id_map.values()) + profiles = batch_get_user_details(all_user_ids) + + print("\n" + "=" * 50) + print("Step 4: Generate CSV") + print("=" * 50) + + fieldnames = ["序号", "姓名", "昵称", "工号", "企业邮箱", "职位", "部门", "userId", "角色", "openDingtalkId"] + rows = [] + for idx, m in enumerate(members, 1): + oid = m.get("openDingtalkId", "") + name = m.get("memberEmpName", "") + nick = m.get("memberNick", "") + role = m.get("memberRoleDesc", "") + + uid = user_id_map.get(oid, "") + p = profiles.get(uid, {}) + job_number = p.get("jobNumber", "") + email = p.get("orgAuthEmail", "") + title = p.get("orgTitle", "") + depts = p.get("depts", []) + dept_name = "/".join(d.get("deptName", "") for d in depts) if depts else "" + + rows.append({ + "序号": idx, "姓名": name, "昵称": nick, "工号": job_number, + "企业邮箱": email, "职位": title, "部门": dept_name, + "userId": uid, "角色": role, "openDingtalkId": oid, + }) + + with open(OUTPUT_CSV, "w", encoding="utf-8-sig", newline="") as f: + writer = csv.DictWriter(f, fieldnames=fieldnames) + writer.writeheader() + writer.writerows(rows) + + print(f"\nCSV saved: {OUTPUT_CSV}") + print(f"Total: {len(rows)} rows") + has_job = sum(1 for r in rows if r["工号"]) + has_email = sum(1 for r in rows if r["企业邮箱"]) + has_dept = sum(1 for r in rows if r["部门"]) + print(f"有工号: {has_job}, 有邮箱: {has_email}, 有部门: {has_dept}") + + +if __name__ == "__main__": + main() diff --git a/scripts/gen_account_html.py b/scripts/gen_account_html.py new file mode 100644 index 0000000..cd8b758 --- /dev/null +++ b/scripts/gen_account_html.py @@ -0,0 +1,55 @@ +import csv, os + +CSV_PATH = r"D:\desktop\feishu_docs_summary\data\group_members_with_account.csv" +HTML_PATH = r"D:\desktop\feishu_docs_summary\output\group_members_with_account.html" + +rows = [] +with open(CSV_PATH, encoding="utf-8-sig") as f: + for r in csv.DictReader(f): + rows.append(r) + +matched = sum(1 for r in rows if r.get("密码")) + +html = [] +html.append('
') +html.append('共 {len(rows)} 人 | 已匹配账号: {matched} | 未匹配: {len(rows)-matched}
') +html.append('') +html.append('| {h} | ') +html.append('||||||
|---|---|---|---|---|---|---|
| {r.get("序号","")} | {r.get("姓名","")} | {acct_d} | {pwd_d} | {r.get("企业邮箱","")} | {r.get("部门","")} | {r.get("职位","")} |