Merge pull request #595 from Michaelxwb/main

修复个人微信前端在 headless 容器内无法登录
This commit is contained in:
LJQ authored and GitHub committed 2026-06-13 15:52:18 +08:00
commit 9aeb80fd52
1 file changed
+37 -12
+37 -12
View File
@@ -63,20 +63,42 @@ class WxBotClient:
return r.json()
def login_qr(self, poll_interval=2):
r = requests.get(f'{API}/ilink/bot/get_bot_qrcode', params={'bot_type': 3}, headers={'User-Agent': UA}, timeout=10)
r.raise_for_status()
d = r.json()
# 获取二维码:对限流/缺字段(无 'qrcode')退避重试,避免崩溃→重启→更狠地打接口的死亡螺旋
d = {}
for attempt in range(6):
try:
r = requests.get(f'{API}/ilink/bot/get_bot_qrcode', params={'bot_type': 3}, headers={'User-Agent': UA}, timeout=10)
r.raise_for_status()
d = r.json()
except requests.exceptions.RequestException as e:
print(f'[QR登录] 获取二维码失败({e}),{2 ** attempt}s 后重试...'); time.sleep(2 ** attempt); continue
if d.get('qrcode') and d.get('qrcode_img_content'): # 二维码 ID + 可扫图都就绪才算成功
break
print(f'[QR登录] 二维码未就绪(可能被限流,ret={d.get("ret")}),{2 ** attempt}s 后重试...')
time.sleep(2 ** attempt)
if not (d.get('qrcode') and d.get('qrcode_img_content')):
raise RuntimeError('多次重试仍未获取到可扫二维码(疑似限流),请稍后重试')
qr_id, url = d['qrcode'], d.get('qrcode_img_content', '')
print(f'[QR登录] ID: {qr_id}')
if url:
img = self._tf.parent / 'wx_qr.png'
qrcode.make(url).save(str(img)) # 保存到文件,不弹浏览器
# 先打 ASCII 二维码(纯文本,无需 PIL;容器/无头环境靠它扫码)
qr = qrcode.QRCode(border=1); qr.add_data(url); qr.make(fit=True); qr.print_ascii(invert=True)
# 再尝试存 PNG 兜底——依赖 PIL,缺失/失败不应让登录崩溃
try:
qrcode.make(url).save(str(self._tf.parent / 'wx_qr.png'))
except Exception as e:
print(f'[QR登录] PNG 兜底保存失败({e}),用上方 ASCII 二维码扫码即可')
last = ''
while True:
time.sleep(poll_interval)
try: s = requests.get(f'{API}/ilink/bot/get_qrcode_status', params={'qrcode': qr_id}, headers={'User-Agent': UA}, timeout=60).json()
except requests.exceptions.ReadTimeout: continue
# 轮询状态:对所有网络异常 / 非 JSON(被限流时常返回 HTML)容错重试,
# 不让单次抖动把进程打崩——配合 restart:unless-stopped 否则会死亡螺旋
try:
s = requests.get(f'{API}/ilink/bot/get_qrcode_status', params={'qrcode': qr_id}, headers={'User-Agent': UA}, timeout=60).json()
except requests.exceptions.RequestException:
continue
except ValueError: # 响应非 JSON(限流/网关页)
time.sleep(poll_interval); continue
st = s.get('status', '')
if st != last: print(f' 状态: {st}'); last = st
if st == 'confirmed':
@@ -415,11 +437,14 @@ if __name__ == '__main__':
print(f'[NEW] Process starting {time.strftime("%m-%d %H:%M")}')
bot = WxBotClient()
if _do_relogin or not bot.token:
if not sys.stdout.isatty():
print('[Bot] no token and not interactive, exit.'); sys.exit(1)
sys.stdout = sys.stderr = sys.__stdout__ # restore for QR display
bot.login_qr()
sys.stdout = sys.stderr = _logf
# QR 登录在无 TTY 的容器里也可用:把二维码打到真实 stdout(docker logs
# 可见),而不是日志文件——之前在重定向后才判 isatty(),文件句柄恒 false
# 导致容器内必然退出,无法首次登录。PNG 仍存 ~/.wxbot/wx_qr.png 作兜底。
sys.stdout = sys.stderr = sys.__stdout__ # restore for QR display (real stdout / container log)
try:
bot.login_qr()
finally:
sys.stdout = sys.stderr = _logf
threading.Thread(target=agent.run, daemon=True).start()
print(f'WeChat Bot 已启动 (bot_id={bot.bot_id})', file=sys.__stdout__)
try: