diff --git a/README.md b/README.md
new file mode 100644
index 0000000..868ceda
--- /dev/null
+++ b/README.md
@@ -0,0 +1,52 @@
+## iSmart 课程自动姬 v1.0.0
+
+>
「不止于自动化,追求极致效率」
+>
+> * 如果你觉得这个脚本好用,请点一个 Star⭐,你的 Star 就是作者更新最大的动力
+
+### 效果展示
+
+* 拥有更好的题型适应性,理论上适配所有客观题种类
+
+* 提升稳定性,中途宕机概率大大降低
+
+* 采用全新思路,相较 [自动化方案](https://github.com/Mufanc/iSmartAuto) ,效率提升超过 1000%
+
+![](images/demo.png)
+
+
+### 工作原理
+
+ 使用抓包工具分析客户端流量,可以得知 iSmart 客户端采用的判题方式为本地评判。也就是说会首先将题目和答案一同下载下来,完成答题后使用用户的计算机完成判分,最后将分数回传。这样一来就为爬取答案提供了可能,脚本会根据提供的用户名和密码完成登录,然后将习题的答案下载下来,为进一步地自动答题做好准备。
+
+ 一次偶然的机会,我发现 iSmart 客户端其实就是一个套壳的 Chromium,在其启动参数中加上 `--remote-debugging-port=9222` 参数后,其中页面便能够在 [chrome://inspect](chrome://inspect) 中被调试:
+
+![](images/inspect.png)
+
+ 进而,可以通过 Python 调用 [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) (cdp),完成答案的自动提交
+
+#### Q&A
+
+* **Q:** 既然是回传分数,那为何不用 Python 直接将分数上报,反而要走 cdp?
+
+> 上报分数的请求中有疑似 Hash 的字段 `ut`,且生成 `ut` 的方法 native,无法通过分析 JavaScript 得到(有木有大佬会 ollydbg 的来交个 PR)
+
+
+
+* **Q:** 使用这个脚本,会不会被检测到作弊?
+
+> 不排除这样的可能性,相较自动化而言,目前的方式提交的数据尚不完整(但成绩和学习时长会被记录),若是仔细比对,有可能会发现数据异常
+
+### 使用方法
+
+ 修改 `configs.yml` 中的账号和密码,保证与 iSmart 客户端中登录的账号一致,然后修改 iSmart 的启动快捷方式,增加参数 `--remote-debugging-port=9222`:
+
+![](images/edit-lnk.png)
+
+ 此时运行 main.py,启动 iSmart 客户端,进入某本书籍的教材学习页(如下图),脚本会自动提交成绩。
+
+![](images/booklearn.png)
+
+### 开发者的话
+
+ 该项目尚处于起步阶段,项目结构还没有完全确定下来,所以后续可能会经历多次重构。目前很多功能虽然存在于源码中,但还不完善或者未经测试,可能造成意料之外的结果,所以在使用前还请三思
diff --git a/automaton/devtools.py b/automaton/devtools.py
index 22c9f7a..4fd4afa 100644
--- a/automaton/devtools.py
+++ b/automaton/devtools.py
@@ -40,9 +40,22 @@ class Browser(object):
for page in pages:
if re.match(r'.*me.ismartlearning.cn/center/student/course/bookLearn\.html.*', page['url']):
return Page(page['url'], page['webSocketDebuggerUrl'])
- await asyncio.sleep(2) # 这样写跟套 finally 有区别
except httpx.ConnectError:
- await asyncio.sleep(2)
+ pass
+ await asyncio.sleep(2)
+
+ async def any_http_page(self): # 等待任意 http 页面
+ async with httpx.AsyncClient() as client:
+ while True:
+ logger.info('等待可用页面...')
+ try:
+ pages = (await client.get(f'http://127.0.0.1:{self.port}/json')).json()
+ for page in pages:
+ if re.match(r'https?://.*', page['url']):
+ return Page(page['url'], page['webSocketDebuggerUrl'])
+ except httpx.ConnectError:
+ pass
+ await asyncio.sleep(2)
class Page(object):
diff --git a/automaton/ismart.py b/automaton/ismart.py
index 2b3d9f9..ae2470d 100644
--- a/automaton/ismart.py
+++ b/automaton/ismart.py
@@ -15,7 +15,7 @@ from .spider import Spider
random_args = { # 不同题型对应的随机时长和分数范围
'1': { # 单选题
'time': (20, 60), # 完成时长 / 秒
- 'score': 1 # 得分 (归一化, 向上至满分)
+ 'score': 1 # 得分 (归一化, 向上随机取至满分)
},
'2': { # 多选题
'time': (40, 120),
@@ -48,7 +48,7 @@ random_args = { # 不同题型对应的随机时长和分数范围
'11': { # 选词填空
'time': (30, 90),
'score': 0.9
- },
+ }
}
diff --git a/configs.yml b/configs.yml
index dc95ad5..cebecbe 100644
--- a/configs.yml
+++ b/configs.yml
@@ -1,13 +1,18 @@
# Todo: 每次 commit 之前务必清除账号密码
-# iSmart 客户端相关配置
+# 刷课配置
+project:
+ skip-finished: true # 跳过已完成任务(暂不支持)
+
+
+# iSmart 客户端配置
browser:
executable: Z:\iSmart\client\iSmart.exe # 客户端可执行文件的路径
args: # 启动 iSmart 客户端时额外提供的参数
- --disable-web-security
port: 9222 # devTools 调试端口
-# 用户相关配置(务必保持账号密码与 iSmart 中已登录的相同)
+# 用户配置(务必保持账号密码与 iSmart 中已登录的相同)
user:
username: <用户名> # 手机号
password: <密码> # 密码
diff --git a/images/booklearn.png b/images/booklearn.png
new file mode 100644
index 0000000..e962587
Binary files /dev/null and b/images/booklearn.png differ
diff --git a/images/demo.png b/images/demo.png
new file mode 100644
index 0000000..716ca10
Binary files /dev/null and b/images/demo.png differ
diff --git a/images/edit-lnk.png b/images/edit-lnk.png
new file mode 100644
index 0000000..c962aa4
Binary files /dev/null and b/images/edit-lnk.png differ
diff --git a/images/inspect.png b/images/inspect.png
new file mode 100644
index 0000000..2dc066e
Binary files /dev/null and b/images/inspect.png differ