-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsession_manager.py
More file actions
225 lines (179 loc) · 6.29 KB
/
Copy pathsession_manager.py
File metadata and controls
225 lines (179 loc) · 6.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
"""
会话管理模块(session_manager.py)
职责:保存、加载、列出和清理历史推送会话。
设计原则:
- 会话以 JSON 文件形式存放在 sessions/ 目录下。
- 文件名包含时间戳与方向标签,便于识别与管理。
- 清理策略写死为双重策略:超 7 天删除 + 超 30 条删除。
"""
import json
import os
import re
from datetime import datetime, timedelta
# 项目根目录与会话目录
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
SESSIONS_DIR = os.path.join(BASE_DIR, "sessions")
# Windows 文件名非法字符(含控制字符)
INVALID_FILENAME_CHARS = re.compile(r'[\\/*?:"<>|\x00-\x1f]')
def ensure_sessions_dir():
"""启动时若 sessions/ 目录不存在则自动创建。"""
if not os.path.exists(SESSIONS_DIR):
os.makedirs(SESSIONS_DIR, exist_ok=True)
def sanitize_label(label, max_len=20):
"""
将方向标签处理为合法文件名片段。
处理规则:
1. 去除首尾空白。
2. 空字符串替换为“默认推送”。
3. 将非法字符替换为下划线。
4. 去除首尾句点和空格。
5. 截断至 max_len 个字符。
"""
if label is None:
label = ""
label = str(label).strip()
if not label:
label = "默认推送"
label = INVALID_FILENAME_CHARS.sub("_", label)
label = label.strip(". ")
if not label:
label = "默认推送"
if len(label) > max_len:
label = label[:max_len]
return label
def generate_filename(topic, dt=None):
"""
生成会话文件名:YYYY-MM-DD_HH-MM-SS_方向标签.json
参数:
topic: 用户输入的方向,留空时标签为“默认推送”
dt: 可选 datetime,默认当前时间
"""
if dt is None:
dt = datetime.now()
timestamp = dt.strftime("%Y-%m-%d_%H-%M-%S")
label = sanitize_label(topic)
return f"{timestamp}_{label}.json"
def save_session(topic, count, items):
"""
保存一次推送会话。
参数:
topic: 用户关注方向
count: 推送条数
items: 推送信息列表
返回:
session_id(即文件名去掉 .json 后缀)
"""
ensure_sessions_dir()
now = datetime.now()
filename = generate_filename(topic, now)
filepath = os.path.join(SESSIONS_DIR, filename)
session_data = {
"date": now.strftime("%Y-%m-%d %H:%M:%S"),
"topic": topic,
"count": count,
"items": items,
}
with open(filepath, "w", encoding="utf-8") as f:
json.dump(session_data, f, ensure_ascii=False, indent=2)
# 保存后执行双重清理(硬约束:写死策略,不做配置项)
cleanup_sessions()
session_id = os.path.splitext(filename)[0]
return session_id
def list_sessions():
"""
列出所有历史会话,按创建时间倒序排列。
返回:
列表,每项为 dict:
{
"session_id": "文件名(无后缀)",
"filename": "完整文件名",
"label": "方向标签",
"display_time": "MM-DD HH:MM",
"created_at": 创建时间戳
}
"""
ensure_sessions_dir()
sessions = []
for filename in os.listdir(SESSIONS_DIR):
if not filename.endswith(".json"):
continue
filepath = os.path.join(SESSIONS_DIR, filename)
try:
ctime = os.path.getctime(filepath)
dt = datetime.fromtimestamp(ctime)
# 从文件内容读取方向,确保显示与保存一致
with open(filepath, "r", encoding="utf-8") as f:
data = json.load(f)
topic = data.get("topic", "")
label = sanitize_label(topic)
sessions.append({
"session_id": os.path.splitext(filename)[0],
"filename": filename,
"label": label,
"display_time": dt.strftime("%m-%d %H:%M"),
"created_at": ctime,
})
except Exception:
# 损坏或不可读文件跳过,不影响整体列表
continue
# 按创建时间倒序:最新的在最前
sessions.sort(key=lambda x: x["created_at"], reverse=True)
return sessions
def load_session(session_id):
"""
加载单个会话详情。
参数:
session_id: 文件名(无 .json 后缀)
返回:
会话数据 dict
"""
ensure_sessions_dir()
filename = f"{session_id}.json"
filepath = os.path.join(SESSIONS_DIR, filename)
if not os.path.exists(filepath):
raise FileNotFoundError("会话不存在")
with open(filepath, "r", encoding="utf-8") as f:
return json.load(f)
def cleanup_sessions():
"""
执行双重清理策略:
1. 删除创建时间超过 7 天的会话文件。
2. 若删除后总数仍超过 30 条,按创建时间从旧到新删除,直至剩余 30 条。
说明:
- Windows 下 os.path.getctime() 返回文件创建时间,符合验收口径。
- 策略写死在代码中,不通过配置项暴露。
"""
ensure_sessions_dir()
now = datetime.now()
cutoff = now - timedelta(days=7)
# 策略 1:删除创建时间超过 7 天的文件
for filename in os.listdir(SESSIONS_DIR):
if not filename.endswith(".json"):
continue
filepath = os.path.join(SESSIONS_DIR, filename)
try:
ctime = datetime.fromtimestamp(os.path.getctime(filepath))
if ctime < cutoff:
os.remove(filepath)
except Exception:
continue
# 策略 2:若总数仍超过 30 条,从旧到新删至 30 条
remaining = []
for filename in os.listdir(SESSIONS_DIR):
if not filename.endswith(".json"):
continue
filepath = os.path.join(SESSIONS_DIR, filename)
try:
ctime = os.path.getctime(filepath)
remaining.append((filepath, ctime))
except Exception:
continue
if len(remaining) > 30:
# 按创建时间升序:最旧的排在最前面
remaining.sort(key=lambda x: x[1])
# 保留最新的 30 条,删除其余
for filepath, _ in remaining[:-30]:
try:
os.remove(filepath)
except Exception:
continue