fix: 重构UI结构+分组检测+分组删除节点+返回按钮修复
This commit is contained in:
160
bot.py
160
bot.py
@@ -109,20 +109,20 @@ def add_sub_to_group(link, user, group_id):
|
||||
async def cmd_help(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
if not await is_member(update, context):
|
||||
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 = [
|
||||
[InlineKeyboardButton("📋 订阅列表", callback_data='menu_list'),
|
||||
InlineKeyboardButton("📥 获取订阅", callback_data='menu_get')],
|
||||
[InlineKeyboardButton("➕ 添加订阅", callback_data='menu_add'),
|
||||
InlineKeyboardButton("🗑 删除订阅", callback_data='menu_del')],
|
||||
[InlineKeyboardButton("🔍 检测存活", callback_data='menu_check')],
|
||||
[InlineKeyboardButton("📁 分组管理", callback_data='sg_list')],
|
||||
[InlineKeyboardButton(f"📦 默认节点池 ({default_cnt})", callback_data='menu_default')],
|
||||
[InlineKeyboardButton(f"📁 分组管理 ({sg_cnt}个分组)", callback_data='sg_list')],
|
||||
[InlineKeyboardButton("🔍 全部检测", callback_data='menu_check')],
|
||||
]
|
||||
if update.effective_user.id == ADMIN_ID:
|
||||
buttons.append([InlineKeyboardButton("⚙️ 绑定当前群", callback_data='menu_setgroup')])
|
||||
await send_del(update.message,
|
||||
'🚀 *订阅管理 Bot*\n\n'
|
||||
'直接发订阅链接自动入库\n'
|
||||
'或点击下方按钮操作:',
|
||||
'发送订阅链接自动入库到默认节点池\n'
|
||||
'或通过分组管理创建独立订阅',
|
||||
parse_mode='Markdown', reply_markup=InlineKeyboardMarkup(buttons))
|
||||
|
||||
# --- menu callbacks ---
|
||||
@@ -132,7 +132,40 @@ async def cb_menu(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
action = q.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']:
|
||||
return await send_del(q.message, '📭 暂无订阅')
|
||||
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', []))
|
||||
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='menu_back')])
|
||||
buttons.append([InlineKeyboardButton("◀️ 返回主菜单", callback_data='sg_back')])
|
||||
text = f"📁 *分组管理*\n\n共 {len(sgs)} 个分组" if sgs else "📁 *分组管理*\n\n暂无分组,点击创建"
|
||||
try:
|
||||
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 = [
|
||||
[InlineKeyboardButton("📋 节点列表", callback_data=f"sg_nodes_{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_reset_{gid}")],
|
||||
[InlineKeyboardButton("✏️ 重命名", callback_data=f"sg_rename_{gid}"),
|
||||
@@ -699,6 +734,91 @@ async def cb_subgroups(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
except:
|
||||
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_'):
|
||||
gid = action[len('sg_getsub_'):]
|
||||
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:
|
||||
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 = [
|
||||
[InlineKeyboardButton("📋 订阅列表", callback_data='menu_list'),
|
||||
InlineKeyboardButton("📥 获取订阅", callback_data='menu_get')],
|
||||
[InlineKeyboardButton("➕ 添加订阅", callback_data='menu_add'),
|
||||
InlineKeyboardButton("🗑 删除订阅", callback_data='menu_del')],
|
||||
[InlineKeyboardButton("🔍 检测存活", callback_data='menu_check')],
|
||||
[InlineKeyboardButton("📁 分组管理", callback_data='sg_list')],
|
||||
[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))
|
||||
await q.edit_message_text(
|
||||
'🚀 *订阅管理 Bot*\n\n发送订阅链接自动入库到默认节点池\n或通过分组管理创建独立订阅',
|
||||
parse_mode='Markdown', reply_markup=InlineKeyboardMarkup(buttons))
|
||||
except: pass
|
||||
|
||||
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_multiout, pattern='^mout_'))
|
||||
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))
|
||||
log.info('Sub Bot starting polling...')
|
||||
threading.Thread(target=start_http, daemon=True).start()
|
||||
|
||||
Reference in New Issue
Block a user