Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

添加答题功能与支持需解锁章节的课程 #360

Merged
merged 42 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
25556d0
feat:根据SocialSisterYi实现字体解密模块
sz134055 Oct 17, 2024
8d4b500
feat: 尽最大可能获取课程任务列表;对答题信息解析进行支持;
sz134055 Oct 17, 2024
3ebc1aa
feat: 根据SocialSisterYi代码片段实现题库缓存;题库实现;言溪题库实现;
sz134055 Oct 17, 2024
bf093a5
feat:引入题库
sz134055 Oct 17, 2024
de302d5
feat: 添加对答题任务的支持
sz134055 Oct 17, 2024
a6691e9
feat: 配置文件模板加入题库信息
sz134055 Oct 17, 2024
9ea61ab
fix: 修复任务点获取补全问题
sz134055 Oct 17, 2024
9ec7744
gitignore添加题库缓存文件cache.json
sz134055 Oct 17, 2024
338a6f6
添加fonttools库依赖
sz134055 Oct 17, 2024
03f28b6
fix: 添加所有课程任务完成日志
sz134055 Oct 18, 2024
0ecbfd5
fix: 修复因阅读任务或调查问卷导致无任务信息触发异常;添加对问卷和阅读任务的识别占位
sz134055 Oct 18, 2024
64f1b4a
fix: 关闭因设置verify=False导致的InsecureRequestWarning警告
sz134055 Oct 18, 2024
f08b95f
fix:支持在无题库配置文件下运行
sz134055 Oct 18, 2024
bc0aa6c
doc:添加题库配置说明
sz134055 Oct 18, 2024
27ba7e0
feat: 对选择题答案作对应选项修复;言溪题库支持多个token轮询使用
sz134055 Oct 18, 2024
9281f46
fix: 针对因上一任务完成不足导致章节未开放的异常情况做出更新
sz134055 Oct 18, 2024
65ff1cb
feat:功能更新,现在支持答题和需要章节解锁的课程。
sz134055 Oct 18, 2024
26496fa
fix: 修复问题
sz134055 Oct 18, 2024
f9e9e11
fix: 修复主程序中的问题
sz134055 Oct 18, 2024
72d230f
fix: 修复错误
sz134055 Oct 18, 2024
c94f707
fix: 修复api.base中的问题
sz134055 Oct 18, 2024
0974f4e
fix: 纠正对题库模块判断题方法的错误调用
sz134055 Oct 18, 2024
47c4cd6
feat: 增加对当前课程和章节的提示
sz134055 Oct 18, 2024
00f0474
fix: 修复问题;增加对课程和章节的提示
sz134055 Oct 18, 2024
ccd031a
fix: 修复因响应答案包含杂项(如换行空格等)导致无法正确判断选项
sz134055 Oct 18, 2024
028c2d9
fix:修复与优化未开放章节的检测以及任务回滚
sz134055 Oct 18, 2024
0f128ae
fix:去除decode_questions_info方法中题目选项q_options尾部的多余的'\n',避免潜在的分割问题
sz134055 Oct 18, 2024
79a3a66
feat:现在可以在配置文件中设置每次答题是仅保存还是直接提交
sz134055 Oct 18, 2024
acf8040
feat:支持设置答题提交模式;修复若干问题
sz134055 Oct 18, 2024
731bb3d
feat:支持完成阅读任务点(仅完成,不增加时长),增加任务回滚检测,避免无限回滚
sz134055 Oct 18, 2024
2f75ac1
fix:允许置空配置文件中的题库配置以关闭题库功能
sz134055 Oct 18, 2024
d33517a
doc:补充题库功能使用说明
sz134055 Oct 18, 2024
6bc22af
feat:功能、修复、与文档更新
sz134055 Oct 18, 2024
3c1fc5b
fix:任务点获取页数增加至3个,以应对偶尔出现的三任务卡片的课程章节
sz134055 Oct 18, 2024
b13dd00
fix:多选题答题问题修复
sz134055 Oct 18, 2024
c7d7050
fix:获取所有课程接口抽风,对请求增加headers应对
sz134055 Oct 18, 2024
805726e
fix:让回滚计数器正确重置
sz134055 Oct 19, 2024
60ac711
fix:修复因未对多选题答案排序导致的提交失败;对填写的答案进行输出,帮助判断答题情况
sz134055 Oct 19, 2024
5fe2084
fix:累积修复更新
sz134055 Oct 19, 2024
1d6f9c3
fix:针对一种新的满屏是阅读资料但是无type的任务点,暂时采取跳过策略
sz134055 Oct 19, 2024
e59eb51
fix: 解决无限回滚问题
sz134055 Oct 19, 2024
e463a05
fix:修复错误;修复任务无限回滚问题
sz134055 Oct 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,5 @@ chaoxing.log
./config.ini
./chaoxing.log
./cookies.txt
.idea/
.idea/
cache.json
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@
3. (可选配置文件运行) 下载config_template.ini文件保存为config.ini文件,修改文件内的账号密码内容, 执行 `./chaoxing.exe -c config.ini`
4. (可选命令行运行)`./chaoxing.exe -u "手机号" -p "密码" -l 课程ID1,课程ID2,课程ID3...(可选)`

