"""登录链路:base64 账号密码、图形验证码识别、ITLMS 换票。""" import base64 import re import time from . import http _OCR = None def b64(text): return base64.b64encode(str(text).encode("utf-8")).decode() def ocr(): """4 位图形验证码:ddddocr 离线识别。""" global _OCR if _OCR is None: try: import ddddocr except Exception as e: raise RuntimeError("登录要过图形验证码,需要先装 ddddocr(pip install ddddocr):%s" % e) _OCR = ddddocr.DdddOcr(show_ad=False) return _OCR def captcha(s): raw = s.get(http.G + "/login/readCaptcha.action?d=%.6f" % time.time(), timeout=30).text return base64.b64decode(raw.strip().strip('"').split(",", 1)[-1]) def login_error(s): html = s.get(http.G + "/login.jsp", timeout=30).text m = re.search(r'id="errorText"[^>]*>([^<]*)<', html) return (m.group(1).strip() if m else "") def sso(s): """jsglpt 会话换 ITLMS 票,票按 `ITLMS-xxx||ticketId` 两段交给 jsxx。 交接入口来自 jsglpt 首页那个隐藏表单: `GET https://jsxx.gds.edu.cn/nts/loginToStoreByAccount?ticket=&ticketId=` """ r = s.post(http.G + "/teacher/ssoItlmsGxkTicket.action?verificaCode=true&page=", timeout=30) ticket = (r.text or "").strip() if not ticket.startswith("ITLMS-") or "||" not in ticket: raise http.expired("取 ITLMS 票失败:%s" % (ticket[:80] or "空响应")) t, tid = ticket.split("||", 1) q = s.get(http.X + "/nts/loginToStoreByAccount", params={"ticket": t, "ticketId": tid}, timeout=30) if "logout" not in q.text and "我的课程" not in q.text: raise http.expired("带票进学习平台失败,票可能已失效") return s