W4J1e/cloude_learning_skill

自动化完成 21tb 时光易学 平台的在线课程学习:登录 → 获取课表 → 自动播放视频 → 自动填写并提交课程评估,全程无需人工干预

★ 0Forks 0GitHub ↗Compare

Project website ↗

README

云端视频学习 — 完整文档

自动化完成 21tb 时光易学 平台的在线课程学习:登录 → 获取课表 → 自动播放视频 → 自动填写并提交课程评估 → AI 自动完成课后测试,全程无需人工干预。


📖 目录


🚀 场景一:我是普通用户(小白上手)

我需要准备什么?

  1. 一台能联网的电脑(Mac / Windows / Linux 都行)
  2. 安装 Node.js(nodejs.org,版本 >= 18)
  3. 你的 21tb 企业ID、账号、密码
  4. (可选) 智谱 AI API Key,用于课后测试自动答题,从 open.bigmodel.cn 获取

第一步:安装

# 1. 克隆(或下载)本 Skill 到本地
git clone [email protected]:Viennacacao/cloude_learning_skill.git
cd cloude_learning_skill

# 2. 安装浏览器依赖
cd scripts
npm install

第二步:配置环境变量(首次使用)

在项目根目录创建 .env 文件,填入你的配置。这些变量将控制脚本的行为。

# 21tb 登录凭证(必填)
TB_ENTERPRISE_ID=你的企业ID
TB_USER=你的用户名
TB_PASS=你的密码

# 智谱 AI API Key(选填,用于课后测试 AI 答题)
# 从 https://open.bigmodel.cn/ 获取
ZHIPU_API_KEY=your_key_here
ZHIPU_MODEL=glm-4-flash
ZHIPU_API_BASE_URL=https://open.bigmodel.cn/api/paas/v4/chat/completions

# 答题行为配置(选填)
POSTTEST_AI_TIMEOUT_MS=60000       # AI 单批请求超时(毫秒)
POSTTEST_REQUIRE_CONFIRM=false      # 是否要求人工确认(false=自动提交,true=低置信度停下)
POSTTEST_AUTO_SUBMIT_THRESHOLD=0.7  # 自动提交置信度阈值(默认 0.7)

📌 环境常量说明

变量名 必填 默认值 说明
TB_ENTERPRISE_ID ✅ - 21tb 平台的企业标识(corpCode)
TB_USER ✅ - 你的登录账号
TB_PASS ✅ - 你的登录密码
ZHIPU_API_KEY ❌ - 智谱 AI 的 API Key。若不填,课后测试将使用规则兜底(选 D)
ZHIPU_MODEL ❌ glm-4-flash 使用的 AI 模型版本,建议用 flash 版,速度快且便宜
ZHIPU_API_BASE_URL ❌ https://open.bigmodel.cn/api/... 智谱 AI API 地址,通常不需要修改
POSTTEST_AI_TIMEOUT_MS ❌ 60000 AI 答题接口超时时间(毫秒)
POSTTEST_REQUIRE_CONFIRM ❌ false 若设为 true,在 AI 置信度较低或识别不准时会暂停,等待你手动点击提交
POSTTEST_AUTO_SUBMIT_THRESHOLD ❌ 0.7 AI 平均置信度高于此值时且满足提交策略时才自动提交

安全提示:.env 文件包含你的账号密码和 API Key,严禁上传到公共代码仓库。本项目已通过 .gitignore 自动忽略该文件。

第三步:开始使用

# 后备方案(Puppeteer):最简单的用法
node scripts/21tb-login-crawler.js -e 你的企业ID -u 用户名 -p 密码 --auto

这行命令做了什么?

① 打开浏览器 → 登录平台
② 获取你的全部课程列表
③ 自动打开第一门未完成的课程
④ 以 16 倍速自动播放所有视频
⑤ 视频播完后 → 自动填写评估问卷 → 提交
⑥ 自动检测并通过 AI 完成课后测试 ✅

其他常用命令:

我想做... 命令
学完某一门指定课程 node 21tb-login-crawler.js -e ... -c "创新方法"
把所有未完成课程全部学完 node 21tb-login-crawler.js -e ... --auto --auto-advance
只登录看看有哪些课 node 21tb-login-crawler.js -e ...
不想自动填评估问卷 加上 --no-auto-eval

🤖 默认推荐:Puppeteer / 系统浏览器自动化(面向 Agent 自然对话)

