fix: 检测改三态(🟢通/🟡未知/🔴不通),去掉自动检测删除

This commit is contained in:
mango
2026-02-25 18:08:52 +08:00
parent ab69bb4864
commit ea233c31b6

57
bot.py
View File

@@ -15,6 +15,13 @@ SUB_SECRET = os.environ.get('SUB_SECRET', 'changeme')
SUB_HOST = os.environ.get('SUB_HOST', 'substore.mjjtop.com')
WAITING_ADD = set() # user_ids waiting to add sub
def _status_icon(s):
"""节点状态图标True=🟢 False=🔴 None=🟡"""
v = s.get('alive')
if v is True: return '🟢'
if v is False: return '🔴'
return '🟡'
def get_default_secret():
"""获取默认节点池的 secret优先用 data.json 里的,没有就用环境变量"""
data = load_data()
@@ -190,7 +197,7 @@ async def cb_menu(update: Update, context: ContextTypes.DEFAULT_TYPE):
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']}"
lines = [f"`{i+1}` {_status_icon(s)} [{s['type']}] {s['name']}"
for i, s in enumerate(data['subs'])]
await send_del(q.message, '📋 *订阅列表*\n\n' + '\n'.join(lines), parse_mode='Markdown')
@@ -216,7 +223,7 @@ async def cb_menu(update: Update, context: ContextTypes.DEFAULT_TYPE):
buttons = []
for i, s in enumerate(data['subs']):
if uid != ADMIN_ID and uid != s.get('added_by'): continue
st = '🟢' if s.get('alive', True) else '🔴'
st = _status_icon(s)
buttons.append([InlineKeyboardButton(f"{st} [{s['type']}] {s['name']}", callback_data=f'del_{i}')])
if not buttons:
return await send_del(q.message, '没有可删除的订阅')
@@ -236,7 +243,7 @@ async def cb_menu(update: Update, context: ContextTypes.DEFAULT_TYPE):
for s in data['subs']:
ok = await check_node(s['link'], s['type'])
s['alive'] = ok
results.append(f"{'🟢' if ok else '🔴'} [{s['type']}] {s['name']}")
results.append(f"{_status_icon(s)} [{s['type']}] {s['name']}")
# 检测所有订阅分组
for sg in data.get('sub_groups', []):
if not sg.get('nodes'): continue
@@ -244,7 +251,7 @@ async def cb_menu(update: Update, context: ContextTypes.DEFAULT_TYPE):
for s in sg['nodes']:
ok = await check_node(s['link'], s['type'])
s['alive'] = ok
results.append(f"{'🟢' if ok else '🔴'} [{s['type']}] {s['name']}")
results.append(f"{_status_icon(s)} [{s['type']}] {s['name']}")
save_data(data)
try: await msg.delete()
except: pass
@@ -355,7 +362,7 @@ async def cb_getsub(update: Update, context: ContextTypes.DEFAULT_TYPE):
for i, s in enumerate(subs):
gi = alive.index(s)
buttons.append([InlineKeyboardButton(
f"{'🟢' if s.get('alive',True) else '🔴'} {s['name']}", callback_data=f'pick_{gi}')])
f"{_status_icon(s)} {s['name']}", callback_data=f'pick_{gi}')])
buttons.append([InlineKeyboardButton(f"📦 全部 ({len(subs)})", callback_data=f'send_{proto}_raw')])
return await send_del(q.message, '选择节点:', reply_markup=InlineKeyboardMarkup(buttons))
@@ -545,13 +552,16 @@ async def auto_detect(update: Update, context: ContextTypes.DEFAULT_TYPE):
# --- node health check ---
async def check_node(link, proto):
"""检测节点存活TCP 直连,连不通返回 None未知"""
try:
server, port = parse_sp(link, proto)
if not server: return False
if not server: return None
_, w = await asyncio.wait_for(asyncio.open_connection(server, int(port)), timeout=5)
w.close(); await w.wait_closed()
return True
except: return False
except:
return None # 连不通返回未知,不判定为死
def parse_sp(link, proto):
try:
@@ -738,7 +748,7 @@ async def cb_subgroups(update: Update, context: ContextTypes.DEFAULT_TYPE):
else:
lines = [f"📁 *{sg['name']}* 节点列表\n"]
for i, s in enumerate(nodes):
st = '🟢' if s.get('alive', True) else '🔴'
st = _status_icon(s)
lines.append(f"`{i+1}` {st} [{s['type']}] {s['name']}")
text = '\n'.join(lines)
buttons = [[InlineKeyboardButton("◀️ 返回", callback_data=f"sg_detail_{gid}")]]
@@ -771,7 +781,7 @@ async def cb_subgroups(update: Update, context: ContextTypes.DEFAULT_TYPE):
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']}")
results.append(f"{_status_icon(s)} [{s['type']}] {s['name']}")
save_data(data)
try: await msg.delete()
except: pass
@@ -790,7 +800,7 @@ async def cb_subgroups(update: Update, context: ContextTypes.DEFAULT_TYPE):
parse_mode='Markdown', reply_markup=InlineKeyboardMarkup(buttons))
buttons = []
for i, s in enumerate(nodes):
st = '🟢' if s.get('alive', True) else '🔴'
st = _status_icon(s)
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}")])
@@ -919,32 +929,6 @@ async def cb_subgroups(update: Update, context: ContextTypes.DEFAULT_TYPE):
parse_mode='Markdown', reply_markup=InlineKeyboardMarkup(buttons))
except: pass
async def auto_cleanup(bot_token):
"""Every 6h: check all nodes, update alive status only (no auto-delete)"""
await asyncio.sleep(60)
while True:
data = load_data()
changed = False
# 默认分组:只更新状态
for s in data['subs']:
ok = await check_node(s['link'], s['type'])
if s.get('alive', True) != ok:
s['alive'] = ok
changed = True
# 订阅分组:只更新状态
for sg in data.get('sub_groups', []):
for s in sg.get('nodes', []):
ok = await check_node(s['link'], s['type'])
if s.get('alive', True) != ok:
s['alive'] = ok
changed = True
if changed:
save_data(data)
log.info('Auto check: updated node status')
await asyncio.sleep(21600)
def run_cleanup():
asyncio.run(auto_cleanup(TOKEN))
def main():
app = Application.builder().token(TOKEN).build()
@@ -960,7 +944,6 @@ def main():
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, auto_detect))
log.info('Sub Bot starting polling...')
threading.Thread(target=start_http, daemon=True).start()
threading.Thread(target=run_cleanup, daemon=True).start()
app.run_polling(drop_pending_updates=True)
if __name__ == '__main__':