### 题库配置说明

在你的配置文件中找到`[tiku]`,按照注释填写想要使用的题库名(即`provider`,大小写要一致),并填写必要信息,如token,然后在启动时添加`-c [你的配置文件路径]`即可。

题库会默认使用根目录下的`config.ini`文件中的配置,所以你可以复制配置模板(参照前面的说明)命名为`config.ini`,并只配置题库项`[tiku]`,这样即使你不填写账号之类的信息,不使用`-c`参数指定配置文件,题库也会根据这个配置文件自动配置并启用。

对于那些有章节检测且任务点需要解锁的课程,必须配置题库。

> 题库名即`answer.py`模块中根据`Tiku`类实现的具体题库类,例如`TikuYanxi`(言溪题库),在填写时,请务必保持大小写一致。


## :heart: CONTRIBUTORS
![Alt](https://repobeats.axiom.co/api/embed/d3931e84b4b2f17cbe60cafedb38114bdf9931cb.svg "Repobeats analytics image")

Expand Down
201 changes: 201 additions & 0 deletions api/answer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
import configparser
import requests
from pathlib import Path
from typing import Optional
import json
from api.logger import logger
import random
from urllib3 import disable_warnings,exceptions
# 关闭警告
disable_warnings(exceptions.InsecureRequestWarning)

class CacheDAO:
"""
@Author: SocialSisterYi
@Reference: https://github.com/SocialSisterYi/xuexiaoyi-to-xuexitong-tampermonkey-proxy
"""
def __init__(self, file: str = "cache.json"):
self.cacheFile = Path(file)
if not self.cacheFile.is_file():
self.cacheFile.open("w").write("{}")
self.fp = self.cacheFile.open("r+", encoding="utf8")

def getCache(self, question: str) -> Optional[str]:
self.fp.seek(0)
data = json.load(self.fp)
if isinstance(data, dict):
return data.get(question)

def addCache(self, question: str, answer: str):
self.fp.seek(0)
data: dict = json.load(self.fp)
data[question] = answer
self.fp.seek(0)
json.dump(data, self.fp, ensure_ascii=False, indent=4)


class Tiku:
CONFIG_PATH = "config.ini" # 默认配置文件路径
DISABLE = False # 停用标志

def __init__(self) -> None:
self._name = None
self._api = None
self._conf = None

@property
def name(self):
return self._name

@name.setter
def name(self, value):
self._name = value

@property
def api(self):
return self._api

@api.setter
def api(self, value):
self._api = value

@property
def token(self):
return self._token

@token.setter
def token(self,value):
self._token = value

def init_tiku(self):
# 仅用于题库初始化,例如配置token
pass

def config_set(self,config:configparser.ConfigParser|None):
self._conf = config

def _get_conf(self):
"""
从默认配置文件查询配置,如果未能查到,停用题库
"""
try:
config = configparser.ConfigParser()
config.read(self.CONFIG_PATH, encoding="utf8")
return config['tiku']
except KeyError or FileNotFoundError:
logger.info("未找到tiku配置,已忽略题库功能")
self.DISABLE = True
return None

def query(self,q_info:dict) -> str|None:
if self.DISABLE:
return None

# 预处理,去除【单选题】这样与标题无关的字段
# 此处需要改进!!!
q_info['title'] = q_info['title'][6:] # 暂时直接用裁切解决

# 先过缓存
cache_dao = CacheDAO()
answer = cache_dao.getCache(q_info['title'])
if answer:
logger.info(f"从缓存中获取答案:{q_info['title']} -> {answer}")
return answer
else:
answer = self._query(q_info)
if answer:
cache_dao.addCache(q_info['title'], answer)
logger.info(f"从{self.name}获取答案:{q_info['title']} -> {answer}")
return answer
logger.error(f"从{self.name}获取答案失败:{q_info['title']}")
return None
def _query(self,q_info:dict):
pass

def get_tiku_from_config(self):
"""
从配置文件加载题库,这个配置可以是用户提供,可以是默认配置文件
"""
if not self._conf:
# 尝试从默认配置文件加载
self.config_set(self._get_conf())
if self.DISABLE:
return self
cls_name = self._conf['provider']
new_cls = globals()[cls_name]()
new_cls.config_set(self._conf)
return new_cls

def jugement_select(self,answer:str) -> bool:
"""
这是一个专用的方法,要求配置维护两个选项列表,一份用于正确选项,一份用于错误选项,以应对题库对判断题答案响应的各种可能的情况
它的作用是将获取到的答案answer与可能的选项列对比并返回对应的布尔值
"""
if self.DISABLE:
return False
true_list = self._conf['true_list'].split(',')
false_list = self._conf['false_list'].split(',')
if answer in true_list:
return True
elif answer in false_list:
return False
else:
# 无法判断,随机选择
logger.error(f'无法判断答案{answer}对应的是正确还是错误,请自行判断并加入配置文件重启脚本,本次将会随机选择选项')
return random.choice([True,False])



# 按照以下模板实现更多题库

class TikuYanxi(Tiku):
# 言溪题库实现
def __init__(self) -> None:
super().__init__()
self.name = '言溪题库'
self.api = 'https://tk.enncy.cn/query'
self._token = None
self._token_index = 0 # token队列计数器
self._times = 100 # 查询次数剩余,初始化为100,查询后校对修正

def _query(self,q_info:dict):
res = requests.get(
self.api,
params={
'question':q_info['title'],
'token':self._token
},
verify=False
)
if res.status_code == 200:
res_json = res.json()
if not res_json['code']:
# 如果是因为TOKEN次数到期,则更换token
if self._times == 0 or '次数不足' in res_json['data']['answer']:
logger.info(f'TOKEN查询次数不足,将会更换并重新搜题')
self._token_index += 1
self.load_token()
# 重新查询
return self._query(q_info)
logger.error(f'{self.name}查询失败:\n剩余查询数{res_json["data"].get("times",f"{self._times}(仅参考)")}:\n消息:{res_json["message"]}')
return None
self._times = res_json["data"].get("times",self._times)
return res_json['data']['answer']
else:
logger.error(f'{self.name}查询失败:\n{res.text}')
return None

def load_token(self):
token_list = self._conf['tokens'].split(',')
if self._token_index == len(token_list):
# TOKEN 用完
logger.error('TOKEN用完,请自行更换再重启脚本')
raise Exception(f'{self.name} TOKEN 已用完,请更换')
self._token = token_list[self._token_index]

def init_tiku(self):
if not self._conf:
self.config_set(self._get_conf())
if not self.DISABLE:
return self.load_token()

Loading