├── image └── 01.png ├── README.md ├── LICENSE └── main.py /image/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisKimZHT/Xiaoya-Script/master/image/01.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Xiaoya-Script 2 | 3 | 小雅学习平台脚本,能够全自动完成文档 (type=6) 以及视频 (type=9) 类型的任务。 4 | 5 | > [!CAUTION] 6 | > 7 | > 小雅 API 已经升级,引入了 nonce-signature-timestamp 校验技术,这导致该脚本完全作废。 8 | > 9 | > 若要破解该校验,需要更细致地逆向前端代码,找到签名的生成逻辑,需要耗费更多精力。同时,由于 timestamp 机制,这代表即使完成了破解,也无法完成秒刷题目,必须传递正确的时间戳。 10 | 11 | #### 脚本依赖 12 | 13 | - Python 3.x 14 | - Requests: `pip install requests` 15 | 16 | #### 使用方式 17 | 18 | 1. **获取 Token** 19 | 20 | PC 登录小雅平台,然后按 F12 打开浏览器调试工具,进入 Application (应用) 选项卡,然后在左侧菜单展开 Cookie,选择小雅的 Cookie,然后在列表中找到 `WT-prd-access-token`,复制它的值即可。 21 | 22 | ![](./image/01.png) 23 | 24 | 2. **启动程序** 25 | 26 | `python .\main.py` 27 | 28 | 3. **输入 token** 29 | 30 | 程序会要求输入 Token,粘贴刚才复制的 `WT-prd-access-token` 的**值**后回车即可。 31 | 32 | 4. **输入 group_id** 33 | 34 | 在小雅中打开需要刷的课程的页面,然后复制链接的 `mycourse` 后的数字。 35 | 36 | 例如链接:`https://whut.ai-augmented.com/app/jx-web/mycourse/6386072187333281406/resource/6386072187937254761` 37 | 38 | 我们需要的数字是:`6386072187333281406` 39 | 40 | 然后将其粘贴进入程序回车即可,程序将会自动运行。 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Haotian Zou 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import requests 3 | 4 | endpoint = "https://whut.ai-augmented.com/api/jx-iresource/" 5 | 6 | token = input("请输入 token: ") or os.environ.get("DEV_TOKEN") # 获取token 7 | headers = { 8 | "Authorization": f"Bearer {token}", 9 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" 10 | } 11 | 12 | group_id = input("请输入 group_id: ") # 课程的总ID,在链接的mycourse后面 13 | url = f"{endpoint}resource/queryCourseResources?group_id={group_id}" # 获取课程资源 14 | course_jobs = requests.get(url=url, headers=headers).json()["data"] 15 | 16 | red_font = "\033[31m" 17 | green_font = "\033[32m" 18 | orange_font = "\033[33m" 19 | reset_font = "\033[0m" 20 | 21 | for job in course_jobs: # 遍历课程任务 22 | node_id = job["id"] # 任务ID,在链接的最尾部 23 | job_type = job["type"] # 任务类型 24 | 25 | if "task_id" not in job: 26 | print(f"{orange_font}{node_id}{reset_font}: Not a task, skipped.") 27 | continue 28 | 29 | if job_type == 9: # 9=视频 30 | url = f"{endpoint}resource/task/studenFinishInfo?group_id={group_id}&node_id={node_id}" 31 | assign_id = requests.get(url=url, headers=headers).json()[ 32 | "data"]["assign_id"] 33 | 34 | url = f"{endpoint}resource/queryResource?node_id={node_id}" 35 | result = requests.get(url=url, headers=headers).json()["data"] 36 | quote_id = result["quote_id"] 37 | media_id = result["resource"]["id"] 38 | duration = result["resource"]["duration"] 39 | task_id = result["task_id"] 40 | 41 | data = { 42 | "video_id": "0000000000000000000", # 似乎不重要 43 | "played": duration, 44 | "media_type": 1, 45 | "duration": duration, 46 | "watched_duration": duration 47 | } 48 | url = f"{endpoint}vod/duration/{quote_id}" # 提交视频观看时长 49 | result = requests.post(url=url, headers=headers, json=data) 50 | 51 | url = f"{endpoint}vod/checkTaskStatus" # 完成视频任务 52 | data = { 53 | "group_id": group_id, 54 | "media_id": media_id, 55 | "task_id": task_id, 56 | "assign_id": assign_id 57 | } 58 | 59 | result = requests.post(url=url, headers=headers, json=data).json() 60 | print(f"{green_font}{node_id}{reset_font}: {result}") 61 | 62 | elif job_type == 6: # 6=文档 63 | task_id = job["task_id"] 64 | 65 | url = f"{endpoint}resource/finishActivity" 66 | data = { 67 | "group_id": group_id, 68 | "task_id": task_id, 69 | "node_id": node_id 70 | } 71 | result = requests.post(url=url, headers=headers, json=data).json() 72 | print(f"{green_font}{node_id}{reset_font}: {result}") 73 | 74 | else: 75 | print( 76 | f"{red_font}{node_id}{reset_font}: job_type={job_type} Not Implemented, skipped.") 77 | --------------------------------------------------------------------------------