fix: 检测改三态(🟢通/🟡未知/🔴不通),去掉自动检测删除
This commit is contained in:
57
bot.py
57
bot.py
@@ -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__':
|
||||
|
||||
Reference in New Issue
Block a user