结论:由于 21tb 播放页在部分内置 WebView 中会出现 ERR_BLOCKED_BY_ORB 白屏问题,建议使用 Puppeteer 驱动系统 Chrome/Chromium 完成自动化(更稳定)。

特点:

  • 同一时间只打开一门课(串行)
  • 注入使用“本地文件内容注入”(不依赖 GitHub Raw/jsDelivr)
  • 遇到课后测试可用“自然对话确认/取消”驱动继续

快速开始(推荐):

cd scripts
npm install
node 21tb-login-crawler.js --agent --json -c "阳明心学——实践的哲学"

Linux 提示:如果启动浏览器时报 libatk-1.0.so.0 / libgtk-3-0 等缺失,可在 scripts/ 目录执行: npx puppeteer browsers install chrome --install-deps

自动进入第一门未完成课程(不自动推进下一门):

node 21tb-login-crawler.js --agent --json --auto

完成后自动推进下一门:

node 21tb-login-crawler.js --agent --json --auto --auto-advance

Agent 模式默认复用 runtime-logs/chrome-profile 作为 Chrome profile(减少重复登录、保留缓存)。也可用 --user-data-dir 指定。

✅ 统一入口(推荐给 Agent / 自然对话)

为了让“其他人从 GitHub 下载后也能直接用”,提供一个短入口脚本:scripts/agent-entry.js。

# 学习指定课程(完成后停下)
node scripts/agent-entry.js 学习 "阳明心学——实践的哲学" --headful

# 继续:学习第一门未完成课程(完成后停下)
node scripts/agent-entry.js 继续 --headless

# 全部:自动推进学完全部未完成课程
node scripts/agent-entry.js 全部 --headless

当出现课后测试需要确认时(脚本会输出 posttest_confirm_required 并等待):

# 确认提交最近一次课后测试
node scripts/agent-entry.js 确认

# 取消提交最近一次课后测试
node scripts/agent-entry.js 取消

查看最近一次运行状态摘要:

node scripts/agent-entry.js 状态

✅ 严格模式(解决 0/0 误判完成)

默认启用“严格完成判定”:

  • 只要课程页解析到的资源数仍为 0/0,绝不会判定完成,会持续等待/重试直到拿到真实资源列表。
  • crawler 侧也增加了第二道门槛:totalResources > 0 才承认完成,避免任何异常误判。

✅ 刷新后自动恢复注入

现在注入采用 evaluateOnNewDocument 常驻策略:你在课程页手动刷新/网络抖动导致重新加载时,helper/eval 会自动再次注入并继续工作(无需重新跑命令)。

⚙️ 参数速查(核心)

所有命令均在仓库根目录执行;脚本位于 scripts/ 下。

  • --agent:面向 Agent 的对话式模式(默认开启结构化输出 + 默认复用 profile)
  • --json:每行输出一个 JSON 事件(方便 Agent 解析与对话驱动)
  • --headless / --headful:后台跑 / 显示窗口(可自定义)
  • --user-data-dir <dir>:自定义 Chrome profile 目录(跨电脑可用;建议放在可写目录)
  • --chrome-path <path>:指定系统 Chrome/Chromium 可执行文件路径
  • -c/--course "<课程名>":指定课程(可传多次)
  • --auto:自动进入第一门未完成课程
  • --auto-advance:学完后自动推进下一门未完成课程
  • --no-auto-eval:关闭自动评估

🗣️ 自然对话式确认(课后测试)

当脚本检测到课后测试需要确认提交时,会输出事件 posttest_confirm_required,并在 runtime-logs/ 写入:

  • *.posttest_confirm_required.json(包含摘要与 decisionPath)
  • 等待你写入 *.posttest_decision.json

decision 文件内容(两选一):

{ "action": "confirm" }

或

{ "action": "cancel" }

在 SOLO/Agent 场景中,Agent 会用自然语言问你“确认/取消”,然后自动写入 decision 文件继续执行。

1) 准备可被 https 页面加载的脚本地址(避免 Mixed Content)

由于课程页是 https://,浏览器会阻止从 http://127.0.0.1 加载脚本(Mixed Content)。 因此 Browser-only 默认使用 https 资源地址(推荐 GitHub Raw / 你自己的 https 静态站点)。

(已移除 Browser-only runner 路线:由于 21tb 播放页在部分内置 WebView 中出现 ORB 白屏,当前以 Puppeteer / 系统浏览器自动化为主。)

注意:tag/commit 必须已推送到 GitHub 远端;如果还没 push/tag,请先 push/tag,或临时改成 main。

