fix: 重构UI结构+分组检测+分组删除节点+返回按钮修复

This commit is contained in:
mango
2026-02-25 17:32:50 +08:00
parent daf2e1e776
commit ba8ffb9a76

160
bot.py
View File

@@ -109,20 +109,20 @@ def add_sub_to_group(link, user, group_id):
async def cmd_help(update: Update, context: ContextTypes.DEFAULT_TYPE): async def cmd_help(update: Update, context: ContextTypes.DEFAULT_TYPE):
if not await is_member(update, context): if not await is_member(update, context):
return await update.message.reply_text('⛔ 仅限群成员使用') return await update.message.reply_text('⛔ 仅限群成员使用')
data = load_data()
default_cnt = len([s for s in data['subs'] if s.get('alive', True)])
sg_cnt = len(data.get('sub_groups', []))
buttons = [ buttons = [
[InlineKeyboardButton("📋 订阅列表", callback_data='menu_list'), [InlineKeyboardButton(f"📦 默认节点池 ({default_cnt})", callback_data='menu_default')],
InlineKeyboardButton("📥 获取订阅", callback_data='menu_get')], [InlineKeyboardButton(f"📁 分组管理 ({sg_cnt}个分组)", callback_data='sg_list')],
[InlineKeyboardButton(" 添加订阅", callback_data='menu_add'), [InlineKeyboardButton("🔍 全部检测", callback_data='menu_check')],
InlineKeyboardButton("🗑 删除订阅", callback_data='menu_del')],
[InlineKeyboardButton("🔍 检测存活", callback_data='menu_check')],
[InlineKeyboardButton("📁 分组管理", callback_data='sg_list')],
] ]
if update.effective_user.id == ADMIN_ID: if update.effective_user.id == ADMIN_ID:
buttons.append([InlineKeyboardButton("⚙️ 绑定当前群", callback_data='menu_setgroup')]) buttons.append([InlineKeyboardButton("⚙️ 绑定当前群", callback_data='menu_setgroup')])
await send_del(update.message, await send_del(update.message,
'🚀 *订阅管理 Bot*\n\n' '🚀 *订阅管理 Bot*\n\n'
'直接发订阅链接自动入库\n' '订阅链接自动入库到默认节点池\n'
'点击下方按钮操作:', '通过分组管理创建独立订阅',
parse_mode='Markdown', reply_markup=InlineKeyboardMarkup(buttons)) parse_mode='Markdown', reply_markup=InlineKeyboardMarkup(buttons))
# --- menu callbacks --- # --- menu callbacks ---
@@ -132,7 +132,40 @@ async def cb_menu(update: Update, context: ContextTypes.DEFAULT_TYPE):
action = q.data action = q.data
data = load_data() data = load_data()
if action == 'menu_list': if action == 'menu_default':
cnt = len(data['subs'])
alive_cnt = len([s for s in data['subs'] if s.get('alive', True)])
buttons = [
[InlineKeyboardButton("📋 节点列表", callback_data='menu_list'),
InlineKeyboardButton("📥 获取订阅", callback_data='menu_get')],
[InlineKeyboardButton(" 添加节点", callback_data='menu_add'),
InlineKeyboardButton("🗑 删除节点", callback_data='menu_del')],
[InlineKeyboardButton("◀️ 返回主菜单", callback_data='menu_home')],
]
try:
await q.edit_message_text(
f'📦 *默认节点池*\n\n总计 {cnt} 个节点,{alive_cnt} 个可用',
parse_mode='Markdown', reply_markup=InlineKeyboardMarkup(buttons))
except:
await send_del(q.message,
f'📦 *默认节点池*\n\n总计 {cnt} 个节点,{alive_cnt} 个可用',
parse_mode='Markdown', reply_markup=InlineKeyboardMarkup(buttons))
elif action == 'menu_home':
default_cnt = len([s for s in data['subs'] if s.get('alive', True)])
sg_cnt = len(data.get('sub_groups', []))
buttons = [
[InlineKeyboardButton(f"📦 默认节点池 ({default_cnt})", callback_data='menu_default')],
[InlineKeyboardButton(f"📁 分组管理 ({sg_cnt}个分组)", callback_data='sg_list')],
[InlineKeyboardButton("🔍 全部检测", callback_data='menu_check')],
]
try:
await q.edit_message_text(
'🚀 *订阅管理 Bot*\n\n发送订阅链接自动入库到默认节点池\n或通过分组管理创建独立订阅',
parse_mode='Markdown', reply_markup=InlineKeyboardMarkup(buttons))
except: pass
elif action == 'menu_list':
if not data['subs']: if not data['subs']:
return await send_del(q.message, '📭 暂无订阅') return await send_del(q.message, '📭 暂无订阅')
lines = [f"`{i+1}` {'🟢' if s.get('alive',True) else '🔴'} [{s['type']}] {s['name']}" lines = [f"`{i+1}` {'🟢' if s.get('alive',True) else '🔴'} [{s['type']}] {s['name']}"
@@ -631,7 +664,7 @@ async def cb_subgroups(update: Update, context: ContextTypes.DEFAULT_TYPE):
cnt = len(sg.get('nodes', [])) cnt = len(sg.get('nodes', []))
buttons.append([InlineKeyboardButton(f"📁 {sg['name']} ({cnt}个节点)", callback_data=f"sg_detail_{sg['id']}")]) buttons.append([InlineKeyboardButton(f"📁 {sg['name']} ({cnt}个节点)", callback_data=f"sg_detail_{sg['id']}")])
buttons.append([InlineKeyboardButton(" 创建分组", callback_data='sg_create')]) buttons.append([InlineKeyboardButton(" 创建分组", callback_data='sg_create')])
buttons.append([InlineKeyboardButton("◀️ 返回主菜单", callback_data='menu_back')]) buttons.append([InlineKeyboardButton("◀️ 返回主菜单", callback_data='sg_back')])
text = f"📁 *分组管理*\n\n{len(sgs)} 个分组" if sgs else "📁 *分组管理*\n\n暂无分组,点击创建" text = f"📁 *分组管理*\n\n{len(sgs)} 个分组" if sgs else "📁 *分组管理*\n\n暂无分组,点击创建"
try: try:
await q.edit_message_text(text, parse_mode='Markdown', reply_markup=InlineKeyboardMarkup(buttons)) await q.edit_message_text(text, parse_mode='Markdown', reply_markup=InlineKeyboardMarkup(buttons))
@@ -659,6 +692,8 @@ async def cb_subgroups(update: Update, context: ContextTypes.DEFAULT_TYPE):
buttons = [ buttons = [
[InlineKeyboardButton("📋 节点列表", callback_data=f"sg_nodes_{gid}"), [InlineKeyboardButton("📋 节点列表", callback_data=f"sg_nodes_{gid}"),
InlineKeyboardButton(" 添加节点", callback_data=f"sg_add_{gid}")], InlineKeyboardButton(" 添加节点", callback_data=f"sg_add_{gid}")],
[InlineKeyboardButton("🗑 删除节点", callback_data=f"sg_delnodes_{gid}"),
InlineKeyboardButton("🔍 检测节点", callback_data=f"sg_check_{gid}")],
[InlineKeyboardButton("📥 获取订阅", callback_data=f"sg_getsub_{gid}"), [InlineKeyboardButton("📥 获取订阅", callback_data=f"sg_getsub_{gid}"),
InlineKeyboardButton("🔄 重置链接", callback_data=f"sg_reset_{gid}")], InlineKeyboardButton("🔄 重置链接", callback_data=f"sg_reset_{gid}")],
[InlineKeyboardButton("✏️ 重命名", callback_data=f"sg_rename_{gid}"), [InlineKeyboardButton("✏️ 重命名", callback_data=f"sg_rename_{gid}"),
@@ -699,6 +734,91 @@ async def cb_subgroups(update: Update, context: ContextTypes.DEFAULT_TYPE):
except: except:
await send_del(q.message, f'📎 请发送要添加到 [{name}] 的订阅链接:') await send_del(q.message, f'📎 请发送要添加到 [{name}] 的订阅链接:')
elif action.startswith('sg_check_'):
gid = action[len('sg_check_'):]
sg = next((g for g in sgs if g['id'] == gid), None)
if not sg: return
nodes = sg.get('nodes', [])
if not nodes:
buttons = [[InlineKeyboardButton("◀️ 返回", callback_data=f"sg_detail_{gid}")]]
return await send_del(q.message, f"📁 *{sg['name']}* 暂无节点",
parse_mode='Markdown', reply_markup=InlineKeyboardMarkup(buttons))
msg = await send_del(q.message, f"🔍 检测 *{sg['name']}* 中...", parse_mode='Markdown', delay=120)
results = []
for s in nodes:
ok = await check_node(s['link'], s['type'])
s['alive'] = ok
results.append(f"{'🟢' if ok else '🔴'} [{s['type']}] {s['name']}")
save_data(data)
try: await msg.delete()
except: pass
buttons = [[InlineKeyboardButton("◀️ 返回", callback_data=f"sg_detail_{gid}")]]
await send_del(q.message, f"📊 *{sg['name']}* 检测结果\n\n" + '\n'.join(results),
parse_mode='Markdown', reply_markup=InlineKeyboardMarkup(buttons))
elif action.startswith('sg_delnodes_'):
gid = action[len('sg_delnodes_'):]
sg = next((g for g in sgs if g['id'] == gid), None)
if not sg: return
nodes = sg.get('nodes', [])
if not nodes:
buttons = [[InlineKeyboardButton("◀️ 返回", callback_data=f"sg_detail_{gid}")]]
return await send_del(q.message, f"📁 *{sg['name']}* 暂无节点",
parse_mode='Markdown', reply_markup=InlineKeyboardMarkup(buttons))
buttons = []
for i, s in enumerate(nodes):
st = '🟢' if s.get('alive', True) else '🔴'
buttons.append([InlineKeyboardButton(
f"{st} [{s['type']}] {s['name']}", callback_data=f"sg_delnode_{gid}_{i}")])
buttons.append([InlineKeyboardButton("🗑 删除所有不可用", callback_data=f"sg_deldown_{gid}")])
buttons.append([InlineKeyboardButton("◀️ 返回", callback_data=f"sg_detail_{gid}")])
try:
await q.edit_message_text(f"🗑 *{sg['name']}* — 选择要删除的节点:",
parse_mode='Markdown', reply_markup=InlineKeyboardMarkup(buttons))
except:
await send_del(q.message, f"🗑 *{sg['name']}* — 选择要删除的节点:",
parse_mode='Markdown', reply_markup=InlineKeyboardMarkup(buttons))
elif action.startswith('sg_delnode_'):
# sg_delnode_{gid}_{idx}
parts = action[len('sg_delnode_'):].rsplit('_', 1)
if len(parts) != 2: return
gid, idx_str = parts
sg = next((g for g in sgs if g['id'] == gid), None)
if not sg: return
idx = int(idx_str)
nodes = sg.get('nodes', [])
if idx >= len(nodes):
return await send_del(q.message, '❌ 已失效')
removed = nodes.pop(idx)
save_data(data)
buttons = [[InlineKeyboardButton("◀️ 返回", callback_data=f"sg_detail_{gid}")]]
try:
await q.edit_message_text(f"✅ 已删除: [{removed['type']}] {removed['name']}",
reply_markup=InlineKeyboardMarkup(buttons))
except:
await send_del(q.message, f"✅ 已删除: [{removed['type']}] {removed['name']}",
reply_markup=InlineKeyboardMarkup(buttons))
elif action.startswith('sg_deldown_'):
gid = action[len('sg_deldown_'):]
sg = next((g for g in sgs if g['id'] == gid), None)
if not sg: return
dead = [s for s in sg.get('nodes', []) if not s.get('alive', True)]
if not dead:
buttons = [[InlineKeyboardButton("◀️ 返回", callback_data=f"sg_detail_{gid}")]]
return await send_del(q.message, '没有不可用节点', reply_markup=InlineKeyboardMarkup(buttons))
names = [s['name'] for s in dead]
sg['nodes'] = [s for s in sg['nodes'] if s.get('alive', True)]
save_data(data)
buttons = [[InlineKeyboardButton("◀️ 返回", callback_data=f"sg_detail_{gid}")]]
try:
await q.edit_message_text(f"🗑 已删除 {len(names)} 个不可用:\n" + '\n'.join(f'{n}' for n in names),
reply_markup=InlineKeyboardMarkup(buttons))
except:
await send_del(q.message, f"🗑 已删除 {len(names)} 个不可用:\n" + '\n'.join(f'{n}' for n in names),
reply_markup=InlineKeyboardMarkup(buttons))
elif action.startswith('sg_getsub_'): elif action.startswith('sg_getsub_'):
gid = action[len('sg_getsub_'):] gid = action[len('sg_getsub_'):]
sg = next((g for g in sgs if g['id'] == gid), None) sg = next((g for g in sgs if g['id'] == gid), None)
@@ -761,19 +881,19 @@ async def cb_subgroups(update: Update, context: ContextTypes.DEFAULT_TYPE):
except: except:
await send_del(q.message, "✅ 分组已删除", reply_markup=InlineKeyboardMarkup(buttons)) await send_del(q.message, "✅ 分组已删除", reply_markup=InlineKeyboardMarkup(buttons))
elif action == 'menu_back': elif action == 'sg_back':
# 返回主菜单 # 返回主菜单
default_cnt = len([s for s in data['subs'] if s.get('alive', True)])
sg_cnt = len(data.get('sub_groups', []))
buttons = [ buttons = [
[InlineKeyboardButton("📋 订阅列表", callback_data='menu_list'), [InlineKeyboardButton(f"📦 默认节点池 ({default_cnt})", callback_data='menu_default')],
InlineKeyboardButton("📥 获取订阅", callback_data='menu_get')], [InlineKeyboardButton(f"📁 分组管理 ({sg_cnt}个分组)", callback_data='sg_list')],
[InlineKeyboardButton(" 添加订阅", callback_data='menu_add'), [InlineKeyboardButton("🔍 全部检测", callback_data='menu_check')],
InlineKeyboardButton("🗑 删除订阅", callback_data='menu_del')],
[InlineKeyboardButton("🔍 检测存活", callback_data='menu_check')],
[InlineKeyboardButton("📁 分组管理", callback_data='sg_list')],
] ]
try: try:
await q.edit_message_text('🚀 *订阅管理 Bot*\n\n直接发订阅链接自动入库\n或点击下方按钮操作:', await q.edit_message_text(
parse_mode='Markdown', reply_markup=InlineKeyboardMarkup(buttons)) '🚀 *订阅管理 Bot*\n\n发送订阅链接自动入库到默认节点池\n或通过分组管理创建独立订阅',
parse_mode='Markdown', reply_markup=InlineKeyboardMarkup(buttons))
except: pass except: pass
async def auto_cleanup(bot_token): async def auto_cleanup(bot_token):
@@ -826,7 +946,7 @@ def main():
app.add_handler(CallbackQueryHandler(cb_multisel, pattern='^msel_')) app.add_handler(CallbackQueryHandler(cb_multisel, pattern='^msel_'))
app.add_handler(CallbackQueryHandler(cb_multiout, pattern='^mout_')) app.add_handler(CallbackQueryHandler(cb_multiout, pattern='^mout_'))
app.add_handler(CallbackQueryHandler(cb_getsub, pattern='^(get_|pick_|send_|fmt_|out_)')) app.add_handler(CallbackQueryHandler(cb_getsub, pattern='^(get_|pick_|send_|fmt_|out_)'))
app.add_handler(CallbackQueryHandler(cb_subgroups, pattern='^(sg_|menu_back)')) app.add_handler(CallbackQueryHandler(cb_subgroups, pattern='^sg_'))
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, auto_detect)) app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, auto_detect))
log.info('Sub Bot starting polling...') log.info('Sub Bot starting polling...')
threading.Thread(target=start_http, daemon=True).start() threading.Thread(target=start_http, daemon=True).start()