Update app.py
Browse files
app.py
CHANGED
|
@@ -1224,7 +1224,6 @@ def start_telethon_worker():
|
|
| 1224 |
|
| 1225 |
task['last_run'] = current_time
|
| 1226 |
|
| 1227 |
-
# ===== 🔧 修复核心:标签变化时重建缓存 =====
|
| 1228 |
if new_tags_found or is_first_run:
|
| 1229 |
task['tags_map'] = tags_map
|
| 1230 |
task['scanned_msgs'] = scanned_msgs
|
|
@@ -1234,7 +1233,6 @@ def start_telethon_worker():
|
|
| 1234 |
active_tags = [t for t in all_tags if t not in blacklist]
|
| 1235 |
task['tags_cache'] = active_tags
|
| 1236 |
|
| 1237 |
-
# 构建目录文本
|
| 1238 |
directory_map = {}
|
| 1239 |
for tag in active_tags:
|
| 1240 |
clean_str = tag[1:]
|
|
@@ -1251,35 +1249,93 @@ def start_telethon_worker():
|
|
| 1251 |
if key not in directory_map: directory_map[key] = []
|
| 1252 |
directory_map[key].append(tag)
|
| 1253 |
|
| 1254 |
-
|
| 1255 |
-
|
| 1256 |
-
if "0-9" in keys: keys.remove("0-9"); keys.insert(0, "0-9")
|
| 1257 |
-
for key in keys:
|
| 1258 |
-
tags_line = " ".join([html.escape(t) for t in sorted(directory_map[key])])
|
| 1259 |
-
dir_lines.append(f"{key}: {tags_line}")
|
| 1260 |
|
| 1261 |
-
|
| 1262 |
-
|
| 1263 |
-
task['_dir_body_cache'] = dir_body
|
| 1264 |
task['_dir_count_cache'] = len(active_tags)
|
| 1265 |
print(f"📂 [目录] 重建缓存: {len(active_tags)} 个标签")
|
| 1266 |
|
| 1267 |
-
|
| 1268 |
-
|
| 1269 |
tag_count = task.get('_dir_count_cache', 0)
|
| 1270 |
-
if not
|
| 1271 |
continue
|
| 1272 |
|
| 1273 |
beijing_tz = timezone(timedelta(hours=8))
|
| 1274 |
now_str = datetime.now(beijing_tz).strftime("%m-%d %H:%M")
|
| 1275 |
task_name = task.get('task_name', '标签目录')
|
| 1276 |
-
safe_title =
|
| 1277 |
-
|
| 1278 |
-
|
| 1279 |
-
# 用标签数量+内容做指纹,不含时间戳
|
| 1280 |
-
content_fingerprint = f"{tag_count}|{dir_body}"
|
| 1281 |
last_html_per_target = task.setdefault('last_html_per_target', {})
|
| 1282 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1283 |
for tgt in targets:
|
| 1284 |
tgt_ch = int(tgt['channel_id'])
|
| 1285 |
tgt_msg = int(tgt['msg_id'])
|
|
@@ -1301,14 +1357,62 @@ def start_telethon_worker():
|
|
| 1301 |
else:
|
| 1302 |
base_html = ""
|
| 1303 |
|
| 1304 |
-
|
| 1305 |
-
|
| 1306 |
-
|
| 1307 |
-
|
| 1308 |
-
|
| 1309 |
-
|
| 1310 |
-
|
| 1311 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1312 |
bot.edit_message_caption(caption=new_message_text, chat_id=tgt_ch, message_id=tgt_msg, parse_mode="HTML")
|
| 1313 |
else:
|
| 1314 |
bot.edit_message_text(text=new_message_text, chat_id=tgt_ch, message_id=tgt_msg, parse_mode="HTML")
|
|
|
|
| 1224 |
|
| 1225 |
task['last_run'] = current_time
|
| 1226 |
|
|
|
|
| 1227 |
if new_tags_found or is_first_run:
|
| 1228 |
task['tags_map'] = tags_map
|
| 1229 |
task['scanned_msgs'] = scanned_msgs
|
|
|
|
| 1233 |
active_tags = [t for t in all_tags if t not in blacklist]
|
| 1234 |
task['tags_cache'] = active_tags
|
| 1235 |
|
|
|
|
| 1236 |
directory_map = {}
|
| 1237 |
for tag in active_tags:
|
| 1238 |
clean_str = tag[1:]
|
|
|
|
| 1249 |
if key not in directory_map: directory_map[key] = []
|
| 1250 |
directory_map[key].append(tag)
|
| 1251 |
|
| 1252 |
+
keys_sorted = sorted(directory_map.keys())
|
| 1253 |
+
if "0-9" in keys_sorted: keys_sorted.remove("0-9"); keys_sorted.insert(0, "0-9")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1254 |
|
| 1255 |
+
task['_dir_map_cache'] = directory_map
|
| 1256 |
+
task['_dir_keys_cache'] = keys_sorted
|
|
|
|
| 1257 |
task['_dir_count_cache'] = len(active_tags)
|
| 1258 |
print(f"📂 [目录] 重建缓存: {len(active_tags)} 个标签")
|
| 1259 |
|
| 1260 |
+
directory_map = task.get('_dir_map_cache', {})
|
| 1261 |
+
keys_sorted = task.get('_dir_keys_cache', [])
|
| 1262 |
tag_count = task.get('_dir_count_cache', 0)
|
| 1263 |
+
if not directory_map and not tag_count:
|
| 1264 |
continue
|
| 1265 |
|
| 1266 |
beijing_tz = timezone(timedelta(hours=8))
|
| 1267 |
now_str = datetime.now(beijing_tz).strftime("%m-%d %H:%M")
|
| 1268 |
task_name = task.get('task_name', '标签目录')
|
| 1269 |
+
safe_title = html.escape(task_name)
|
| 1270 |
+
content_fingerprint = f"{tag_count}|{'|'.join(keys_sorted)}"
|
|
|
|
|
|
|
|
|
|
| 1271 |
last_html_per_target = task.setdefault('last_html_per_target', {})
|
| 1272 |
|
| 1273 |
+
# ===== 生成外部网页 HTML =====
|
| 1274 |
+
sections_html = ""
|
| 1275 |
+
for key in keys_sorted:
|
| 1276 |
+
tags_in_group = sorted(directory_map.get(key, []))
|
| 1277 |
+
tags_chips = ""
|
| 1278 |
+
for t in tags_in_group:
|
| 1279 |
+
safe_t = html.escape(t)
|
| 1280 |
+
# tg://search 协议让标签在 Telegram 内可搜索
|
| 1281 |
+
tags_chips += f'<a href="tg://search?query={html.escape(t)}" class="tag">{safe_t}</a>'
|
| 1282 |
+
sections_html += f'<div class="section"><div class="section-title">{html.escape(key)}</div><div class="tags">{tags_chips}</div></div>'
|
| 1283 |
+
|
| 1284 |
+
page_html = f"""<!DOCTYPE html>
|
| 1285 |
+
<html lang="zh-CN">
|
| 1286 |
+
<head>
|
| 1287 |
+
<meta charset="UTF-8">
|
| 1288 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 1289 |
+
<title>完整目录 - {safe_title}</title>
|
| 1290 |
+
<style>
|
| 1291 |
+
body {{ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #1a1a2e; color: #eaeaea; padding: 15px; margin: 0; }}
|
| 1292 |
+
.container {{ max-width: 600px; margin: 0 auto; background: #16213e; border-radius: 12px; padding: 15px; box-shadow: 0 4px 15px rgba(0,0,0,0.3); }}
|
| 1293 |
+
h2 {{ text-align: center; color: #fff; margin-bottom: 20px; font-size: 18px; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 10px; line-height: 1.5; }}
|
| 1294 |
+
.search-box {{ width: 100%; padding: 10px 14px; background: #1a1a2e; border: 1px solid rgba(255,255,255,0.1); border-radius: 10px; color: #eaeaea; font-size: 14px; outline: none; margin-bottom: 15px; box-sizing: border-box; }}
|
| 1295 |
+
.search-box:focus {{ border-color: #e94560; }}
|
| 1296 |
+
.search-box::placeholder {{ color: #8a8a9a; }}
|
| 1297 |
+
.section {{ margin-bottom: 16px; }}
|
| 1298 |
+
.section-title {{ font-size: 15px; font-weight: 700; color: #e94560; margin-bottom: 8px; padding-left: 4px; border-left: 3px solid #e94560; padding-left: 8px; }}
|
| 1299 |
+
.tags {{ display: flex; flex-wrap: wrap; gap: 8px; }}
|
| 1300 |
+
.tag {{ display: inline-block; padding: 6px 12px; background: rgba(255,255,255,0.06); border-radius: 20px; font-size: 13px; color: #5dade2; text-decoration: none; transition: all 0.2s; border: 1px solid rgba(255,255,255,0.08); }}
|
| 1301 |
+
.tag:active {{ background: rgba(233,69,96,0.2); border-color: #e94560; }}
|
| 1302 |
+
.tag.hidden {{ display: none; }}
|
| 1303 |
+
.stats {{ text-align: center; color: #8a8a9a; font-size: 12px; margin-top: 15px; padding-top: 10px; border-top: 1px solid rgba(255,255,255,0.06); }}
|
| 1304 |
+
.section.hidden {{ display: none; }}
|
| 1305 |
+
.no-result {{ text-align: center; color: #8a8a9a; padding: 30px; display: none; }}
|
| 1306 |
+
</style>
|
| 1307 |
+
</head>
|
| 1308 |
+
<body>
|
| 1309 |
+
<div class="container">
|
| 1310 |
+
<h2>🗂️ {safe_title}<br><span style="font-size:12px;color:#8a8a9a;font-weight:normal">共 {tag_count} 个标签 | 更新于 {now_str}</span></h2>
|
| 1311 |
+
<input type="text" class="search-box" placeholder="🔍 搜索标签..." oninput="filterTags(this.value)">
|
| 1312 |
+
<div id="noResult" class="no-result">😔 没有找到匹配的标签</div>
|
| 1313 |
+
{sections_html}
|
| 1314 |
+
<div class="stats">💡 点击标签可在 Telegram 内搜索</div>
|
| 1315 |
+
</div>
|
| 1316 |
+
<script>
|
| 1317 |
+
function filterTags(q) {{
|
| 1318 |
+
q = q.toLowerCase().trim();
|
| 1319 |
+
const sections = document.querySelectorAll('.section');
|
| 1320 |
+
const noResult = document.getElementById('noResult');
|
| 1321 |
+
let totalVisible = 0;
|
| 1322 |
+
sections.forEach(sec => {{
|
| 1323 |
+
const tags = sec.querySelectorAll('.tag');
|
| 1324 |
+
let visible = 0;
|
| 1325 |
+
tags.forEach(tag => {{
|
| 1326 |
+
const match = !q || tag.textContent.toLowerCase().includes(q);
|
| 1327 |
+
tag.classList.toggle('hidden', !match);
|
| 1328 |
+
if (match) visible++;
|
| 1329 |
+
}});
|
| 1330 |
+
sec.classList.toggle('hidden', visible === 0);
|
| 1331 |
+
totalVisible += visible;
|
| 1332 |
+
}});
|
| 1333 |
+
noResult.style.display = totalVisible === 0 && q ? 'block' : 'none';
|
| 1334 |
+
}}
|
| 1335 |
+
</script>
|
| 1336 |
+
</body>
|
| 1337 |
+
</html>"""
|
| 1338 |
+
|
| 1339 |
for tgt in targets:
|
| 1340 |
tgt_ch = int(tgt['channel_id'])
|
| 1341 |
tgt_msg = int(tgt['msg_id'])
|
|
|
|
| 1357 |
else:
|
| 1358 |
base_html = ""
|
| 1359 |
|
| 1360 |
+
# 把网页存入缓存
|
| 1361 |
+
cache_key = f"dir_{tgt_ch}_{tgt_msg}"
|
| 1362 |
+
HTML_CACHE[cache_key] = page_html
|
| 1363 |
+
|
| 1364 |
+
space_host = "bangdan.nine7.cc.cd"
|
| 1365 |
+
dir_link = f"https://{space_host}/list/{cache_key}"
|
| 1366 |
+
|
| 1367 |
+
# ===== 构建精简消息(兼容 caption 1024 字符限制)=====
|
| 1368 |
+
# 先尝试放一些热门分类摘要
|
| 1369 |
+
preview_lines = []
|
| 1370 |
+
chars_used = 0
|
| 1371 |
+
max_preview_chars = 600 if not (original_msg.photo or original_msg.video or original_msg.document) else 300
|
| 1372 |
+
for key in keys_sorted:
|
| 1373 |
+
tags_in_group = sorted(directory_map.get(key, []))
|
| 1374 |
+
line = f"{key}: {' '.join(tags_in_group)}"
|
| 1375 |
+
if chars_used + len(line) > max_preview_chars:
|
| 1376 |
+
preview_lines.append(f"... 共 {len(keys_sorted)} 个分类")
|
| 1377 |
+
break
|
| 1378 |
+
preview_lines.append(line)
|
| 1379 |
+
chars_used += len(line)
|
| 1380 |
+
|
| 1381 |
+
preview_text = "\n".join(preview_lines)
|
| 1382 |
+
|
| 1383 |
+
dir_section = (
|
| 1384 |
+
f"<b>{safe_title} ({tag_count}) "
|
| 1385 |
+
f"<a href='{dir_link}'>📖 完整目录</a></b>\n"
|
| 1386 |
+
f"<blockquote expandable>{html.escape(preview_text)}</blockquote>\n"
|
| 1387 |
+
f"⏳ <code>最后更新: {now_str}</code>"
|
| 1388 |
+
)
|
| 1389 |
+
|
| 1390 |
+
new_message_text = f"{base_html}{SEPARATOR_MARK}{dir_section}"
|
| 1391 |
+
|
| 1392 |
+
# 如果是媒体消息,caption 限制 1024
|
| 1393 |
+
is_media = original_msg.photo or original_msg.video or original_msg.document
|
| 1394 |
+
char_limit = 1024 if is_media else 4096
|
| 1395 |
+
|
| 1396 |
+
if len(new_message_text) > char_limit:
|
| 1397 |
+
# 超限就只放标题+链接
|
| 1398 |
+
short_section = (
|
| 1399 |
+
f"<b>{safe_title} ({tag_count}) "
|
| 1400 |
+
f"<a href='{dir_link}'>📖 完整目录</a></b>\n"
|
| 1401 |
+
f"⏳ <code>最后更新: {now_str}</code>"
|
| 1402 |
+
)
|
| 1403 |
+
new_message_text = f"{base_html}{SEPARATOR_MARK}{short_section}"
|
| 1404 |
+
|
| 1405 |
+
# 终极保险:如果 base_html 本身就很长
|
| 1406 |
+
if len(new_message_text) > char_limit:
|
| 1407 |
+
new_message_text = (
|
| 1408 |
+
f"<b>{safe_title} ({tag_count}) "
|
| 1409 |
+
f"<a href='{dir_link}'>📖 完整目录</a></b>\n"
|
| 1410 |
+
f"⏳ <code>最后更新: {now_str}</code>"
|
| 1411 |
+
)
|
| 1412 |
+
|
| 1413 |
+
print(f"📏 [目录] 消息长度: {len(new_message_text)}/{char_limit}")
|
| 1414 |
+
|
| 1415 |
+
if is_media:
|
| 1416 |
bot.edit_message_caption(caption=new_message_text, chat_id=tgt_ch, message_id=tgt_msg, parse_mode="HTML")
|
| 1417 |
else:
|
| 1418 |
bot.edit_message_text(text=new_message_text, chat_id=tgt_ch, message_id=tgt_msg, parse_mode="HTML")
|