Bot-hosting 免费主机自动续期脚本深度解析:原理 + 全流程
先说它是干什么的
Bot-hosting.net 是一个免费托管 Discord Bot 的平台。免费套餐有个限制——服务每 4 天就过期,必须手动点击 Renew 才能续期。
手动操作?不存在的。eooce 写了 Auto-Renew-Bothosting ,一个运行在 GitHub Actions 上的 Python 脚本,自动完成续期全流程。这篇文章就是拆解它的实现原理。
整体架构:4 个模块
脚本的逻辑分为四个核心职责:
Cookie 注入登录 — 用 session_token 跳过账号密码登录
Turnstile 验证绕过 — 处理 Cloudflare 的机器人检测
续期操作 — 找到 Renew 按钮并点击
Token 自动刷新 — 把新的 session_token 写回 GitHub Secrets
依赖工具链
1 2 import os, re, sys, time, requests, subprocessfrom seleniumbase import SB
核心依赖是 SeleniumBase (简称 SB)。它是对 Selenium 的封装,内置了 uc(undetected-chrome)模式,专门用于绕过反爬检测。脚本以 with SB(uc=True, headless=HEADLESS) as sb: 方式启动浏览器实例,整个流程都在这个上下文里跑。
第一步:环境变量读取
1 2 3 4 SESSION_TOKEN=os.env...N") or " " GH_TOKEN=os.env...N" ) or "" TG_BOT_TOKEN=os.env...N") or " " TG_CHAT_ID = os.environ.get(" TG_CHAT_ID") or " "
所有敏感信息都从环境变量读取,配合 GitHub Repository Secrets 使用。脚本启动时就校验 SESSION_TOKEN,为空直接 sys.exit(1) 退出——干净利落,不做无意义的后续操作。
第二步:Cookie 注入,绕过登录表单
直接打开登录页走账号密码流程?风险太高——容易触发 2FA,也容易被 Cloudflare 拦截。聪明的做法是注入已有的 session_token cookie :
1 2 3 4 5 6 7 8 9 10 11 12 COOKIES = { "session_token" : SESSION_TOKEN, "login" : "true" , "theme" : "system" , } sb.open ("https://bot-hosting.net/" ) sb.wait_for_ready_state_complete()for name, value in COOKIES.items(): if value: sb.add_cookie({"name" : name, "value" : value, "domain" : "bot-hosting.net" })
流程是:先访问主页让浏览器建立与目标域的连接,然后通过 sb.add_cookie() 把 session 塞进去,再跳转到账单页 /a/billings 验证是否登录成功。
登录验证逻辑很直接——检查页面 title 是否符合预期:
1 2 3 4 if current_title != "Bot-Hosting.net | A Free Host For Discord Bots" : print ("❌ 登录失败,Cookie 已失效" ) send_telegram_message(...) return
title 不对就说明 Cookie 失效了,直接退出并推送 TG 通知。
第三步:提取到期日期
登录成功后,脚本用正则从页面 HTML 源码里抓到期日期:
1 2 3 4 5 6 patterns = [ r"[Ee]xpires\s*[:\-]?\s*(\d{4}/\d{2}/\d{2})" , r"[Ee]xpires\s*[:\-]?\s*(\d{2}/\d{2}/\d{4})" , r"(\d{4}/\d{2}/\d{2})\s*[\-–]\s*renew" , r"(\d{2}/\d{2}/\d{4})\s*[\-–]\s*renew" , ]
同时还处理了 MM/DD/YYYY 和 YYYY/MM/DD 两种日期格式的转换,兼容性不错。
第四步:识别续期按钮状态
脚本需要判断当前是「可以续期」还是「还在倒计时」:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 possible_selectors = [ 'button:contains("Renew")' , 'a:contains("Renew")' , '[class*="renew"]' , '[class*="Renew"]' , ]for selector in possible_selectors: if sb.is_element_visible(selector): button_text = sb.get_text(selector) if "Renew in" in button_text: countdown_text = re.search(r"Renew in (\d{2}:\d{2}:\d{2})" , button_text).group(1 ) else : outer_renew_selector = selector
关键区分点:按钮文字是 "Renew" 还是 "Renew in HH:MM:SS"。前者可以点,后者表示还没到时间。
第五步:Turnstile 验证绕过(最硬核的部分)
点击 Renew 后,页面会弹出一个含 Cloudflare Turnstile 的 modal。Turnstile 是比普通 reCAPTCHA 更难绕的验证机制,专门针对自动化脚本设计。
SeleniumBase 的 uc_gui_click_captcha() 是关键武器:
1 2 3 4 5 6 7 8 9 10 11 12 for attempt in range (1 , 4 ): try : sb.uc_gui_click_captcha() time.sleep(8 ) except Exception as e: print (f"⚠️ 点击 Turnstile 出错: {e} " ) if wait_for_turnstile_pass(sb, timeout=20 ): turnstile_passed = True break else : print (f"⏳ 第 {attempt} 次未通过,重试..." )
验证通过的判断逻辑是检测页面中是否还存在 Turnstile 特征字符串:
1 2 3 4 cf_indicators = ["verify you are human" , "确认您是真人" , "troubleshoot" , "just a moment" ] page_lower = sb.get_page_source().lower()if not any (x in page_lower for x in cf_indicators): return True
最多重试 3 次,全部失败则推送 TG 通知并退出。
第六步:点击续期 + 结果验证
Turnstile 过了之后,点击 modal 里的续期按钮:
1 sb.click('button:contains("Renew for 4 days")' , timeout=8 )
然后等待 3 秒,再次抓取页面源码,用正则检测续期结果:
1 2 3 4 new_match = re.search(r"Renew in (\d{2}:\d{2}:\d{2})" , new_page_text)if new_match: send_telegram_message(format_notification("✅ 续期成功" , ...))
逻辑判断链:续期后按钮变成 Renew in XX:XX:XX 就是成功;到期日期有变化也算成功;其他情况推送「请手动检查」警告。
第七步:SESSION_TOKEN 自动刷新
这是整个脚本最有价值的自动化设计之一。浏览器跑完整个流程后,session_token 可能已经刷新了(平台服务端轮转 Token 是常见安全实践)。
脚本会主动检测并回写:
1 2 3 4 5 6 7 8 9 10 new_token, token_expiry = get_cookie_info(sb, "session_token" )def should_update_cookie (new_value, old_value, expiry_dt, days_threshold=3 ): if new_value != old_value: return True if expiry_dt: remaining = (expiry_dt - datetime.now()).total_seconds() if remaining < days_threshold * 24 * 3600 : return True return False
如果需要更新,调用 gh secret set 命令写回 GitHub Secrets:
1 2 3 4 proc = subprocess.run( ["gh" , "secret" , "set" , "SESSION_TOKEN" , "--body" , new_value], capture_output=True , text=True , timeout=30 , env=env )
这意味着脚本是自维护 的——只要 GH_TOKEN 有效,SESSION_TOKEN 过期了也能自动续上,形成闭环。
通知格式
每次执行结果都会格式化成一条 TG 消息,包含:账户(脱敏)、执行状态、到期时间、执行时间(上海时区)。
1 2 3 4 5 6 🇫🇮 Bot-hosting 续期通知 ✅ 续期成功 👤 登录账户 : zh****bi@163.com 📅 到期日期 : 2026/07/09 ⏱️ 登录时间 : 2026-07-05 16:40:24
时区处理用了 time.gmtime(time.time() + 8 * 3600) 这种手动加 8 小时的方式——GitHub Actions Runner 跑在 UTC,不用这个就会显示成凌晨。
执行流程总结
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 启动浏览器(undetected-chrome 模式) ↓ 注入 session_token Cookie ↓ 跳转账单页 → 验证登录状态 ↓ 提取当前到期日期 ↓ 检测 Renew 按钮状态 ├── 倒计时中 → 推送「未到续期时间」通知 → 退出 └── 可续期 → 点击按钮 ↓ 弹出 Turnstile 验证(最多重试 3 次) ↓ 点击「Renew for 4 days」 ↓ 验证结果 → 推送通知 ↓ 检查 SESSION_TOKEN 是否需要更新 ↓ 如需更新 → gh secret set → 写回 GitHub Secrets
几个值得注意的细节
1. uc=True 是关键
普通 Selenium 早就被各大反爬系统列入黑名单。SB 的 uc 模式通过修改 Chrome 的 CDP 特征来规避检测,是跑这类自动化脚本的标配。
2. Cookie 注入 vs 账号密码登录
直接注入 session_token 比走登录流程风险低很多。登录流程容易触发 2FA、图形验证码、设备风控——而 Cookie 本身就是「已通过验证的凭证」。
3. 3 次 Turnstile 重试机制
Turnstile 不是每次都能一次过,加上 sleep(8) 等待页面响应,3 次重试覆盖了大多数情况。
4. 自维护的 Token 回写
这个设计让整个脚本真正做到了无人值守。部署一次,只要 GH_TOKEN 不失效,理论上可以无限期自动续期。
这个脚本结构清晰,错误处理完善,通知机制贴心。如果你有类似「需要定期点击某个按钮才能保活」的服务,这套 SeleniumBase + GitHub Actions + Secrets 自动回写的模式值得直接复用。
本文由 BOSH 的博客助手 HerMes 整理 🤖