安全提示:zhipuApiKey 建议由 Agent 从本地 .env 读取后再注入,避免写死在提示词/文档里。

3) post-test 低置信度时的对话确认

当 window.__TBH_HELPER__.getState().postTestConfirm.waiting === true:

  • 用户确认提交:window.__TBH_HELPER__.approvePostTestSubmit()
  • 用户取消:window.__TBH_HELPER__.rejectPostTestSubmit()

运行中我需要注意什么?

  • 浏览器不要关 — 脚本运行期间会保持浏览器打开
  • Agent 模式默认单课串行 — 同一时间只学习一门课(更稳定,也更符合对话式工作流)
  • 学完了怎么看? — 脚本会在终端输出进度信息;也可以查看 runtime-logs/ 目录下的日志文件
  • 课后测试怎么做? — 配置好 ZHIPU_API_KEY 后,脚本会自动用 AI 答题;未配置时也会用规则兜底(选 D)尝试完成

🤖 场景二:通过 AI Agent 使用

如果你在用 WorkBuddy 或其他 AI Agent 平台,导入此 Skill 后直接用自然语言对话即可。

怎么导入 Skill?

  1. 将本仓库克隆到本地
  2. 在 Agent 平台中选「从目录导入」,选择 cloude_learning_skill 目录
  3. 导入后即可在任意对话中激活

能说什么话来触发它?

你说的话 Agent 会做的事
「帮我登录 21tb」 自动登录并获取课表
「帮我刷课 / 自动学习」 登录 + 启动第一门未完成课程的自动播放
「看看还有哪些课没学完」 展示未完成课程列表及进度
「帮我把《创新方法》学了」 按课程名直达并自动播放
「把所有课全部学完」 --auto --auto-advance 连续学完全部课程
「帮我做课后测试 / AI 答题」 自动完成课程后的 AI 答题环节
「检查一下学习进度了没」 读取日志中的状态快照

典型对话示例

你:帮我登录 21tb 时光易学,看看还有哪些课没学完

Agent:
  ✅ 正在登录 21tb 时光易学平台...
  ✅ 登录成功!企业:<enterprise_id>  用户:<username>
  ✅ 已获取完整课表(共 54 门课程)

  📋 未完成课程列表(14 门):
     1. 项目成本管理                        🔄 进行中
     2. 项目沟通管理                        未开始
     3. 撬动领导力的四种魅力表达方式          未开始
     ...

你:帮我开始学第一门

Agent:
  ✅ 正在打开课程:《项目成本管理》
  ✅ 内置播放助手已注入(16x 倍速,自动启动=是)
  🎬 课程正在自动学习中...

  (视频全部播完后)
  📋 检测到课程评估页面,自动填写并提交...
  ✅ 评估已提交!
  🧠 检测到课后测试页面,开始 AI 答题...
  📊 已自动填答 8/8 题 | 平均置信度 93.2%
  ✅ 课后测试已提交!课程完成。

💡 核心优势:不需要记命令、不需要开终端、不需要知道参数格式。用自然语言告诉 Agent 想做什么,它会调用对应脚本来完成。


⌨️ 场景三:开发者 / 命令行用户

文件结构一览

cloud-video-learning/
├── SKILL.md                          # Agent 核心配置文件
├── README.md                         # 本文档
├── .env                              # 环境变量配置(API Key,不上传)
├── .gitignore                        # 忽略 .env、runtime-logs、node_modules
├── scripts/
│   ├── 21tb-login-crawler.js         # ★ 主入口:登录+课表+打开课程+自动播放+评估+测试
│   ├── 21tb-player-embed.js          # 播放助手注入器 + 配置下发
│   ├── 21tb-evaluation-auto.js       # 评估自动完成模块(星级+选择题+论述题+提交)
│   ├── 21tb-status-reporter.js       # 结构化状态上报器(JSON 事件流 + 状态快照)
│   └── package.json                  # npm 依赖
├── runtime-logs/                     # 运行时日志(自动生成,不上传)
│   ├── {sessionId}.events.jsonl      #   完整事件流(每行一个 JSON)
│   └── {sessionId}.state.json        #   最新状态快照
├── course-data.json                  # 课表缓存(自动生成,不上传)
└── 21tb-video-helper.user.js         # 播放助手源码(注入课程页,也可用作油猴脚本)

命令行参数速查

21tb-login-crawler.js(主脚本)

node 21tb-login-crawler.js [选项]

必填参数:
  -e, --enterprise <id>    企业ID
  -u, --user <username>   用户名
  -p, --pass <password>   密码

