From 4dd77bf0b730c56d03aa546162a733d3c19c3f72 Mon Sep 17 00:00:00 2001 From: zhilv Date: Fri, 28 Nov 2025 20:12:51 +0800 Subject: [PATCH] =?UTF-8?q?feat(work):=20=E6=96=B0=E5=A2=9E=E5=88=B7?= =?UTF-8?q?=E4=BD=9C=E4=B8=9A=E6=A8=A1=E5=BC=8F=EF=BC=8C=E5=85=B7=E4=BD=93?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E8=AF=B7=E6=9F=A5=E7=9C=8B=20main=20?= =?UTF-8?q?=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __pycache__/deepseek.cpython-313.pyc | Bin 0 -> 2545 bytes __pycache__/doubao.cpython-313.pyc | Bin 0 -> 2569 bytes deepseek.py | 64 +++++++++ doubao.py | 65 +++++++++ main.py | 196 ++++++++++++++++++++++++--- pyproject.toml | 1 + 6 files changed, 307 insertions(+), 19 deletions(-) create mode 100644 __pycache__/deepseek.cpython-313.pyc create mode 100644 __pycache__/doubao.cpython-313.pyc create mode 100644 deepseek.py create mode 100644 doubao.py diff --git a/__pycache__/deepseek.cpython-313.pyc b/__pycache__/deepseek.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5c8b719a7306a1f58f61681249e373b7a585fd1c GIT binary patch literal 2545 zcmb_e?Qavw8K3p;+Rny~P3%BOxiU>qb4KY}a3mFQs+y9ZBRM&ow@5`;S}pd(Sy=D- zX2(Z@REeq;XH-#vdMQ^G^=MV;I9iET?FUL*^WkIDXMkRF|P4|3YG>XQNp^PCzosNV;8ZE_)=#lRrWaSYuVn*B;Ff?P( zNEk^yVhkCnpidh+f_~V@h(50vSzV>^QuK50#Y`{Eb6qJuErmmjs!~=EsNj*G;qUDp^LUTUyo70Nw61=LWXmEpftf@U3^+18!uu^XXcpG1K{*c9Pxz4#bXpQ%Q~kPk;rA!pmN zigHSUr|0J9Pa5;{i{|Xf6XxRl>5HQ)jMe=k`Mg!P^$PIJ2cGEwUM|98AT!UCH6A}} z`NV8cC#O=8E01_Sj{((f>bYF562_xt#|FIMC@4O0mVL1d%)dH!q;$-ykkZLDw&J-Z zqYkLjQu+La^QG-zbqHXZKt5)gd`F+_dSA2)Jo)3$TtXl3-1BjE=Z%Hy3pWba3xAuK zevqBn7|B0Vo@J-56KBk1Dr55PVS`K_Zm(ap>jwMoh4$n0{GP z9DZ5$YENWMAoTz6w+|WdeJD3jxR~tk<=4t97I48|^BEBgQmE$;9uw6C$iWp+hK}cC zuGWAQ4v*KMa#>a4g9)R=s!;(wM>&m(HE|_yHwbKma~~Wl;2F$ggeSuoCX_Qclw1*8 z&DBbyRtIMGDhH4C{L?Y;mXqlY=;%)2IZlaRhhs{7K?jU66@XDJbjfWBitw+1$iE5A zCA5**b>ryuqwUN@D>Ko~+4peN{GffKYKvdj=T&dV)#%0)=5SS6u zge!&FWz%9%1J5=X@lEq16hsdJj0$7X2%h`)#W*g$5J^kSKn}594xz>Et zyav3K;lANATV-7OR-k|MtE-Q%d=wgU=~yWH$-9q!(d;yTw)vNvT$&4IH}7|Tdi(KT z|McYU^_ee?dmEt3>5XTj aNKJl!;oYV0EnR!#@5(FPFCk?t$m~CHu#yV^ literal 0 HcmV?d00001 diff --git a/__pycache__/doubao.cpython-313.pyc b/__pycache__/doubao.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..403486d2c1321d6704854b3d0be13c0ee885e887 GIT binary patch literal 2569 zcmb_e|7#q_6`#G`y*urlboz3#WyP%qSw%i0?bZ6?)^=#CST?cToRZrLYJ9M)?nbAT z_jWI{bFyS8xU@Jj#l@yk+)~px5QyUz+>rj@x(WRkdYE!znt)3rfBC6rRLHM=vwLzJ zn?ir+BE6Y;Z{C-A?=x?1yS-gQ@M)Ja_P62)eIXV*Um$_S_8lPW$Uug43ej+ss08cC zsmP2>Wh{8|DP=~bDnL!FF-Od2i3jOdXcBV=U}GGa#DXfre; zVI+-qJz{hisbK6hx`MIW=n-R9G19t9AkK-GS?Uq6CW{vm^4C)%V zp?JxgKcXGfah_Nu%)r<%vzbGN!BuNrT=YDf_`|w3rsHYC2(8#I3HBB}N`-?kto?pQ z*T!``TVvH4!?U>`o(?U#waPH|7QUteR_!A3b!|e&Cl_#~<}kbJK;*#*eN0y1gd-51 z?FzjLoR2Ny<8Ilp{Y5xYrDFLbnCNAF1P@P*92=R204D=mtB}5;<+>ijrLTqC${wU+ z0g~i0XLSTq!fMn7x0qC_Y!&;ALY{Q(5S-P+!iEY<%%sd1;9{&%J9Ek0J4tGJe8ekVH=_R4uk@QkmO^}ELu}T+QF$2G33LMIpjRm zETfE);~gQ6%&FH;nx~GRKEHpFv8sP0o3-d!z37h46MIRVZP{}~<{^h!t7>PL$1@Qg zgPD1jtn&E0-~A$Qe~V@9L+JluZwMLjA(Uy$ogWD6IO-Ep8ZAPtN2`=9+vMEn z*yO=)kA0U%{Z*fl3gAhho-G0d4f`zIOEL(MHd{de6Q_PqyBZ zZS)+f_Z+(4J>HC>{=u82hW>*({B=%k>>2pO^zE5jGk^Tay^-Vf&J#^l-qq2Jqx7C; z7fOGpiQ3ZITD-Xzb@nw<1NGFv4XK_QY@`m None: + self.key = os.environ.get("DEEPSEEK_API_KEY") + if self.key: + raise Exception("找不到 DEEPSEEK_API_KEY 请设置环境变量在运行代码") + self.client = OpenAI( + api_key=self.key, + base_url="https://api.deepseek.com", + ) + + def chat(self, question_json: dict) -> str: + response = self.client.chat.completions.create( + model="deepseek-chat", + # model="deepseek-reasoner", + messages=[ + {"role": "system", "content": self.system_prompt}, + {"role": "user", "content": json.dumps(question_json)}, + ], + stream=False, + ) + + # print(response.choices[0].message.content) + return ( + response.choices[0].message.content + if response.choices[0].message.content + else "" + ) + + +if __name__ == "__main__": + question = { + "id": "25594265", + "index": "1", + "type": "单选", + "title": "下列哪个属于AI视觉识别应用?", + "chooies": [ + {"num": "A", "txt": "音乐合成"}, + {"num": "B", "txt": "植物识别"}, + {"num": "C", "txt": "文案创作"}, + {"num": "D", "txt": "机器翻译"}, + ], + } + DeepSeek().chat(question) diff --git a/doubao.py b/doubao.py new file mode 100644 index 0000000..375393b --- /dev/null +++ b/doubao.py @@ -0,0 +1,65 @@ +# Please install OpenAI SDK first: `pip3 install openai` +import os +import json +from openai import OpenAI +from dotenv import load_dotenv + +load_dotenv() + + +class DOUBAO: + system_prompt = ( + "You are a professional QA answer assistant.\n" + "The user will provide a JSON object containing a question.\n" + "Your job:\n" + "1. Read the 'title' and the 'chooies'.\n" + "2. Determine the correct answer(s).\n" + "3. Output ONLY the 'num' of the correct choices.\n" + "4. If multiple answers exist, join them with English commas, e.g., 'A,C,D'.\n" + "5. If you cannot determine the answer confidently, return an empty string.\n" + "6. Do not output explanations or any extra text." + ) + + def __init__(self) -> None: + self.key = os.environ.get("DOUBAO_API_KEY") + if self.key: + raise Exception("找不到 DOUBAO_API_KEY 请设置环境变量在运行代码") + self.client = OpenAI( + api_key=self.key, + base_url="https://ark.cn-beijing.volces.com/api/v3", + ) + + def chat(self, question_json: dict) -> str: + response = self.client.chat.completions.create( + # model="deepseek-chat", + model="doubao-seed-code-preview-251028", + # model="deepseek-reasoner", + messages=[ + {"role": "system", "content": self.system_prompt}, + {"role": "user", "content": json.dumps(question_json)}, + ], + stream=False, + ) + + # print(response.choices[0].message.content) + return ( + response.choices[0].message.content + if response.choices[0].message.content + else "" + ) + + +if __name__ == "__main__": + question = { + "id": "25594265", + "index": "1", + "type": "单选", + "title": "下列哪个属于AI视觉识别应用?", + "chooies": [ + {"num": "A", "txt": "音乐合成"}, + {"num": "B", "txt": "植物识别"}, + {"num": "C", "txt": "文案创作"}, + {"num": "D", "txt": "机器翻译"}, + ], + } + DOUBAO().chat(question) diff --git a/main.py b/main.py index a6db6b1..af15fd1 100644 --- a/main.py +++ b/main.py @@ -26,6 +26,9 @@ from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry import warnings +from deepseek import DeepSeek +from doubao import DOUBAO + # 修复 ANTIALIAS 错误 - 添加猴子补丁 import PIL.Image @@ -79,7 +82,20 @@ def replace_html_entities(text, replace_mode="keep_char"): class CKWK: - def __init__(self, un: str, pw: str, host: str) -> None: + def __init__(self, un: str, pw: str, host: str, ai_type="doubao") -> None: + """ + un: 用户名 + pw: 密码 + host: 网站域名 + ai_type: ai类别取值为 'doubao' 或者 'deepseek' + """ + if ai_type == "doubao": + self.ai = DOUBAO() + elif ai_type == "deepseek": + self.ai = DeepSeek() + else: + self.ai = DOUBAO() + self.session = requests.Session() # 定义重试策略 retry_strategy = Retry( @@ -93,18 +109,20 @@ class CKWK: adapter = HTTPAdapter(max_retries=retry_strategy) self.session.mount("http://", adapter) self.session.mount("https://", adapter) - # self.session.verify = False - # self.session.proxies.update( - # { - # "http": "http://127.0.0.1:9000", - # "https": "http://127.0.0.1:9000", - # } - # ) + self.session.verify = False + self.session.proxies.update( + { + "http": "http://127.0.0.1:9000", + "https": "http://127.0.0.1:9000", + } + ) self.username = un self.host = host self.password = pw self.user = dict() self.courses = [] + self.works = [] + self.qas = [] self.ocr = ddddocr.DdddOcr() self.session.headers.update( { @@ -215,6 +233,82 @@ class CKWK: finally: return video, state + def parse_q_a(self, html: str) -> None: + tree = etree.HTML(html) + qas = tree.xpath('//div[@class="topic-item"]/form') + for qa in qas: + items = { + "id": qa.xpath( + './div[@class="courseexamcon-submit"]/input[@name="answerId"]/@value' + )[0], + "index": qa.xpath( + './div[@class="courseexamcon-main"]/div/div[@class="num"]/span/text()' + )[0], + "type": qa.xpath( + './div[@class="courseexamcon-main"]/div/div[@class="type"]/text()' + )[0], + "title": qa.xpath( + './div[@class="courseexamcon-main"]/div[@class="name"]/text()' + )[0].strip(), + "chooies": [ + { + "num": a.xpath('.//span[@class="num"]/text()')[0], + "txt": a.xpath('.//span[@class="txt"]/text()')[0], + } + for a in qa.xpath( + './div[@class="courseexamcon-main"]/div[@class="list"]//li' + ) + ], + } + self.qas.append(items) + + def get_question_answer(self, courseID: str, nodeID: str, workID: str): + headers = self.session.headers + headers["x-requested-with"] = "XMLHttpRequest" + resp = self.session.get( + "https://{}/user/work/start".format(self.host), + params={"courseId": courseID, "nodeId": nodeID, "workId": workID}, + headers=headers, + ) + if resp.status_code == 200 and resp.json().get("status", False): + qa_path = resp.json().get("url", "") + self.log(f'开始作业: {resp.json().get("msg", "")} url -> {qa_path}') + resp2 = self.session.get("https://{}{}".format(self.host, qa_path)) + if resp.status_code == 200: + self.parse_q_a(resp2.text) + else: + self.log(f'开始作业: {resp.json().get("msg", "")}') + return False + return True + + def work_submit(self, answerID: str, workID: str, answer: str, final: bool): + headers = self.session.headers + headers["x-requested-with"] = "XMLHttpRequest" + data = { + "answerId": answerID, + "workId": workID, + "finish": "0" if final else "1", + } + if "," in answer: + answers = answer.split(",") + data["answer[]"] = answers + # for a in answers: + # data.setdefault("answer[]", []).append(a) + else: + if answer == "": + data["answer"] = "C" + data["answer"] = answer + + resp = self.session.post( + f"https://{self.host}/user/work/submit", + data=data, + headers=headers, + ) + if resp.status_code == 200 and resp.json().get("status", False): + self.log( + f'提交答案: {resp.json().get("msg", "")} {answerID} => {answer} {final}' + ) + def get_course(self, _id: str) -> tuple[int, int]: resp = self.session.get("https://{}/user/node?nodeId={}".format(self.host, _id)) if resp.status_code == 200 and "错误提示" not in resp.text: @@ -306,7 +400,23 @@ class CKWK: if resp.status_code == 200 and resp.json().get("status", False): self.log(f'{resp.json().get("msg", "")} page -> {page}') self.courses.extend(resp.json().get("list", [])) - if page <= resp.json().get("pageInfo", {}).get("pageCount", 0): + if page < resp.json().get("pageInfo", {}).get("pageCount", 0): + self.get_study_record(course_id, page + 1) + + def get_work_record(self, course_id: str, user_id: str, page=1): + resp = self.session.get( + f"https://{self.host}/user/study_record/work.json", + params={ + "courseId": course_id, + # "userId": user_id, + "page": page, + "_": str(int(time.time() * 1000)), + }, + ) + if resp.status_code == 200 and resp.json().get("status", False): + self.log(f'{resp.json().get("msg", "")} page -> {page}') + self.works.extend(resp.json().get("list", [])) + if page < resp.json().get("pageInfo", {}).get("pageCount", 0): self.get_study_record(course_id, page + 1) def log(self, *args, **kwargs) -> None: @@ -414,6 +524,7 @@ class CKWK: self.online() def run(self) -> None: + print("当前为刷课模式: ") while self.login(): self.get_user() thread = Thread(target=self.start_loop, daemon=True) @@ -443,7 +554,7 @@ class CKWK: f'{l.get("name", "")} --> {l.get("id", "")} --> 已经学习过了' ) continue - + if isReply: self.reply(c.get("id", ""), l.get("id", "")) @@ -470,6 +581,48 @@ class CKWK: self.log("所有课程已经结束") break + def work(self): + print("当前为刷作业模式: ") + while self.login(): + self.get_user() + thread = Thread(target=self.start_loop, daemon=True) + thread.start() + cs = self.user.get("courses", []) + print( + f'姓名: {self.user.get("name", "")}\n\t互评: {self.user.get("huping", 0)}\n\t乐学园: {self.user.get("lexueyuan", 0)}\n\t讨论主题: {self.user.get("taolunzhuti", 0)}\n\t学习时长: {self.user.get("study_time", "")}' + ) + if len(cs) > 1: + for i, _c in enumerate(cs): + print(f'{i+1}: {_c.get("name", "")} {_c.get("progress", "")}') + cs_order = input("请输入刷课顺序(用英文逗号分隔,不需要耍的不用加): ") + cs_list = [cs[int(_c) - 1] for _c in cs_order.split(",")] + cs_list = cs + for c in cs_list: + self.get_work_record(c.get("id", ""), "") + # print(self.works) + for work in self.works: + self.qas = [] + if "已阅" in work.get("state"): + self.log( + f'{work.get("title", "")} 已阅 {work.get("finalScore", "")}' + ) + continue + if not self.get_question_answer( + c.get("id", ""), work.get("nodeId", ""), work.get("id", "") + ): + continue + for q in self.qas: + # print(q) + chooise = self.ai.chat(q) + self.work_submit( + q.get("id", ""), + work.get("id", ""), + chooise, + int(q.get("index", 1)) < len(self.qas), + ) + # break + break + if __name__ == "__main__": host_server = [ @@ -486,12 +639,17 @@ if __name__ == "__main__": u = input("请输入账号: ") if p == "": p = input("请输入密码: ") - r = input("是否需要评论(不需要[0],需要[1]): ") - if int(r) == 1: - isReply = True - else: - isReply = False - print(bool(isReply)) - if u is None and p is None and c in [0, 1, 2]: - os._exit(0) - CKWK(f"{u}", f"{p}", host_server[c][0]).run() + + # 刷课 + # r = input("是否需要评论(不需要[0],需要[1]): ") + # if int(r) == 1: + # isReply = True + # else: + # isReply = False + # print(bool(isReply)) + # if u is None and p is None and c in [0, 1, 2]: + # os._exit(0) + # CKWK(f"{u}", f"{p}", host_server[c][0]).run() + + # 刷作业 + CKWK(f"{u}", f"{p}", host_server[c][0]).work() diff --git a/pyproject.toml b/pyproject.toml index cafa223..c1053d9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,5 +8,6 @@ dependencies = [ "ddddocr>=1.5.6", "dotenv>=0.9.9", "lxml>=6.0.2", + "openai>=2.8.1", "requests>=2.32.5", ]