From 92c59d97db3bd48b26746e2c3ba9ebfc282d22e3 Mon Sep 17 00:00:00 2001 From: weishen Date: Mon, 13 Apr 2026 21:31:10 +0800 Subject: [PATCH] =?UTF-8?q?=E6=98=9F=E6=9E=A2:=20PST=E5=A4=84=E7=90=86?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=E5=A4=87=E4=BB=BD=20(2026-04-13)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pst-processing/rules/apply_rules.py | 101 ++++++++++++++++++ .../pst-processing/rules/delete_rules.json | 84 +++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 openclaw/xingshu/pst-processing/rules/apply_rules.py create mode 100644 openclaw/xingshu/pst-processing/rules/delete_rules.json diff --git a/openclaw/xingshu/pst-processing/rules/apply_rules.py b/openclaw/xingshu/pst-processing/rules/apply_rules.py new file mode 100644 index 00000000..a1b45640 --- /dev/null +++ b/openclaw/xingshu/pst-processing/rules/apply_rules.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +""" +PST 邮件清理规则执行脚本 v1.2 +用法: python3 apply_rules.py +""" +import csv +import json +import sys +from collections import defaultdict + +RULES_FILE = '/Users/weishen/pst-processing/rules/delete_rules.json' + +def load_rules(): + with open(RULES_FILE) as f: + return json.load(f)['rules'] + +def apply_rules(csv_path): + rules = load_rules() + rows = list(csv.DictReader(open(csv_path, encoding='utf-8'))) + + for r in rows: + r['delete_flag'] = 'N' + + total_deleted = 0 + + for rule in rules: + folder_pattern = rule['folder_contains'] + action = rule['action'] + + folder_rows = [r for r in rows if folder_pattern in r['folder']] + if not folder_rows: + continue + + deleted_count = 0 + kept_count = 0 + + if action == 'keep': + kept_count = len(folder_rows) + + elif action == 'delete_all': + for r in folder_rows: + r['delete_flag'] = 'Y' + deleted_count += 1 + + elif action == 'keep_sample': + keep_limit = rule['keep_count'] + subject_map = defaultdict(list) + for r in folder_rows: + subj = r['subject'].strip()[:80] + subject_map[subj].append(r) + sorted_subjects = sorted(subject_map.items(), key=lambda x: -len(x[1])) + + for subj, emails in sorted_subjects: + if kept_count >= keep_limit: + for r in emails: + r['delete_flag'] = 'Y' + deleted_count += 1 + else: + for i, r in enumerate(emails): + if i == 0: + kept_count += 1 + else: + r['delete_flag'] = 'Y' + deleted_count += 1 + + elif action == 'keep_if_attachment': + for r in folder_rows: + if r['has_attachment'] == 'Y': + kept_count += 1 + else: + r['delete_flag'] = 'Y' + deleted_count += 1 + + print(f" {folder_pattern}: {len(folder_rows)} | 保留{kept_count} | 删除{deleted_count}") + total_deleted += deleted_count + + total_kept = len(rows) - total_deleted + print(f"📊 总计: 保留{total_kept} | 删除{total_deleted}") + + fieldnames = list(rows[0].keys()) + out_marked = csv_path.replace('.csv', '_marked.csv') + out_delete = csv_path.replace('.csv', '_delete_list.csv') + + with open(out_marked, 'w', newline='', encoding='utf-8') as f: + writer = csv.DictWriter(f, fieldnames=fieldnames) + writer.writeheader() + writer.writerows(rows) + + with open(out_delete, 'w', newline='', encoding='utf-8') as f: + writer = csv.DictWriter(f, fieldnames=fieldnames) + writer.writeheader() + writer.writerows([r for r in rows if r['delete_flag'] == 'Y']) + + print(f"✅ 输出: {out_marked}") + print(f"✅ 删除清单: {out_delete} ({total_deleted} 封)") + +if __name__ == '__main__': + if len(sys.argv) < 2: + print("用法: python3 apply_rules.py ") + sys.exit(1) + apply_rules(sys.argv[1]) diff --git a/openclaw/xingshu/pst-processing/rules/delete_rules.json b/openclaw/xingshu/pst-processing/rules/delete_rules.json new file mode 100644 index 00000000..1bdb4ec0 --- /dev/null +++ b/openclaw/xingshu/pst-processing/rules/delete_rules.json @@ -0,0 +1,84 @@ +{ + "version": "1.2", + "description": "PST邮件清理规则", + "rules": [ + { + "id": "aws_notification", + "folder_contains": "AWS Notification", + "action": "keep_sample", + "keep_count": 5, + "keep_by": "unique_subject", + "description": "AWS告警通知,每不同subject保留1封,最多5封" + }, + { + "id": "prisma_cloud", + "folder_contains": "Prisma Cloud Notifications", + "action": "keep_sample", + "keep_count": 5, + "keep_by": "unique_subject", + "description": "Prisma Cloud通知,每不同subject保留1封,最多5封" + }, + { + "id": "x4x_tenant_provisioning", + "folder_contains": "X4X-Tenant Provisioning", + "action": "keep_sample", + "keep_count": 5, + "keep_by": "unique_subject", + "description": "X4X租户配置通知,每不同subject保留1封,最多5封" + }, + { + "id": "qualys", + "folder_contains": "Qualys", + "action": "keep_sample", + "keep_count": 5, + "keep_by": "unique_subject", + "description": "Qualys安全扫描通知,每不同subject保留1封,最多5封" + }, + { + "id": "teams_notification", + "folder_contains": "Teams Notification", + "action": "keep_if_attachment", + "description": "Teams会议通知,有附件保留,无附件删除" + }, + { + "id": "sma_notification", + "folder_contains": "SMA Notficiation", + "action": "keep_sample", + "keep_count": 10, + "keep_by": "unique_subject", + "description": "SMA工单通知,每月保留10封不同subject,其余删除" + }, + { + "id": "ppm_saas_change", + "folder_contains": "PPM SaaS Change", + "action": "delete_all", + "description": "PPM故障单解决通知,全部删除" + }, + { + "id": "cloudhealth", + "folder_contains": "CloudHealth", + "action": "keep", + "description": "CloudHealth成本报告,全部保留" + }, + { + "id": "saas_bi_report", + "folder_contains": "SaaS BI Report", + "action": "keep", + "description": "BI数据推送,全部保留" + }, + { + "id": "x4x_tenant_decommissioning", + "folder_contains": "X4X-Tenant Decommissioning", + "action": "delete_all", + "description": "租户下线通知,全部删除" + }, + { + "id": "x4x_license_renewal", + "folder_contains": "X4X-License Renewal", + "action": "delete_all", + "description": "续期通知,全部删除" + } + ], + "default_action": "keep", + "updated": "2026-04-13" +}