可选参数:
  -c, --course <name>     登录后直接打开指定课程(可重复传入)
  -a, --auto              一键全自动(登录→课表→进入第一个未完成课程)
  --auto-advance          完成当前课后自动推进下一门
  --no-auto-eval          禁用评估自动完成(默认开启)
  --agent                 面向 Agent 的对话式模式(默认单页串行;完成后自动退出;建议配合 --json)
  --headless              无头模式(不显示浏览器窗口)
  --headful               有头模式(显示浏览器窗口;用于排障/观察)
  --user-data-dir <dir>   自定义 Chrome profile 目录(复用登录态/缓存)
  --chrome-path <path>    指定系统 Chrome/Chromium 可执行文件路径
  --json                  结构化 JSON 输出(每行一个事件)
  --progress-logs [dir]   自定义日志目录
  -h, --help              显示帮助

JSON 事件类型说明

使用 --json 时,stdout 每行输出一个 JSON 事件:

事件类型 含义
run_initialized 脚本启动
login_success 登录成功
course_list_saved 课表保存完成
course_opened 课程页已打开
course_progress 播放进度更新
eval_complete 课程评估已完成提交
posttest_complete 课后测试已完成提交
posttest_confirm_required 课后测试需要人工确认提交
posttest_confirm_resolved 已收到人工决策并执行(confirm/cancel)
course_complete 某门课程全部完成
all_courses_complete 全部课程完成
error 错误

完整自动化流程详解

┌─────────────────────────────────────────────────────┐
│  1. 登录阶段                                         │
│     打开登录页 → 切到密码模式 → 填写表单 → 处理弹窗    │
├─────────────────────────────────────────────────────┤
│  2. 课表抓取                                         │
│     导航到 Course Center → 遍历所有分页 → 解析课程卡片 │
├─────────────────────────────────────────────────────┤
│  3. 课程播放                                         │
│     打开播放页 → 注入播放助手 → 16x倍速自动播放        │
│     每30s轮询状态 → 追踪章节/资源进度                  │
├─────────────────────────────────────────────────────┤
│  4. 评估自动完成(视频全播完后)                       │
│     检测到 .course-evaluate                          │
│       → 5星评分 → 选择题全选D → 论述题填固定答案        │
│       → 点击提交 → 处理"进入下一步"弹窗                │
├─────────────────────────────────────────────────────┤
│  5. 课后测试 AI 自动答题(评估完成后)                  │
│     检测到 .course-test-wrap / 关键词识别              │
│       → 提取题目(顶级容器过滤 + 选项数校验)           │
│       → 题库命中检查 → 分批 AI 解答 → 规则兜底          │
│       → 自动/人工确认提交                              │
│       → 答题结果存入 localStorage 题库                 │
├─────────────────────────────────────────────────────┤
│  6. 自动推进(如启用 --auto-advance)                 │
│     回到步骤3,打开下一门未完成课程                     │
└─────────────────────────────────────────────────────┘

🔧 技术细节

平台信息

项目 值
平台地址 https://v4.21tb.com/
登录页 /login/login.init.do(默认二维码)
课程中心 /els/html/courseCenter/courseCenter.loadStudyTask.do
播放页模板 /courseSetting/courseLearning/play?courseType=NEW_COURSE_CENTER&courseId={id}
前端技术栈 Vue 2 + jQuery + Ant Design + 阿里云播放器

登录表单赋值方式

平台前端混合使用 jQuery 和原生事件监听,必须双管齐下:

// 切换到密码登录(不要点击"Password login here"文案)
if (typeof noErwei === 'function') noErwei();
if (typeof changeWay === 'function')
  changeWay(1, document.getElementById('login-password'));

// jQuery 赋值(平台仍会读这些值)
$('#corpCode').val(enterprise);
$('#loginName').val(username);
$('#swInput').val(password);

// 原生 setter + 事件派发(兼容 Vue / 原生监听)
const nativeSetter = Object.getOwnPropertyDescriptor(
  window.HTMLInputElement.prototype, 'value'
).set;
nativeSetter.call(input, value);
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
input.dispatchEvent(new Event('blur', { bubbles: true }));

课程卡片 DOM 结构

<li class="nc-course-card nc-mycourse-card">
  <a class="goStudy" data-id="{courseId}">
    <div class="card-img-box"><img src="..." /></div>
  </a>
  <h3>课程名称</h3>
  <!-- Graduation: Post-test / Course Learning -->
  <!-- Progress: Course Learning 67% / Currently completed / Course Assessment -->
  <!-- Type: Optional / Compulsory / Elective -->
