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):
|
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()
|
||||||
|
|||||||
Reference in New Issue
Block a user