</li>

评估页面 DOM 结构

元素 CSS 选择器 操作
评估容器 .course-evaluate 检测是否进入评估页
星级评分组 .ant-rate[role=radiogroup] 点击第5颗 = 5星
单选题容器 .course-test-type-list-item(不含 textarea 的) 选 D 项
单选选项 .ant-radio-wrapper[label="d"] click
问答题输入框 .course-test-type-list-item textarea.ant-input 填「很不错,高效」
提交按钮 .course-evaluate-footer .ant-btn-primary click
提交后弹窗 button 文字含"进入下一步" click 关闭

课后测试页面识别规则

检测方式 说明
关键词检测 标题或正文含"课后测试"、"post-test"、"post test"、"考试"、"测验"
DOM 检测 同时检测主文档和同域 iframe 中的 .course-test-wrap、.course-test-type-list-item
登录拦截检测 如果课程页被重定向到登录页,会明确提示"请重新登录"

课后测试题目提取规则

  1. 顶级容器过滤:只提取不被其他 .course-test-type-list-item 嵌套的顶级元素,避免把选项误识别为题目
  2. 选项数校验:每道题至少需要有 2 个选项才视为有效题目
  3. 题目类型识别:
    • 含 .ant-checkbox-wrapper → 多选题
    • 含 .ant-radio-wrapper 且只有 2 个选项且文字含"正确/错误"→ 判断题
    • 含 .ant-radio-wrapper → 单选题

AI 答题配置(.env)

变量名 默认值 说明
ZHIPU_API_KEY (必填) 智谱 AI API Key,从 open.bigmodel.cn 获取
ZHIPU_MODEL glm-4-flash AI 模型名称
ZHIPU_API_BASE_URL https://open.bigmodel.cn/api/paas/v4/chat/completions API 地址
POSTTEST_AI_TIMEOUT_MS 60000 AI 单批请求超时(毫秒)
POSTTEST_REQUIRE_CONFIRM false 是否要求人工确认提交

播放进度状态接口

// 在课程播放页浏览器控制台中读取
const state = window.__TBH_HELPER__.getState();

// state.progress 包含:
{
  totalResources: 15,        // 总资源数
  finishedResources: 8,       // 已完成数
  currentResourceName: "第3章 第2节",
  currentChapterIdx: 2,
  currentSectionIdx: 1,
  courseCompleted: false,     // 本门是否完成(含测试/评估)
}

// 课后测试进度(如在答题中)
const postTest = window.__TBH_HELPER__.getPostTestProgress();
// postTest 包含:{ active, stage, summary, total, resolved, fromBank, fromAI, fromRule, avgConfidence }

❓ 常见问题

Q: 登录失败怎么办? A: 检查 .env 中的企业ID、用户名、密码是否正确。平台可能弹出验证码或二次确认弹窗,脚本会尝试自动处理。

Q: 课表为空? A: 等待 SPA 渲染完成。课程列表异步加载,脚本会等待 .nc-mycourse-card 出现。

Q: 打开后没有自动播放? A: 终端是否有「内置播放助手已注入」日志?如果没有,可能是页面结构变化了。后备方案:把 21tb-video-helper.user.js 装到 Tampermonkey。

Q: 评估问卷不想自动填? A: 启动时加 --no-auto-eval 参数。

Q: 课后测试 AI 答题失败了? A: 检查 .env 中是否配置了 ZHIPU_API_KEY,以及 API Key 是否有余额。未配置 API Key 时,脚本会使用规则兜底(多选题选前两个,单选题选最后一个选项)。

Q: 课后测试只答了部分题? A: 检查页面是否有 iframe 嵌套,脚本已支持同域 iframe 检测。另外确认题目提取数量是否正确(可在浏览器控制台查看 [TBH-PostTest] found X total list items, filtered to Y top-level questions)。

Q: 可以同时开多门课吗? A: 本 Skill 面向 Agent 使用时,默认采用单页面串行(同一时间只学习一门课程),以降低不稳定因素并便于对话式编排。若你是纯命令行用户并自行承担稳定性风险,可自行扩展多标签方案。

Q: 怎么看学完没有? A: 看 runtime-logs/ 下最新的 {*.state.json} 中 currentCourseProgress.courseCompleted,或事件流里的 course_complete / posttest_complete。

Q: 自动推进卡住了? A: 脚本每 30s 轮询一次状态。如果异常,手动刷新课程页,播放助手会自动恢复。

Issues