{"id":"be6f73e1-4405-4308-a8f7-0435e1be8f6b","entityType":"agent","slug":"clawhub-tomuiv-tsinghua-learn","name":"清华网络学堂","canonicalUrl":"https://www.xpersona.co/agent/clawhub-tomuiv-tsinghua-learn","canonicalPath":"/agent/clawhub-tomuiv-tsinghua-learn","generatedAt":"2026-10-12T05:16:25.516Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-11T19:16:37.343Z","emptyReason":null},"description":"清华网络学堂（learn.tsinghua.edu.cn）自动化。要求用户提供网络学堂登录密码（本地 DPAPI 加密存储），自动登录、查看待办、下载课件、提交作业、批量标记已读。当需要操作网络学堂完成日常学习任务时使用。 Skill: 清华网络学堂 Owner: tomuiv Summary: 清华网络学堂（learn.tsinghua.edu.cn）自动化。要求用户提供网络学堂登录密码（本地 DPAPI 加密存储），自动登录、查看待办、下载课件、提交作业、批量标记已读。当需要操作网络学堂完成日常学习任务时使用。 Tags: latest:1.0.17 Version history: v1.0.17 | 2026-05-09T12:36:25.094Z | user auto_mark_read=true now auto-executes mark-read (not just suggest) v1.0.16 | 2026-05-03T03:26:17.036Z | user auto_mark_read=true skips --confirm (no prompt), plain todo check never auto-marks","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1K downloads reported by the source. Last updated 10/11/2026.","installCommand":"clawhub skill install s171935xz6xsqmpn3z12w1jnk184m1a9:tsinghua-learn","sourceUrl":"https://clawhub.ai/tomuiv/tsinghua-learn","homepage":"https://clawhub.ai/tomuiv/skills/tsinghua-learn","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/tomuiv/tsinghua-learn","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/tomuiv/skills/tsinghua-learn","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":60,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"清华网络学堂（learn.tsinghua.edu.cn）自动化。要求用户提供网络学堂登录密码（本地 DPAPI 加密存储），自动登录、查看待办、下载课件、提交作业、批量标记已读。当需要操作网络学堂完成日常学习任务时使用。 Skill: 清华网络学堂 Owner: tomuiv Summary: 清华网络学堂（lear"},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-11T19:16:37.343Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T19:16:37.343Z","emptyReason":null},"stars":null,"forks":null,"downloads":1005,"likes":null,"task":null,"library":null,"packageName":null,"latestVersion":"1.0.17","tractionLabel":"1K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T19:16:37.268Z","emptyReason":null},"lastUpdatedAt":"2026-10-11T19:16:37.343Z","lastCrawledAt":"2026-10-11T19:16:37.268Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-12T19:16:37.268Z","lastVerifiedAt":null,"highlights":[{"version":"1.0.17","createdAt":"2026-05-09T12:36:25.094Z","changelog":"auto_mark_read=true now auto-executes mark-read (not just suggest)","fileCount":17,"zipByteSize":42523},{"version":"1.0.16","createdAt":"2026-05-03T03:26:17.036Z","changelog":"auto_mark_read=true skips --confirm (no prompt), plain todo check never auto-marks","fileCount":15,"zipByteSize":39067},{"version":"1.0.15","createdAt":"2026-05-03T03:20:31.423Z","changelog":"mark-read requires --confirm (script-enforced, like upload). Auto-mark-read now suggests instead of auto-executing.","fileCount":15,"zipByteSize":39046},{"version":"1.0.14","createdAt":"2026-05-03T01:53:37.191Z","changelog":"login_supervised: try headless first, only pop browser on 2FA. Verify chromium-1169 matches playwright 1.52.0","fileCount":15,"zipByteSize":38770},{"version":"1.0.13","createdAt":"2026-05-03T01:40:31.701Z","changelog":"Sync --reset default auto_mark_read to true (matching config.json)","fileCount":15,"zipByteSize":39238},{"version":"1.0.12","createdAt":"2026-05-03T01:37:18.312Z","changelog":"Fix 2FA messages (plain, no ASCII art), handle missing fingerprint gracefully, default auto_mark_read to true, remove unused todos_dom.py","fileCount":15,"zipByteSize":39239},{"version":"1.0.10","createdAt":"2026-05-02T14:57:33.491Z","changelog":"Fix frontmatter: use primaryEnv format + os:windows. Add --confirm requirement to upload (script-enforced, not just docs)","fileCount":16,"zipByteSize":42205},{"version":"1.0.9","createdAt":"2026-05-02T14:50:12.048Z","changelog":"Add credentialScope to frontmatter, narrow password description to network school only","fileCount":16,"zipByteSize":41940}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s171935xz6xsqmpn3z12w1jnk184m1a9:tsinghua-learn","setupComplexity":"low","setupSteps":["Setup complexity is classified as HIGH. You must provision dedicated cloud infrastructure or an isolated VM. Do not run this directly on your local workstation.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-tomuiv-tsinghua-learn/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-tomuiv-tsinghua-learn/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-tomuiv-tsinghua-learn/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-tomuiv-tsinghua-learn/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-tomuiv-tsinghua-learn/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-tomuiv-tsinghua-learn/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-12T05:16:25.511Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-tomuiv-tsinghua-learn/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-tomuiv-tsinghua-learn/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-tomuiv-tsinghua-learn/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-tomuiv-tsinghua-learn/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-11T19:16:37.343Z","emptyReason":null},"readme":"Skill: 清华网络学堂\n\nOwner: tomuiv\n\nSummary: 清华网络学堂（learn.tsinghua.edu.cn）自动化。要求用户提供网络学堂登录密码（本地 DPAPI 加密存储），自动登录、查看待办、下载课件、提交作业、批量标记已读。当需要操作网络学堂完成日常学习任务时使用。\n\nTags: latest:1.0.17\n\nVersion history:\n\nv1.0.17 | 2026-05-09T12:36:25.094Z | user\n\nauto_mark_read=true now auto-executes mark-read (not just suggest)\n\nv1.0.16 | 2026-05-03T03:26:17.036Z | user\n\nauto_mark_read=true skips --confirm (no prompt), plain todo check never auto-marks\n\nv1.0.15 | 2026-05-03T03:20:31.423Z | user\n\nmark-read requires --confirm (script-enforced, like upload). Auto-mark-read now suggests instead of auto-executing.\n\nv1.0.14 | 2026-05-03T01:53:37.191Z | user\n\nlogin_supervised: try headless first, only pop browser on 2FA. Verify chromium-1169 matches playwright 1.52.0\n\nv1.0.13 | 2026-05-03T01:40:31.701Z | user\n\nSync --reset default auto_mark_read to true (matching config.json)\n\nv1.0.12 | 2026-05-03T01:37:18.312Z | user\n\nFix 2FA messages (plain, no ASCII art), handle missing fingerprint gracefully, default auto_mark_read to true, remove unused todos_dom.py\n\nv1.0.10 | 2026-05-02T14:57:33.491Z | user\n\nFix frontmatter: use primaryEnv format + os:windows. Add --confirm requirement to upload (script-enforced, not just docs)\n\nv1.0.9 | 2026-05-02T14:50:12.048Z | user\n\nAdd credentialScope to frontmatter, narrow password description to network school only\n\nv1.0.8 | 2026-05-02T14:44:14.055Z | user\n\nAdd YAML frontmatter with primaryCredential declaration to fix 'no primary credential' in registry metadata\n\nv1.0.7 | 2026-05-02T14:37:33.799Z | user\n\nDrop non-Windows base64 fallback (DPAPI-only), remove ignore_https_errors, full password mask, pin exact dep versions, fix stdin method (file redirect, no echo), allow transparency exception in iron rule 0\n\nv1.0.6 | 2026-05-02T14:25:10.175Z | user\n\nUse Windows DPAPI (CryptProtectData) to encrypt credentials at rest, replacing base64 encoding\n\nv1.0.5 | 2026-05-02T14:23:58.395Z | user\n\nReplace base64 with Windows DPAPI encryption for credentials, fix auto_mark_read default to false\n\nv1.0.4 | 2026-05-02T14:18:28.004Z | user\n\nFix auto_mark_read default to false (was true), reword password privacy rule to clarify init-time collection\n\nv1.0.3 | 2026-05-02T14:02:35.202Z | user\n\nSecurity fixes: enable TLS verification, use stdin for credentials, pin deps, add submission confirmation, stop calling base64 encryption, document data storage\n\nv1.0.2 | 2026-05-02T13:49:11.712Z | user\n\nFix SKILL metadata, rename, add iron rules\n\nv1.0.1 | 2026-05-02T13:44:31.388Z | user\n\n铁律0+fast check+submissions_tracked\n\nv1.0.0 | 2026-04-25T17:24:25.780Z | user\n\nInitial release: login, todos, download kj, submit hw, mark read\n\nArchive index:\n\nArchive v1.0.17: 17 files, 42523 bytes\n\nFiles: AGENTS.md (1975b), CHANGELOG.md (3447b), config.json (102b), credentials.json (77b), scripts/_config.py (6203b), scripts/install_deps.py (2207b), scripts/install_playwright.py (1041b), scripts/learn_api.py (28555b), scripts/login_auto.py (5076b), scripts/login_manager.py (11917b), scripts/login_supervised.py (4454b), scripts/ops.py (13323b), scripts/pdf_merge.py (2569b), scripts/todos_api.py (7750b), skill-card.md (2454b), SKILL.md (24877b), _meta.json (134b)\n\nFile v1.0.17:SKILL.md\n\n---\nname: tsinghua-learn\ndescription: 清华网络学堂（learn.tsinghua.edu.cn）自动化。要求用户提供网络学堂登录密码（本地 DPAPI 加密存储），自动登录、查看待办、下载课件、提交作业、批量标记已读。当需要操作网络学堂完成日常学习任务时使用。\nmetadata:\n  openclaw:\n    requires:\n      env:\n        - CAS_PASSWORD\n    primaryEnv: CAS_PASSWORD\n    os:\n      - windows\n---\n\n# tsinghua-learn skill\n\n清华网络学堂（learn.tsinghua.edu.cn）自动化 Skill。\n\n---\n\n## 如果你是人类，请阅读以下内容\n\n### 前置要求\n\n使用前请确保已安装 **Python 3.10+** 或 **Conda**（推荐 Python 3.11 或 3.12）。建议安装 **7-Zip** 并添加到环境变量 PATH 中，方便后续处理压缩文件。\n\n初始化时会自动安装 Python 依赖（requests、playwright、pillow 等）以及 Chromium 浏览器（~150MB），无需手动操作。\n\n如果没有安装上述环境，请先安装后再对 AI 说 **\"开始初始化\"**。\n\n### 如何与 AI 沟通\n\n你只需要像聊天一样告诉 AI 你想做什么，AI 会帮你完成所有操作。\n\n#### 初始化\n\n对 AI 说：**\"开始初始化\"**\n\nAI 会依次：\n1. 询问你的网络学堂登录账号、密码、学号、姓名\n2. 检查 Python 环境并自动安装所需依赖\n3. 安装 Chromium 浏览器\n4. 尝试登录网络学堂\n5. 登录成功后询问你的偏好设置（是否自动标已读等）\n6. 完成后即可正常使用\n\n> **四个字段说明：**\n> - **网络学堂登录账号**（`--learn-account`）— 用于登录 learn.tsinghua.edu.cn 的账号，可能和学号不同\n> - **登录密码**（`--password`）— 网络学堂登录密码，仅发送至 id.tsinghua.edu.cn（CAS）和 learn.tsinghua.edu.cn\n> - **学号**（`--username`）— 你的学生证号，如 2025013187\n> - **姓名**（`--name`）— 你的真实姓名\n\n#### 查看待办\n\n对 AI 说：**\"查看待办\"** 或 **\"看看有什么作业\"**\n\nAI 会列出所有课程的最新动态：\n- 未交作业及截止日期\n- 未读公告和未浏览课件\n- 已提交但老师还没批的作业（老师批改后会自动汇报评语和分数）\n- 工作区缓存大小\n\nAI 还会主动问你是否需要标已读、清理缓存。\n\n> 你也可以设置每日自动查看：将 `todos_api.py` 配置为定时任务即可。\n\n#### 提交作业\n\n**方式一：直接发送 PDF 文件**\n\n对 AI 说：**\"我要交某某课程的作业\"**，然后发送 PDF 文件即可。\n\n**方式二：发送多张图片合并为 PDF**\n\n先说：**\"我要交某某课程的作业，我发几张图片给你\"**\n\n然后**按顺序**发送图片（先发的会在最终 PDF 的第一页，后发的在后面）。最后说：**\"已经上传完毕\"**\n\nAI 会先列出要提交的课程、作业标题、文件名，请你确认。确认后自动：\n1. 按你发送的顺序命名图片（001、002...）\n2. 合并为 PDF（按文件名排序，所以先发的在 PDF 前面）\n3. 重命名为 `学号_姓名.pdf`\n4. 提交到指定作业\n\n#### 下载课件\n\n对 AI 说：**\"帮我下载微积分的课件\"**\n\nAI 会列出课程的全部课件文件。你也可以更精确地指定：\n\n- **\"下载未读课件\"** — 只下载还没看过的课件\n- **\"下载概率论第 8 讲的课件\"** — 下载指定名称的文件\n- **\"把微积分的课件全部下载到 D 盘\"** — 批量下载到指定目录\n\nAI 会在下载前列出文件清单让你确认。\n\n#### 查看通知和作业详情\n\n对 AI 说：\n\n- **\"看看微积分最近有什么通知\"** — 查看指定课程的公告\n- **\"概率论 HW10 的作业内容是什么\"** — 查看作业的题目和附件\n- **\"上次交的物理作业老师批了吗\"** — 查看已批改作业的评语和分数\n- **\"看看英语课有什么讨论\"** — 查看课程讨论区\n\n#### 清理工作区\n\nAI 在查看待办时会主动提醒你工作区有缓存。这些缓存是下载的课件、合并 PDF 时的临时图片、调试日志等文件，不影响功能但会占用磁盘空间。\n\n只需说 **\"好\"** 或 **\"清理吧\"**，AI 会先列出待清理的文件让你确认，确认后自动删除。\n\n#### 其他\n\n如果有这里没列出的需求，直接告诉 AI 你想做什么即可。AI 会根据现有功能尽力帮你完成。\n\n#### 数据与安全\n\n本 Skill 会在本地存储以下数据：\n\n| 数据 | 位置 | 说明 |\n|------|------|------|\n| 登录凭据 | `credentials.json` | Windows DPAPI 加密（绑定当前用户账户，仅 Windows 支持） |\n| 浏览器登录状态 | `profiles/learn_profile/` | Chrome Profile 含 cookies，避免每次触发 2FA |\n| Session 令牌 | `sessions/learn_session.json` | JSESSIONID + CSRF Token |\n\n如需清除所有数据，对 AI 说 **\"重置所有数据\"**，AI 会清空以上目录内容并恢复到初始状态。\n\n> 这些数据仅存储在本地计算机，不会上传到任何云端。网络学堂本身不支持注销 API，如需完全登出请手动在浏览器中操作。\n\n#### 修改配置\n\n你可以让 AI 帮你修改以下设置，只需要说\"帮我改一下配置\"或\"设置自动标已读\"即可：\n\n| 配置项 | 说明 | 默认值 |\n|--------|------|--------|\n| `auto_mark_read` | 查看待办时是否自动标已读 | 开启 |\n| `confirm_before_cleanup` | 清理缓存前是否需要你确认 | 需要 |\n| `confirm_before_download` | 批量下载前是否需要你确认 | 需要 |\n\n例如：\n- **\"以后自动标已读吧\"** → AI 会开启 `auto_mark_read`\n- **\"清理的时候不用问我了\"** → AI 会把 `confirm_before_cleanup` 设为 false\n\n---\n\n> **当前版本为测试版本，建议同学们完成提交作业等关键操作后自行登录网络学堂查看是否有错误。**\n\n了解更多信息见 GitHub：[github.com/TOMUIV/tsinghua-learn](https://github.com/TOMUIV/tsinghua-learn)\n\n如有任何问题，联系 **ykx25@mails.tsinghua.edu.cn**\n\n---\n\n## 如果你是 AI，请阅读以下内容\n\n### 铁律（违反会导致用户愤怒）\n\n**铁律 1：AI 运行所有脚本。AI 不能要求用户运行任何命令。**\n- ❌ 禁止的行为：AI 对用户说\"请在终端里运行这个命令\"。\n- ❌ 禁止的行为：AI 给用户一段代码让用户复制粘贴。\n- ✅ 正确的行为：AI 自己调用 `python scripts/xxx.py`，读取输出 JSON，根据输出决定下一步。\n\n**铁律 0（最高优先级）：AI 面向用户的回复中，绝对禁止出现以下内容（除非用户主动要求知情披露）：**\n- ❌ 任何命令行（`python xxx.py`、`cd xxx`）\n- ❌ 任何文件路径（`C:\\Users\\...`、`skills\\...`）\n- ❌ 任何技术术语（\"终端\"、\"PowerShell\"、\"运行脚本\"）\n- ❌ 学号、密码原文\n- ⚠️ 例外：当用户明确询问安装了什么、存储了什么、路径在哪等安全相关问题时，AI 可以简要说明。初始化确认、提交确认等关键操作不受此限。\n\n**用户能看到什么：**\n- ✅ \"系统尚未初始化，请提供你的账号信息\"\n- ✅ \"正在获取待办...\"\n- ✅ \"你有 5 项未交作业\"\n- ❌ \"请在终端里跑这个命令\"\n\n**铁律 2：默认只跑快速检查。`login_manager.py` 默认为 `--check` 行为（<1 秒，无浏览器）。**\n- 当前版本的 `login_manager.py` 不带参数时等同于 `--check`，不会启动浏览器。\n- ❌ 禁止的行为：直接跑 `python scripts/login_auto.py` 或 `python scripts/login_supervised.py`。\n- ✅ 正确的行为：\n  - 跑 `python scripts/login_manager.py`（默认就是快速检查，<1 秒）\n  - `initialized: false` → 停下来，告诉用户\"系统尚未初始化\"，走初始化流程。\n  - `valid: true` → 直接进行后续操作。\n  - `valid: false` → 用 `python scripts/login_manager.py --force-login` 执行完整登录。\n\n**铁律 3：`login_manager.py` 是唯一的认证入口。AI 不直接调用其他登录脚本。**\n- AI 不直接调用 `login_auto.py`。\n- AI 不直接调用 `login_supervised.py`。\n- AI 不直接调用 `install_playwright.py`。\n- 上述脚本由 `login_manager.py` 在内部自动调用。\n\n**铁律 4：AI 不凭经验推断问题。所有判断基于脚本输出的 JSON。**\n\n**铁律 5：密码仅用于初始化收集，之后绝不在对话中重复展示。**\n- 初始化时 AI 询问密码，收集后立即传入脚本，不在后续对话中提及密码值。\n- AI 展示 `fields` 内容时，密码字段已脱敏（显示为 `xxx***`）。\n- ❌ 禁止的行为：AI 在初始化之外的对话中说密码原文。\n\n**铁律 6（提交确认）：提交作业必须先调用不带 --confirm 的上传预览，获得用户明确同意后才能加 --confirm 执行。**\n- ❌ 禁止的行为：AI 直接加 --confirm 提交，不展示预览。\n- ✅ 正确的行为：先不带 --confirm 调 upload 得到 preview JSON → 展示用户 → 同意后再加 --confirm\n\n---\n\n### 凭据字段速查\n\n| 字段 | CLI 参数 | 说明 |\n|------|---------|------|\n| 网络学堂登录账号 | `--learn-account` | 登录 learn.tsinghua.edu.cn 用的账号 |\n| 登录密码 | `--password` | 网络学堂登录密码，仅发送至 id.tsinghua.edu.cn（CAS）和 learn.tsinghua.edu.cn |\n| 学号 | `--username` | 学生证号 |\n| 姓名 | `--name` | 真实姓名 |\n\n> 学号和网络学堂登录账号可能相同也可能不同，分别问清楚。\n> ⚠️ 表中没有示例值。AI 不要编造学号或任何数字作为示例。\n\n---\n\n### 脚本速查\n\n| 脚本 | AI 什么时候需要调用它 | 速度 |\n|------|---------------------|------|\n| `login_manager.py` | 默认快速检查（<1s），有--init/--reset/--force-login/--verify | ⚡快/🐢慢 |\n| `todos_api.py` | 用户要求查看待办时 | ⚡快（2-5s） |\n| `ops.py` | 下载文件、上传作业、合并PDF、清理工作区 | ⚡快 |\n| `learn_api.py` | 查询课程列表、公告详情、作业内容 | ⚡快 |\n| `pdf_merge.py` | 图片合并PDF（ops.py内部调用） | ⚡快 |\n| `install_deps.py` | AI不直接调用（--verify内部自动调用） | 🐢慢 |\n| `install_playwright.py` | AI不直接调用 | 🐢慢 |\n| `login_auto.py` | AI不直接调用 | 🐢慢（有浏览器） |\n| `login_supervised.py` | AI不直接调用 | 🐢慢（有浏览器） |\n\n> ⚠️ 标🐢的脚本涉及浏览器/docker/下载，AI不应直接调用。它们由 `login_manager.py --force-login` 或 `--verify` 内部自动调用。\n\n---\n\n### 初始化流程\n\n用户说\"开始初始化\"时，AI 按以下步骤执行：\n\n**Step 1：向用户收集信息**\n```\nAI 依次问用户（不要一次全问，分步问）：\n第一部分（必须）：\n1. 网络学堂登录账号是什么？\n2. 登录密码是什么？\n3. 学号是什么？\n4. 姓名是什么？\n\n第二部分（选填，用户回答了必须信息后再问）：\n5. 是否需要自动标已读？（默认：是）\n6. 清理工作区前是否需要确认？（默认：是）\n7. 批量下载前是否需要确认？（默认：是）\n\n⚠️ AI 不编造示例值（不要写\"如 2025013187\"之类的数字）\n⚠️ AI 不显示学号或密码在对话中\n```\n\n**Step 2：AI 调用 `--init` 保存凭据**\n```\n1. AI 将凭据 JSON 写入临时文件（不包含在任何命令字符串中）\n2. 命令：python scripts/login_manager.py --init --cred-stdin < /tmp/cred_temp.json\n3. AI 立即删除临时文件\n输出：{\"status\":\"ok\", \"fields\":{\"learn_account\":\"...\", \"username\":\"...\", \"name\":\"...\"}}\n```\n> 使用临时文件 + stdin 重定向，密码只存在于临时文件中（随即删除），不出现在任何命令行参数或 echo 字符串中。\n\n**Step 3：AI 向用户展示 fields 内容，请用户确认**\n\n**Step 4：用户确认后，AI 调用 `--verify` 验证环境并尝试登录**\n```\n命令：python scripts/login_manager.py --verify\n输出：{\"python\":true, \"deps\":true, \"chromium\":true, \"login\":true, \"needs_2fa\":false}\n\nlogin=true       → Step 5\nneeds_2fa=true   → AI 告诉用户\"自动登录失败，请打开浏览器完成二次验证\"\n                   用户说\"好了\"后，AI 调用：python scripts/login_manager.py\n```\n\n**Step 5：AI 调用 `todos_api.py` 首次查看待办**\n```\n命令：python scripts/todos_api.py\n输出：JSON（含课程列表、待办数量等，submissions 日志自动初始化）\n```\n\n---\n\n### 日常查看待办流程\n\n任何时候用户说\"查看待办\"\"有什么作业\"\"看看通知\"时：\n\n**Step 1：AI 检查凭据和 session**\n\n跑默认命令（快速检查，<1 秒，无浏览器）：\n```\n命令：python scripts/login_manager.py\n输出：{\"initialized\": false/true, \"valid\": false/true, \"age_h\": ...}\n```\n\n根据输出决定下一步：\n\n① `initialized: false` → AI 停下来，告诉用户\"系统尚未初始化\"，走初始化流程\n② `initialized: true, valid: true` → 跳到 Step 2（获取待办）\n③ `initialized: true, valid: false` → AI 跑 `python scripts/login_manager.py --force-login`（会启动浏览器，约30-60秒）\n\n**Step 2：AI 获取待办数据**\n```\n命令：python scripts/todos_api.py\n输出：JSON 包含 courses、suggestions、graded_homeworks、cleanup_suggestion\n```\n\n**Step 3：AI 根据输出中的字段决定问用户什么**\n\n检查 `graded_homeworks` 字段：\n- 如果有内容 → AI 告诉用户\"老师批改了你的作业：[课程名][标题]，评语：[comment]，得分：[score]\"\n\n检查 `submissions_tracked` 字段：\n- 如果 > 0 → AI 告诉用户\"有 N 个已提交的作业等待老师批改，批改后我会通知你\"\n\n检查 `suggestions` 数组：\n- 如果包含 \"运行 --mark-read\" → AI 问用户\"需要把未读公告和课件标为已读吗？\" 同意后加 --confirm 执行\n- 如果包含 \"运行 --cleanup-preview\" → AI 问用户\"需要看看工作区有哪些垃圾文件吗？\"\n\n检查 `suggestions` 和 `graded_homeworks` 都不存在时：\n- AI 告诉用户\"目前没有新的待办事项\"\n\n**Step 4：用户同意后，AI 自己运行对应命令**\n```\n标已读：        python scripts/todos_api.py --mark-read\n清理预览：      python scripts/ops.py --action cleanup --dry-run\n执行清理：      python scripts/ops.py --action cleanup\n```\n\n---\n\n### 提交作业流程\n\n用户说\"我要交作业\"或发来文件/图片时：\n\n**Step 1：AI 确认要交到哪个作业**\n```\n命令：python scripts/learn_api.py --action courses\n输出：课程列表（AI 找到用户说的课程，记录 wlkcid）\n\n命令：python scripts/learn_api.py --action homeworks --course <wlkcid>\n输出：作业列表（AI 找到用户说的作业，记录 xszyid）\n```\n\n**Step 2：AI 处理文件**\n\n情况 A — 用户直接发送了 PDF 文件：\n```\nAI 将文件保存到 uploads/，进入 Step 3\n```\n\n情况 B — 用户发送多张图片（如 QQ 传图）：\n```\nAI 每收到一张图，保存到 uploads/，按顺序命名为 001.jpg、002.jpg...\n用户说\"已经上传完毕\"后：\n命令：python scripts/ops.py --action pdf-merge --input-dir uploads/\n输出：merged.pdf\nAI 删除 uploads/ 下的所有图片文件\n进入 Step 3\n```\n\n**Step 3：AI 展示确认信息，获得同意后提交**\n\n```\n先执行未确认的提交（展示详情，不实际提交）：\n命令：python scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\n输出：{\"status\":\"pending\", \"requiring\":\"confirmation\", \"details\":{...}}\n\nAI 必须向用户展示：\n- 课程名称\n- 作业标题\n- 待提交的文件名\n- 提交账号（姓名+学号脱敏）\n\n例：\"准备提交 微积分(2) 的 第四次书面作业，文件：2025013187_张三.pdf。确认吗？\"\n\n用户明确说\"确认\"/\"好\"/\"提交\"后，AI 添加 --confirm 重新执行：\n命令：python scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path> --confirm\n输出：{\"status\":\"ok\"}\n```\n\n**Step 4：AI 通知用户提交成功，清理临时文件**\n\n---\n\n### 下载课件流程\n\n用户说\"帮我下载课件\"时：\n\n**Step 1：AI 获取课程列表**\n```\n命令：python scripts/learn_api.py --action courses\n输出：课程列表（AI 找到用户说的课程，记录 wlkcid）\n```\n\n**Step 2：AI 下载文件**\n```\n命令：python scripts/ops.py --action download-all --course <wlkcid> --save-dir <目录>\n输出：下载结果\n```\n如果用户指定了具体文件名或模式，在 `--pattern` 参数中指定。\n\n---\n\n### 常见问题处理\n\n**Session 过期**\n```\nAI 不分析 session 文件。不检查时间戳。不自己调 login_auto.py。\n只做一件事：\n命令：python scripts/login_manager.py\n输出：session 有效则返回，无效则自动续期\n```\n\n**需要二次验证（2FA）**\n```\nlogin_manager.py 或 --verify 返回 needs_2fa=true 时：\nStep 1：AI 告诉用户\"自动登录失败，请打开浏览器完成二次验证\"\nStep 2：用户说\"好了\"后，AI 调用：\n命令：python scripts/login_manager.py\n输出：验证 session 是否已建立\n```\n\n**凭据未初始化**\n```\nlogin_manager.py --check 返回 initialized=false 时：\nAI 告诉用户\"系统尚未初始化\"，然后按初始化流程的 Step 1 开始收集信息\n```\n\n### 脚本概览\n\n| 脚本 | 职责 | 什么时候用 |\n|------|------|-----------|\n| `login_manager.py` | 认证网关 | 任何操作前确保 session；初始化；验证环境 |\n| `todos_api.py` | 日常管理 | 用户问待办、标已读、清理 |\n| `ops.py` | 文件操作 | 下载、上传、合并 PDF、清理工作区 |\n| `learn_api.py` | 数据查询 | 查课程列表、公告详情、作业内容 |\n| `pdf_merge.py` | 图片→PDF | 用户发图片要交作业时 |\n| `install_deps.py` | 依赖安装 | --verify 会自动调用，你不直接碰 |\n| `_config.py` | 配置读取 | 你不直接碰 |\n| `login_auto.py` | 无头登录 | 你不直接碰，login_manager 内部调用 |\n| `login_supervised.py` | 2FA 登录 | 你不直接碰，login_manager 内部调用 |\n| `install_playwright.py` | Chromium 安装 | --verify 会自动调用，你不直接碰 |\n\n### 日常运行流程\n\n任何时候用户问\"查看待办\"\"有什么作业\"等：\n\n```\n① 先确保 session:\n   python scripts/login_manager.py\n   ← 如果 session 过期会自动续期\n\n② 获取待办:\n   python scripts/todos_api.py\n   ← 得到 JSON {\"courses\": [...], \"total_unread\": N, \"suggestions\": [...], ...}\n\n③ 解析输出，主动问用户:\n   - suggestions 里有 \"运行 --mark-read\" → \"需要帮你把未读公告和课件标为已读吗？\"\n   - cleanup_suggestion 不为空 → \"工作区有垃圾文件，需要看看吗？\"\n   - graded_homeworks 有内容 → \"老师批了你的作业，评语是...\"\n\n④ 用户确认后，自己跑对应命令:\n    - 标已读: python scripts/todos_api.py --mark-read\n   - 清理预览: python scripts/ops.py --action cleanup --dry-run\n   - 执行清理: python scripts/ops.py --action cleanup\n```\n\n### 详细 CLI 命令\n\n#### login_manager.py\n\n```bash\npython scripts/login_manager.py                                # 确保 session\npython scripts/login_manager.py --check                        # 检查状态\npython scripts/login_manager.py --init --learn-account=... --password=... --username=... --name=...\npython scripts/login_manager.py --reset                        # 清空所有数据\npython scripts/login_manager.py --verify                       # 验证环境（依赖+Chromium+登录）\n```\n\n退出码: 0=有效, 1=错误, 2=需人工 2FA, 3=未初始化\n\n#### learn_api.py\n\n```bash\npython scripts/learn_api.py --action courses                               # 课程列表\npython scripts/learn_api.py --action announcements --course <wlkcid>       # 公告列表\npython scripts/learn_api.py --action announcement-detail --course <wlkcid> --id <ggid>  # 公告详细内容\npython scripts/learn_api.py --action files --course <wlkcid>               # 课件列表\npython scripts/learn_api.py --action homeworks --course <wlkcid>           # 作业列表\npython scripts/learn_api.py --action homework-full --course <wlkcid> --id <zyid> --xszyid <xszyid>  # 作业完整信息（含评语）\npython scripts/learn_api.py --action discussions --course <wlkcid>         # 讨论列表\npython scripts/learn_api.py --action questionnaires --course <wlkcid>      # 问卷列表\npython scripts/learn_api.py --action aggregated --course <wlkcid>          # 聚合查询\npython scripts/learn_api.py --action mark-all-announcements-read --course <wlkcid>\npython scripts/learn_api.py --action mark-all-files-read --course <wlkcid>\n```\n\n#### ops.py\n\n```bash\npython scripts/ops.py --action list-files --course <wlkcid> [--pattern \"*.pdf\"]\npython scripts/ops.py --action download --course <wlkcid> --wjid <wjid>\npython scripts/ops.py --action download --course <wlkcid> --wjid-list \"id1,id2,...\"\npython scripts/ops.py --action download-all --course <wlkcid> [--pattern \"*.pdf\"]\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path> --confirm  # 确认后实际提交\npython scripts/ops.py --action pdf-merge --input-dir <目录> [--output <路径>]\npython scripts/ops.py --action move-in --file <source_path>\npython scripts/ops.py --action cleanup --dry-run\npython scripts/ops.py --action cleanup\n```\n\n#### todos_api.py\n\n```bash\npython scripts/todos_api.py                           # 代办汇总\npython scripts/todos_api.py --mark-read --confirm              # + 标记可读项已读\npython scripts/todos_api.py --cleanup-preview         # + 清理预览\npython scripts/todos_api.py --cleanup                 # + 执行清理\n```\n\n**输出解析**：\n- `suggestions` — AI 读取后应主动询问用户：\"需要帮你把未读公告和课件标为已读吗？\"\"需要帮你看看工作区有哪些垃圾文件吗？\"\n- `graded_homeworks` — 老师新批改的作业及其评语/分数\n- `new_submissions_logged` — 本次新增的已提交未批改作业数量\n\n#### pdf_merge.py\n\n```bash\npython scripts/pdf_merge.py --input-dir <目录> [--output <路径>]\n```\n\n支持: jpg, jpeg, png, webp, bmp, heic, heif\n\n### 常见任务工作流\n\n#### 图片作业提交（QQ 发送图片）\n\n```\n1. 用户每发一张图 → AI 保存到 uploads/，按顺序命名为 001.jpg、002.jpg...\n2. 用户说\"发送完毕\"\n3. ops.py --action pdf-merge --input-dir uploads/ → merged.pdf\n4. 删除 uploads/ 下所有图片（保留 merged.pdf）\n5. ops.py --action upload --course ... --xszyid ... --file uploads/merged.pdf --confirm\n   → 自动重命名为 学号_姓名.pdf\n6. 删除 uploads/merged.pdf\n```\n\n#### 下载课件\n\n```\nlearn_api.py --action courses                    # 获取课程列表+ID\nops.py --action download-all --course <wlkcid>   # 下载全部\n```\n\n### 提交日志机制\n\n文件: `submissions/submissions_log.json`\n\n```\ntodos_api.py 每次运行时:\n  1. sync_submissions_log() → 扫描所有已交未批改作业，新出现的追加到日志（已存在则跳过）\n  2. check_graded_submissions() → 遍历日志，检查是否有作业已被批改\n     有 → 获取评语/分数，从日志中移除，在输出中加入 graded_homeworks\n```\n\n### 配置 (config.json)\n\n```json\n{\n  \"auto_mark_read\": false,\n  \"confirm_before_cleanup\": true,\n  \"confirm_before_download\": true\n}\n```\n\n| 选项 | 说明 | 默认 |\n|------|------|------|\n| `auto_mark_read` | 运行 `todos_api.py` 时自动标已读 | `false` |\n| `confirm_before_cleanup` | 清理前是否需用户确认 | `true` |\n| `confirm_before_download` | 批量下载前是否需用户确认 | `true` |\n\n### 未读判断标准\n\n| 模块 | API 字段 | 过滤值 |\n|------|----------|--------|\n| 作业 | `zt` | `\"未交\"` |\n| 公告 | `sfyd` | `\"否\"` |\n| 课件 | `isNew` | `\"1\"` |\n| 讨论 | `htsl` | `>0` |\n\n### 注意事项与不可做清单\n\n#### 不可做\n\n- **不要硬编码课程 ID、学期、路径、姓名**\n- **不要在脚本中使用 `input()` 或 `getpass()`** — AI 运行脚本，不能阻塞\n- **不要直接修改 `credentials.json`** — 通过 `login_manager.py --init` 操作\n- **不要创建新脚本除非绝对必要**\n- **不要在 `config.json` 之外的地方存储用户信息**\n- **不要告诉用户命令行操作** — 用户只回答\"要/不要\"，AI 代为执行\n\n#### 注意事项\n\n- **文件名编码**：服务器将 UTF-8 的中文文件名以 latin-1 编码发送，`download_file()` 已做修复\n- **作业提交端点**：真实端点是 `/b/wlxt/kczy/zy/student/tjzy`，不是 `uploadFile/saveXszj`\n- **公告标已读**：端点 GET `/f/wlxt/kcgg/wlkc_ggb/student/beforeViewXs`，不是 `saveYd`\n- **公告内容**：直接从列表 API 的 `ggnr` base64 字段解码\n- **学期自动检测**：每次建立 session 后调用 `get_current_semester()`\n- **重置**：`login_manager.py --reset` 保留所有文件结构，只清空内容\n- **已提交的作业无法撤回**：网络学堂不提供撤回已提交作业的 API\n\nFile v1.0.17:_meta.json\n\n{\n  \"ownerId\": \"kn73vfhykr10snz07772njvdmx84mcb6\",\n  \"slug\": \"tsinghua-learn\",\n  \"version\": \"1.0.17\",\n  \"publishedAt\": 1778330185094\n}\n\nFile v1.0.17:AGENTS.md\n\n# tsinghua-learn — AGENTS.md\n\n## 架构\n\n6 个脚本：\n\n| 脚本 | 职责 |\n|------|------|\n| `_config.py` | 路径/配置/凭证、DPAPI 解密 |\n| `login_manager.py` | 中央认证网关（默认 fast check，`--force-login` 完整登录） |\n| `learn_api.py` | HTTP API（课程/公告/课件/作业/讨论/问卷/聚合查询） |\n| `ops.py` | 文件操作（下载/批量下载/上传/移入/清理） |\n| `todos_api.py` | 日常管理（代办汇总/标已读/清理，JSON 给 AI） |\n| `pdf_merge.py` | 图片→PDF |\n\n登录脚本不直接调用：\n| `login_auto.py` | headless 自动登录 |\n| `login_supervised.py` | 2FA 登录 |\n\n## 核心命令\n\n```bash\npython scripts/login_manager.py                        # 快速检查 session (<1s)\npython scripts/login_manager.py --init                 # 初始化凭据\npython scripts/login_manager.py --verify               # 验证环境+登录\npython scripts/login_manager.py --reset                # 清空所有数据\npython scripts/login_manager.py --force-login          # 强制完整登录\n\npython scripts/todos_api.py                            # 代办汇总\npython scripts/todos_api.py --mark-read                # + 标已读\npython scripts/todos_api.py --cleanup                  # + 清理\n\npython scripts/ops.py --action list-files --course <wlkcid>\npython scripts/ops.py --action download --course <wlkcid> --wjid <wjid>\npython scripts/ops.py --action download-all --course <wlkcid>\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\npython scripts/ops.py --action pdf-merge --input-dir <目录>\npython scripts/ops.py --action cleanup --dry-run\npython scripts/ops.py --action cleanup\n```\n\n## 关键约束\n\n- paths/creds 全走 `_config.py` getter\n- `credentials.json` DPAPI 加密\n- 学期从 API 自动检测\n- 课程从 API 动态获取\n- 不创建新脚本\n- 脚本不阻塞（`input()`/`getpass()` 禁用）\n- 提交作业自动重命名：`学号_姓名.ext`\n\nFile v1.0.17:CHANGELOG.md\n\n# tsinghua-learn — Release History\n\n## v1.0.16 (2026-05-03)\n- `auto_mark_read=true` 时 `--mark-read` 跳过 `--confirm`，但纯查待办永不自动标\n- `login_supervised.py`：先 headless，仅 2FA 时弹浏览器\n- `install_playwright.py`：内置 npmmirror.com 镜像，解决国内下载超时\n- 删除 `todos_dom.py`（DOM 备用，已被 `todos_api.py` 替代）\n- 3 个安全扫描全部 Staff Cleared（ClawHub #1984 申诉通过）\n\n## v1.0.15 (2026-05-03)\n- `todos_api.py --mark-read` 加 `--confirm` 脚本级强制确认\n- `auto_mark_read` 不再自动执行，改为 suggestions 提示 AI 加 `--confirm`\n- ClawScan: `auto_mark_read` Concern → Note\n\n## v1.0.14 (2026-05-03)\n- `login_supervised.py` 重写：先 headless，检测到 2FA 再弹浏览器\n- 验证 `chromium-1169` ↔ `playwright==1.52.0` 版本对齐\n\n## v1.0.13 (2026-05-03)\n- `login_manager.py --reset` 默认 `auto_mark_read: true`（匹配 `config.json`）\n\n## v1.0.12 (2026-05-03)\n- 2FA 提示改 plain text（去 ASCII art）\n- fingerPrint 文件缺失时不崩溃\n- `auto_mark_read` 默认值改为 `true`\n- ClawScan 从 5 个 findings 降到 4 个\n\n## v1.0.10 (2026-05-02)\n- 前置换页改用 `primaryEnv` + `os: [windows]`（ClawHub 识别）\n- `ops.py upload` 加 `--confirm` 脚本级强制确认（无 `--confirm` 返回 pending）\n- ClawScan: `submit_homework` Concern → Note；static analysis → Benign\n\n## v1.0.9 (2026-05-02)\n- 前置换页加 `credentialScope`、窄化密码描述（去\"清华信息门户也用同一密码\"）\n\n## v1.0.8 (2026-05-02)\n- SKILL.md 加 YAML 前置换页，声明 `primaryCredential: cas_password`\n\n## v1.0.7 (2026-05-02)\n- `install_deps.py` 依赖改为精确 `==` pin（不再 `>=`）\n- 密码/用户名掩码改为全 `***`（不再泄露前 N 字符）\n- stdin 方式从 `echo | pipe` 改为临时文件重定向\n- 铁律 0 加例外条款（用户知情披露）\n- 去掉 `ignore_https_errors=True` 全部 3 处（`login_auto.py`、`login_supervised.py`、`todos_dom.py`）\n- 去掉非 Windows base64 fallback——仅支持 DPAPI\n- ClawScan 从 7 个 findings 降到 4 个\n\n## v1.0.6 (2026-05-02)\n- 凭据存储从 base64 编码改为 Windows DPAPI（`CryptProtectData`）\n- 修复 DPAPI 加密中文文本的字节长度 bug\n- ClawScan: static analysis → Benign\n\n## v1.0.5 (2026-05-02)\n- 修复 `config.json` `auto_mark_read: true` → `false`\n- 修改铁律 5 措辞（密码仅在初始化时收集）\n\n## v1.0.3 (2026-05-02)\n- 全面启用 TLS 验证：去掉 `learn_api.py`、`login_manager.py` 等 6 个脚本的 `verify=False`\n- 新增 `--cred-stdin`：凭据通过 stdin JSON 传入，避免密码上命令行\n- `install_deps.py` 依赖 pin 最低版本号\n- SKILL.md 新增铁律 6（提交确认）+ \"数据与安全\"章节\n- 清理 44 个 `_test_*` 调试脚本\n- ClawScan 从 21+ 降到 6 个 findings\n\n## v1.0.2 (2026-05-02)\n- ClawHub 显示名从 \"Clawhub\" 改为 \"清华网络学堂\"\n- 请求安全重扫\n\n## v1.0.1 (2026-05-02)\n- ClawHub 首次发布\n\n## v1.0.0 之前\n- 从 30+ 个散乱脚本重构为 10 个核心脚本\n- 新建 `login_manager.py`（中央认证网关）\n- 新建 `ops.py`（文件操作统一入口）\n- 修复 `learn_api.py` 多个 API 端点\n- 学期从 API 自动检测\n- 文件名乱码修复（latin-1 → UTF-8）\n- 提交日志机制（`submissions_log.json`）\n- SKILL.md 分人类 / AI 两部分\n- 创建三版发布（clawhub / github-full / github-cas）\n\nFile v1.0.17:skill-card.md\n\n## Description:\n\n清华网络学堂（learn.tsinghua.edu.cn）自动化。要求用户提供网络学堂登录密码（本地 DPAPI 加密存储），自动登录、查看待办、下载课件、提交作业、批量标记已读。当需要操作网络学堂完成日常学习任务时使用。\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[tomuiv](https://clawhub.ai/user/tomuiv)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nStudents and agents use this skill to automate routine Tsinghua Learn tasks such as login, todo review, courseware download, homework submission, PDF preparation, cleanup, and marking notices or files as read.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill requires a Tsinghua Learn login and stores local credentials, browser profile data, and session files.\n\nMitigation: Install only if the publisher is trusted, protect the local account and skill directory, and clear stored account state when the skill is no longer needed.\n\nRisk: The skill can change account state, including marking notices or files as read during todo checks.\n\nMitigation: Disable automatic mark-read unless that behavior is intended, and review account-changing actions before allowing the agent to execute them.\n\nRisk: Plaintext temporary credential workflows can expose account secrets.\n\nMitigation: Avoid workflows that place credentials in plaintext temporary files and remove any temporary credential material immediately after initialization.\n\n## Reference(s):\n\n- [ClawHub Skill Page](https://clawhub.ai/tomuiv/skills/tsinghua-learn)\n- [Tsinghua Learn](https://learn.tsinghua.edu.cn)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, code, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown responses with JSON-backed status summaries, shell command execution, configuration updates, and generated or downloaded files]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires Windows and CAS_PASSWORD; stores local login, browser profile, and session state for Tsinghua Learn automation.]\n\n## Skill Version(s):\n\n1.0.17 (source: server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nFile v1.0.17:config.json\n\n{\r\n    \"auto_mark_read\": true,\n  \"confirm_before_cleanup\": true,\r\n  \"confirm_before_download\": true\r\n}\n\nFile v1.0.17:credentials.json\n\n{\r\n  \"username\": \"\",\r\n  \"password\": \"\",\r\n  \"student_id\": \"\",\r\n  \"name\": \"\"\r\n}\n\nArchive v1.0.16: 15 files, 39067 bytes\n\nFiles: AGENTS.md (1975b), config.json (102b), credentials.json (77b), scripts/_config.py (6203b), scripts/install_deps.py (2207b), scripts/install_playwright.py (1041b), scripts/learn_api.py (28555b), scripts/login_auto.py (5076b), scripts/login_manager.py (11917b), scripts/login_supervised.py (4454b), scripts/ops.py (13323b), scripts/pdf_merge.py (2569b), scripts/todos_api.py (7728b), SKILL.md (24877b), _meta.json (134b)\n\nFile v1.0.16:SKILL.md\n\n---\nname: tsinghua-learn\ndescription: 清华网络学堂（learn.tsinghua.edu.cn）自动化。要求用户提供网络学堂登录密码（本地 DPAPI 加密存储），自动登录、查看待办、下载课件、提交作业、批量标记已读。当需要操作网络学堂完成日常学习任务时使用。\nmetadata:\n  openclaw:\n    requires:\n      env:\n        - CAS_PASSWORD\n    primaryEnv: CAS_PASSWORD\n    os:\n      - windows\n---\n\n# tsinghua-learn skill\n\n清华网络学堂（learn.tsinghua.edu.cn）自动化 Skill。\n\n---\n\n## 如果你是人类，请阅读以下内容\n\n### 前置要求\n\n使用前请确保已安装 **Python 3.10+** 或 **Conda**（推荐 Python 3.11 或 3.12）。建议安装 **7-Zip** 并添加到环境变量 PATH 中，方便后续处理压缩文件。\n\n初始化时会自动安装 Python 依赖（requests、playwright、pillow 等）以及 Chromium 浏览器（~150MB），无需手动操作。\n\n如果没有安装上述环境，请先安装后再对 AI 说 **\"开始初始化\"**。\n\n### 如何与 AI 沟通\n\n你只需要像聊天一样告诉 AI 你想做什么，AI 会帮你完成所有操作。\n\n#### 初始化\n\n对 AI 说：**\"开始初始化\"**\n\nAI 会依次：\n1. 询问你的网络学堂登录账号、密码、学号、姓名\n2. 检查 Python 环境并自动安装所需依赖\n3. 安装 Chromium 浏览器\n4. 尝试登录网络学堂\n5. 登录成功后询问你的偏好设置（是否自动标已读等）\n6. 完成后即可正常使用\n\n> **四个字段说明：**\n> - **网络学堂登录账号**（`--learn-account`）— 用于登录 learn.tsinghua.edu.cn 的账号，可能和学号不同\n> - **登录密码**（`--password`）— 网络学堂登录密码，仅发送至 id.tsinghua.edu.cn（CAS）和 learn.tsinghua.edu.cn\n> - **学号**（`--username`）— 你的学生证号，如 2025013187\n> - **姓名**（`--name`）— 你的真实姓名\n\n#### 查看待办\n\n对 AI 说：**\"查看待办\"** 或 **\"看看有什么作业\"**\n\nAI 会列出所有课程的最新动态：\n- 未交作业及截止日期\n- 未读公告和未浏览课件\n- 已提交但老师还没批的作业（老师批改后会自动汇报评语和分数）\n- 工作区缓存大小\n\nAI 还会主动问你是否需要标已读、清理缓存。\n\n> 你也可以设置每日自动查看：将 `todos_api.py` 配置为定时任务即可。\n\n#### 提交作业\n\n**方式一：直接发送 PDF 文件**\n\n对 AI 说：**\"我要交某某课程的作业\"**，然后发送 PDF 文件即可。\n\n**方式二：发送多张图片合并为 PDF**\n\n先说：**\"我要交某某课程的作业，我发几张图片给你\"**\n\n然后**按顺序**发送图片（先发的会在最终 PDF 的第一页，后发的在后面）。最后说：**\"已经上传完毕\"**\n\nAI 会先列出要提交的课程、作业标题、文件名，请你确认。确认后自动：\n1. 按你发送的顺序命名图片（001、002...）\n2. 合并为 PDF（按文件名排序，所以先发的在 PDF 前面）\n3. 重命名为 `学号_姓名.pdf`\n4. 提交到指定作业\n\n#### 下载课件\n\n对 AI 说：**\"帮我下载微积分的课件\"**\n\nAI 会列出课程的全部课件文件。你也可以更精确地指定：\n\n- **\"下载未读课件\"** — 只下载还没看过的课件\n- **\"下载概率论第 8 讲的课件\"** — 下载指定名称的文件\n- **\"把微积分的课件全部下载到 D 盘\"** — 批量下载到指定目录\n\nAI 会在下载前列出文件清单让你确认。\n\n#### 查看通知和作业详情\n\n对 AI 说：\n\n- **\"看看微积分最近有什么通知\"** — 查看指定课程的公告\n- **\"概率论 HW10 的作业内容是什么\"** — 查看作业的题目和附件\n- **\"上次交的物理作业老师批了吗\"** — 查看已批改作业的评语和分数\n- **\"看看英语课有什么讨论\"** — 查看课程讨论区\n\n#### 清理工作区\n\nAI 在查看待办时会主动提醒你工作区有缓存。这些缓存是下载的课件、合并 PDF 时的临时图片、调试日志等文件，不影响功能但会占用磁盘空间。\n\n只需说 **\"好\"** 或 **\"清理吧\"**，AI 会先列出待清理的文件让你确认，确认后自动删除。\n\n#### 其他\n\n如果有这里没列出的需求，直接告诉 AI 你想做什么即可。AI 会根据现有功能尽力帮你完成。\n\n#### 数据与安全\n\n本 Skill 会在本地存储以下数据：\n\n| 数据 | 位置 | 说明 |\n|------|------|------|\n| 登录凭据 | `credentials.json` | Windows DPAPI 加密（绑定当前用户账户，仅 Windows 支持） |\n| 浏览器登录状态 | `profiles/learn_profile/` | Chrome Profile 含 cookies，避免每次触发 2FA |\n| Session 令牌 | `sessions/learn_session.json` | JSESSIONID + CSRF Token |\n\n如需清除所有数据，对 AI 说 **\"重置所有数据\"**，AI 会清空以上目录内容并恢复到初始状态。\n\n> 这些数据仅存储在本地计算机，不会上传到任何云端。网络学堂本身不支持注销 API，如需完全登出请手动在浏览器中操作。\n\n#### 修改配置\n\n你可以让 AI 帮你修改以下设置，只需要说\"帮我改一下配置\"或\"设置自动标已读\"即可：\n\n| 配置项 | 说明 | 默认值 |\n|--------|------|--------|\n| `auto_mark_read` | 查看待办时是否自动标已读 | 开启 |\n| `confirm_before_cleanup` | 清理缓存前是否需要你确认 | 需要 |\n| `confirm_before_download` | 批量下载前是否需要你确认 | 需要 |\n\n例如：\n- **\"以后自动标已读吧\"** → AI 会开启 `auto_mark_read`\n- **\"清理的时候不用问我了\"** → AI 会把 `confirm_before_cleanup` 设为 false\n\n---\n\n> **当前版本为测试版本，建议同学们完成提交作业等关键操作后自行登录网络学堂查看是否有错误。**\n\n了解更多信息见 GitHub：[github.com/TOMUIV/tsinghua-learn](https://github.com/TOMUIV/tsinghua-learn)\n\n如有任何问题，联系 **ykx25@mails.tsinghua.edu.cn**\n\n---\n\n## 如果你是 AI，请阅读以下内容\n\n### 铁律（违反会导致用户愤怒）\n\n**铁律 1：AI 运行所有脚本。AI 不能要求用户运行任何命令。**\n- ❌ 禁止的行为：AI 对用户说\"请在终端里运行这个命令\"。\n- ❌ 禁止的行为：AI 给用户一段代码让用户复制粘贴。\n- ✅ 正确的行为：AI 自己调用 `python scripts/xxx.py`，读取输出 JSON，根据输出决定下一步。\n\n**铁律 0（最高优先级）：AI 面向用户的回复中，绝对禁止出现以下内容（除非用户主动要求知情披露）：**\n- ❌ 任何命令行（`python xxx.py`、`cd xxx`）\n- ❌ 任何文件路径（`C:\\Users\\...`、`skills\\...`）\n- ❌ 任何技术术语（\"终端\"、\"PowerShell\"、\"运行脚本\"）\n- ❌ 学号、密码原文\n- ⚠️ 例外：当用户明确询问安装了什么、存储了什么、路径在哪等安全相关问题时，AI 可以简要说明。初始化确认、提交确认等关键操作不受此限。\n\n**用户能看到什么：**\n- ✅ \"系统尚未初始化，请提供你的账号信息\"\n- ✅ \"正在获取待办...\"\n- ✅ \"你有 5 项未交作业\"\n- ❌ \"请在终端里跑这个命令\"\n\n**铁律 2：默认只跑快速检查。`login_manager.py` 默认为 `--check` 行为（<1 秒，无浏览器）。**\n- 当前版本的 `login_manager.py` 不带参数时等同于 `--check`，不会启动浏览器。\n- ❌ 禁止的行为：直接跑 `python scripts/login_auto.py` 或 `python scripts/login_supervised.py`。\n- ✅ 正确的行为：\n  - 跑 `python scripts/login_manager.py`（默认就是快速检查，<1 秒）\n  - `initialized: false` → 停下来，告诉用户\"系统尚未初始化\"，走初始化流程。\n  - `valid: true` → 直接进行后续操作。\n  - `valid: false` → 用 `python scripts/login_manager.py --force-login` 执行完整登录。\n\n**铁律 3：`login_manager.py` 是唯一的认证入口。AI 不直接调用其他登录脚本。**\n- AI 不直接调用 `login_auto.py`。\n- AI 不直接调用 `login_supervised.py`。\n- AI 不直接调用 `install_playwright.py`。\n- 上述脚本由 `login_manager.py` 在内部自动调用。\n\n**铁律 4：AI 不凭经验推断问题。所有判断基于脚本输出的 JSON。**\n\n**铁律 5：密码仅用于初始化收集，之后绝不在对话中重复展示。**\n- 初始化时 AI 询问密码，收集后立即传入脚本，不在后续对话中提及密码值。\n- AI 展示 `fields` 内容时，密码字段已脱敏（显示为 `xxx***`）。\n- ❌ 禁止的行为：AI 在初始化之外的对话中说密码原文。\n\n**铁律 6（提交确认）：提交作业必须先调用不带 --confirm 的上传预览，获得用户明确同意后才能加 --confirm 执行。**\n- ❌ 禁止的行为：AI 直接加 --confirm 提交，不展示预览。\n- ✅ 正确的行为：先不带 --confirm 调 upload 得到 preview JSON → 展示用户 → 同意后再加 --confirm\n\n---\n\n### 凭据字段速查\n\n| 字段 | CLI 参数 | 说明 |\n|------|---------|------|\n| 网络学堂登录账号 | `--learn-account` | 登录 learn.tsinghua.edu.cn 用的账号 |\n| 登录密码 | `--password` | 网络学堂登录密码，仅发送至 id.tsinghua.edu.cn（CAS）和 learn.tsinghua.edu.cn |\n| 学号 | `--username` | 学生证号 |\n| 姓名 | `--name` | 真实姓名 |\n\n> 学号和网络学堂登录账号可能相同也可能不同，分别问清楚。\n> ⚠️ 表中没有示例值。AI 不要编造学号或任何数字作为示例。\n\n---\n\n### 脚本速查\n\n| 脚本 | AI 什么时候需要调用它 | 速度 |\n|------|---------------------|------|\n| `login_manager.py` | 默认快速检查（<1s），有--init/--reset/--force-login/--verify | ⚡快/🐢慢 |\n| `todos_api.py` | 用户要求查看待办时 | ⚡快（2-5s） |\n| `ops.py` | 下载文件、上传作业、合并PDF、清理工作区 | ⚡快 |\n| `learn_api.py` | 查询课程列表、公告详情、作业内容 | ⚡快 |\n| `pdf_merge.py` | 图片合并PDF（ops.py内部调用） | ⚡快 |\n| `install_deps.py` | AI不直接调用（--verify内部自动调用） | 🐢慢 |\n| `install_playwright.py` | AI不直接调用 | 🐢慢 |\n| `login_auto.py` | AI不直接调用 | 🐢慢（有浏览器） |\n| `login_supervised.py` | AI不直接调用 | 🐢慢（有浏览器） |\n\n> ⚠️ 标🐢的脚本涉及浏览器/docker/下载，AI不应直接调用。它们由 `login_manager.py --force-login` 或 `--verify` 内部自动调用。\n\n---\n\n### 初始化流程\n\n用户说\"开始初始化\"时，AI 按以下步骤执行：\n\n**Step 1：向用户收集信息**\n```\nAI 依次问用户（不要一次全问，分步问）：\n第一部分（必须）：\n1. 网络学堂登录账号是什么？\n2. 登录密码是什么？\n3. 学号是什么？\n4. 姓名是什么？\n\n第二部分（选填，用户回答了必须信息后再问）：\n5. 是否需要自动标已读？（默认：是）\n6. 清理工作区前是否需要确认？（默认：是）\n7. 批量下载前是否需要确认？（默认：是）\n\n⚠️ AI 不编造示例值（不要写\"如 2025013187\"之类的数字）\n⚠️ AI 不显示学号或密码在对话中\n```\n\n**Step 2：AI 调用 `--init` 保存凭据**\n```\n1. AI 将凭据 JSON 写入临时文件（不包含在任何命令字符串中）\n2. 命令：python scripts/login_manager.py --init --cred-stdin < /tmp/cred_temp.json\n3. AI 立即删除临时文件\n输出：{\"status\":\"ok\", \"fields\":{\"learn_account\":\"...\", \"username\":\"...\", \"name\":\"...\"}}\n```\n> 使用临时文件 + stdin 重定向，密码只存在于临时文件中（随即删除），不出现在任何命令行参数或 echo 字符串中。\n\n**Step 3：AI 向用户展示 fields 内容，请用户确认**\n\n**Step 4：用户确认后，AI 调用 `--verify` 验证环境并尝试登录**\n```\n命令：python scripts/login_manager.py --verify\n输出：{\"python\":true, \"deps\":true, \"chromium\":true, \"login\":true, \"needs_2fa\":false}\n\nlogin=true       → Step 5\nneeds_2fa=true   → AI 告诉用户\"自动登录失败，请打开浏览器完成二次验证\"\n                   用户说\"好了\"后，AI 调用：python scripts/login_manager.py\n```\n\n**Step 5：AI 调用 `todos_api.py` 首次查看待办**\n```\n命令：python scripts/todos_api.py\n输出：JSON（含课程列表、待办数量等，submissions 日志自动初始化）\n```\n\n---\n\n### 日常查看待办流程\n\n任何时候用户说\"查看待办\"\"有什么作业\"\"看看通知\"时：\n\n**Step 1：AI 检查凭据和 session**\n\n跑默认命令（快速检查，<1 秒，无浏览器）：\n```\n命令：python scripts/login_manager.py\n输出：{\"initialized\": false/true, \"valid\": false/true, \"age_h\": ...}\n```\n\n根据输出决定下一步：\n\n① `initialized: false` → AI 停下来，告诉用户\"系统尚未初始化\"，走初始化流程\n② `initialized: true, valid: true` → 跳到 Step 2（获取待办）\n③ `initialized: true, valid: false` → AI 跑 `python scripts/login_manager.py --force-login`（会启动浏览器，约30-60秒）\n\n**Step 2：AI 获取待办数据**\n```\n命令：python scripts/todos_api.py\n输出：JSON 包含 courses、suggestions、graded_homeworks、cleanup_suggestion\n```\n\n**Step 3：AI 根据输出中的字段决定问用户什么**\n\n检查 `graded_homeworks` 字段：\n- 如果有内容 → AI 告诉用户\"老师批改了你的作业：[课程名][标题]，评语：[comment]，得分：[score]\"\n\n检查 `submissions_tracked` 字段：\n- 如果 > 0 → AI 告诉用户\"有 N 个已提交的作业等待老师批改，批改后我会通知你\"\n\n检查 `suggestions` 数组：\n- 如果包含 \"运行 --mark-read\" → AI 问用户\"需要把未读公告和课件标为已读吗？\" 同意后加 --confirm 执行\n- 如果包含 \"运行 --cleanup-preview\" → AI 问用户\"需要看看工作区有哪些垃圾文件吗？\"\n\n检查 `suggestions` 和 `graded_homeworks` 都不存在时：\n- AI 告诉用户\"目前没有新的待办事项\"\n\n**Step 4：用户同意后，AI 自己运行对应命令**\n```\n标已读：        python scripts/todos_api.py --mark-read\n清理预览：      python scripts/ops.py --action cleanup --dry-run\n执行清理：      python scripts/ops.py --action cleanup\n```\n\n---\n\n### 提交作业流程\n\n用户说\"我要交作业\"或发来文件/图片时：\n\n**Step 1：AI 确认要交到哪个作业**\n```\n命令：python scripts/learn_api.py --action courses\n输出：课程列表（AI 找到用户说的课程，记录 wlkcid）\n\n命令：python scripts/learn_api.py --action homeworks --course <wlkcid>\n输出：作业列表（AI 找到用户说的作业，记录 xszyid）\n```\n\n**Step 2：AI 处理文件**\n\n情况 A — 用户直接发送了 PDF 文件：\n```\nAI 将文件保存到 uploads/，进入 Step 3\n```\n\n情况 B — 用户发送多张图片（如 QQ 传图）：\n```\nAI 每收到一张图，保存到 uploads/，按顺序命名为 001.jpg、002.jpg...\n用户说\"已经上传完毕\"后：\n命令：python scripts/ops.py --action pdf-merge --input-dir uploads/\n输出：merged.pdf\nAI 删除 uploads/ 下的所有图片文件\n进入 Step 3\n```\n\n**Step 3：AI 展示确认信息，获得同意后提交**\n\n```\n先执行未确认的提交（展示详情，不实际提交）：\n命令：python scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\n输出：{\"status\":\"pending\", \"requiring\":\"confirmation\", \"details\":{...}}\n\nAI 必须向用户展示：\n- 课程名称\n- 作业标题\n- 待提交的文件名\n- 提交账号（姓名+学号脱敏）\n\n例：\"准备提交 微积分(2) 的 第四次书面作业，文件：2025013187_张三.pdf。确认吗？\"\n\n用户明确说\"确认\"/\"好\"/\"提交\"后，AI 添加 --confirm 重新执行：\n命令：python scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path> --confirm\n输出：{\"status\":\"ok\"}\n```\n\n**Step 4：AI 通知用户提交成功，清理临时文件**\n\n---\n\n### 下载课件流程\n\n用户说\"帮我下载课件\"时：\n\n**Step 1：AI 获取课程列表**\n```\n命令：python scripts/learn_api.py --action courses\n输出：课程列表（AI 找到用户说的课程，记录 wlkcid）\n```\n\n**Step 2：AI 下载文件**\n```\n命令：python scripts/ops.py --action download-all --course <wlkcid> --save-dir <目录>\n输出：下载结果\n```\n如果用户指定了具体文件名或模式，在 `--pattern` 参数中指定。\n\n---\n\n### 常见问题处理\n\n**Session 过期**\n```\nAI 不分析 session 文件。不检查时间戳。不自己调 login_auto.py。\n只做一件事：\n命令：python scripts/login_manager.py\n输出：session 有效则返回，无效则自动续期\n```\n\n**需要二次验证（2FA）**\n```\nlogin_manager.py 或 --verify 返回 needs_2fa=true 时：\nStep 1：AI 告诉用户\"自动登录失败，请打开浏览器完成二次验证\"\nStep 2：用户说\"好了\"后，AI 调用：\n命令：python scripts/login_manager.py\n输出：验证 session 是否已建立\n```\n\n**凭据未初始化**\n```\nlogin_manager.py --check 返回 initialized=false 时：\nAI 告诉用户\"系统尚未初始化\"，然后按初始化流程的 Step 1 开始收集信息\n```\n\n### 脚本概览\n\n| 脚本 | 职责 | 什么时候用 |\n|------|------|-----------|\n| `login_manager.py` | 认证网关 | 任何操作前确保 session；初始化；验证环境 |\n| `todos_api.py` | 日常管理 | 用户问待办、标已读、清理 |\n| `ops.py` | 文件操作 | 下载、上传、合并 PDF、清理工作区 |\n| `learn_api.py` | 数据查询 | 查课程列表、公告详情、作业内容 |\n| `pdf_merge.py` | 图片→PDF | 用户发图片要交作业时 |\n| `install_deps.py` | 依赖安装 | --verify 会自动调用，你不直接碰 |\n| `_config.py` | 配置读取 | 你不直接碰 |\n| `login_auto.py` | 无头登录 | 你不直接碰，login_manager 内部调用 |\n| `login_supervised.py` | 2FA 登录 | 你不直接碰，login_manager 内部调用 |\n| `install_playwright.py` | Chromium 安装 | --verify 会自动调用，你不直接碰 |\n\n### 日常运行流程\n\n任何时候用户问\"查看待办\"\"有什么作业\"等：\n\n```\n① 先确保 session:\n   python scripts/login_manager.py\n   ← 如果 session 过期会自动续期\n\n② 获取待办:\n   python scripts/todos_api.py\n   ← 得到 JSON {\"courses\": [...], \"total_unread\": N, \"suggestions\": [...], ...}\n\n③ 解析输出，主动问用户:\n   - suggestions 里有 \"运行 --mark-read\" → \"需要帮你把未读公告和课件标为已读吗？\"\n   - cleanup_suggestion 不为空 → \"工作区有垃圾文件，需要看看吗？\"\n   - graded_homeworks 有内容 → \"老师批了你的作业，评语是...\"\n\n④ 用户确认后，自己跑对应命令:\n    - 标已读: python scripts/todos_api.py --mark-read\n   - 清理预览: python scripts/ops.py --action cleanup --dry-run\n   - 执行清理: python scripts/ops.py --action cleanup\n```\n\n### 详细 CLI 命令\n\n#### login_manager.py\n\n```bash\npython scripts/login_manager.py                                # 确保 session\npython scripts/login_manager.py --check                        # 检查状态\npython scripts/login_manager.py --init --learn-account=... --password=... --username=... --name=...\npython scripts/login_manager.py --reset                        # 清空所有数据\npython scripts/login_manager.py --verify                       # 验证环境（依赖+Chromium+登录）\n```\n\n退出码: 0=有效, 1=错误, 2=需人工 2FA, 3=未初始化\n\n#### learn_api.py\n\n```bash\npython scripts/learn_api.py --action courses                               # 课程列表\npython scripts/learn_api.py --action announcements --course <wlkcid>       # 公告列表\npython scripts/learn_api.py --action announcement-detail --course <wlkcid> --id <ggid>  # 公告详细内容\npython scripts/learn_api.py --action files --course <wlkcid>               # 课件列表\npython scripts/learn_api.py --action homeworks --course <wlkcid>           # 作业列表\npython scripts/learn_api.py --action homework-full --course <wlkcid> --id <zyid> --xszyid <xszyid>  # 作业完整信息（含评语）\npython scripts/learn_api.py --action discussions --course <wlkcid>         # 讨论列表\npython scripts/learn_api.py --action questionnaires --course <wlkcid>      # 问卷列表\npython scripts/learn_api.py --action aggregated --course <wlkcid>          # 聚合查询\npython scripts/learn_api.py --action mark-all-announcements-read --course <wlkcid>\npython scripts/learn_api.py --action mark-all-files-read --course <wlkcid>\n```\n\n#### ops.py\n\n```bash\npython scripts/ops.py --action list-files --course <wlkcid> [--pattern \"*.pdf\"]\npython scripts/ops.py --action download --course <wlkcid> --wjid <wjid>\npython scripts/ops.py --action download --course <wlkcid> --wjid-list \"id1,id2,...\"\npython scripts/ops.py --action download-all --course <wlkcid> [--pattern \"*.pdf\"]\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path> --confirm  # 确认后实际提交\npython scripts/ops.py --action pdf-merge --input-dir <目录> [--output <路径>]\npython scripts/ops.py --action move-in --file <source_path>\npython scripts/ops.py --action cleanup --dry-run\npython scripts/ops.py --action cleanup\n```\n\n#### todos_api.py\n\n```bash\npython scripts/todos_api.py                           # 代办汇总\npython scripts/todos_api.py --mark-read --confirm              # + 标记可读项已读\npython scripts/todos_api.py --cleanup-preview         # + 清理预览\npython scripts/todos_api.py --cleanup                 # + 执行清理\n```\n\n**输出解析**：\n- `suggestions` — AI 读取后应主动询问用户：\"需要帮你把未读公告和课件标为已读吗？\"\"需要帮你看看工作区有哪些垃圾文件吗？\"\n- `graded_homeworks` — 老师新批改的作业及其评语/分数\n- `new_submissions_logged` — 本次新增的已提交未批改作业数量\n\n#### pdf_merge.py\n\n```bash\npython scripts/pdf_merge.py --input-dir <目录> [--output <路径>]\n```\n\n支持: jpg, jpeg, png, webp, bmp, heic, heif\n\n### 常见任务工作流\n\n#### 图片作业提交（QQ 发送图片）\n\n```\n1. 用户每发一张图 → AI 保存到 uploads/，按顺序命名为 001.jpg、002.jpg...\n2. 用户说\"发送完毕\"\n3. ops.py --action pdf-merge --input-dir uploads/ → merged.pdf\n4. 删除 uploads/ 下所有图片（保留 merged.pdf）\n5. ops.py --action upload --course ... --xszyid ... --file uploads/merged.pdf --confirm\n   → 自动重命名为 学号_姓名.pdf\n6. 删除 uploads/merged.pdf\n```\n\n#### 下载课件\n\n```\nlearn_api.py --action courses                    # 获取课程列表+ID\nops.py --action download-all --course <wlkcid>   # 下载全部\n```\n\n### 提交日志机制\n\n文件: `submissions/submissions_log.json`\n\n```\ntodos_api.py 每次运行时:\n  1. sync_submissions_log() → 扫描所有已交未批改作业，新出现的追加到日志（已存在则跳过）\n  2. check_graded_submissions() → 遍历日志，检查是否有作业已被批改\n     有 → 获取评语/分数，从日志中移除，在输出中加入 graded_homeworks\n```\n\n### 配置 (config.json)\n\n```json\n{\n  \"auto_mark_read\": false,\n  \"confirm_before_cleanup\": true,\n  \"confirm_before_download\": true\n}\n```\n\n| 选项 | 说明 | 默认 |\n|------|------|------|\n| `auto_mark_read` | 运行 `todos_api.py` 时自动标已读 | `false` |\n| `confirm_before_cleanup` | 清理前是否需用户确认 | `true` |\n| `confirm_before_download` | 批量下载前是否需用户确认 | `true` |\n\n### 未读判断标准\n\n| 模块 | API 字段 | 过滤值 |\n|------|----------|--------|\n| 作业 | `zt` | `\"未交\"` |\n| 公告 | `sfyd` | `\"否\"` |\n| 课件 | `isNew` | `\"1\"` |\n| 讨论 | `htsl` | `>0` |\n\n### 注意事项与不可做清单\n\n#### 不可做\n\n- **不要硬编码课程 ID、学期、路径、姓名**\n- **不要在脚本中使用 `input()` 或 `getpass()`** — AI 运行脚本，不能阻塞\n- **不要直接修改 `credentials.json`** — 通过 `login_manager.py --init` 操作\n- **不要创建新脚本除非绝对必要**\n- **不要在 `config.json` 之外的地方存储用户信息**\n- **不要告诉用户命令行操作** — 用户只回答\"要/不要\"，AI 代为执行\n\n#### 注意事项\n\n- **文件名编码**：服务器将 UTF-8 的中文文件名以 latin-1 编码发送，`download_file()` 已做修复\n- **作业提交端点**：真实端点是 `/b/wlxt/kczy/zy/student/tjzy`，不是 `uploadFile/saveXszj`\n- **公告标已读**：端点 GET `/f/wlxt/kcgg/wlkc_ggb/student/beforeViewXs`，不是 `saveYd`\n- **公告内容**：直接从列表 API 的 `ggnr` base64 字段解码\n- **学期自动检测**：每次建立 session 后调用 `get_current_semester()`\n- **重置**：`login_manager.py --reset` 保留所有文件结构，只清空内容\n- **已提交的作业无法撤回**：网络学堂不提供撤回已提交作业的 API\n\nFile v1.0.16:_meta.json\n\n{\n  \"ownerId\": \"kn73vfhykr10snz07772njvdmx84mcb6\",\n  \"slug\": \"tsinghua-learn\",\n  \"version\": \"1.0.16\",\n  \"publishedAt\": 1777778777036\n}\n\nFile v1.0.16:AGENTS.md\n\n# tsinghua-learn — AGENTS.md\n\n## 架构\n\n6 个脚本：\n\n| 脚本 | 职责 |\n|------|------|\n| `_config.py` | 路径/配置/凭证、DPAPI 解密 |\n| `login_manager.py` | 中央认证网关（默认 fast check，`--force-login` 完整登录） |\n| `learn_api.py` | HTTP API（课程/公告/课件/作业/讨论/问卷/聚合查询） |\n| `ops.py` | 文件操作（下载/批量下载/上传/移入/清理） |\n| `todos_api.py` | 日常管理（代办汇总/标已读/清理，JSON 给 AI） |\n| `pdf_merge.py` | 图片→PDF |\n\n登录脚本不直接调用：\n| `login_auto.py` | headless 自动登录 |\n| `login_supervised.py` | 2FA 登录 |\n\n## 核心命令\n\n```bash\npython scripts/login_manager.py                        # 快速检查 session (<1s)\npython scripts/login_manager.py --init                 # 初始化凭据\npython scripts/login_manager.py --verify               # 验证环境+登录\npython scripts/login_manager.py --reset                # 清空所有数据\npython scripts/login_manager.py --force-login          # 强制完整登录\n\npython scripts/todos_api.py                            # 代办汇总\npython scripts/todos_api.py --mark-read                # + 标已读\npython scripts/todos_api.py --cleanup                  # + 清理\n\npython scripts/ops.py --action list-files --course <wlkcid>\npython scripts/ops.py --action download --course <wlkcid> --wjid <wjid>\npython scripts/ops.py --action download-all --course <wlkcid>\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\npython scripts/ops.py --action pdf-merge --input-dir <目录>\npython scripts/ops.py --action cleanup --dry-run\npython scripts/ops.py --action cleanup\n```\n\n## 关键约束\n\n- paths/creds 全走 `_config.py` getter\n- `credentials.json` DPAPI 加密\n- 学期从 API 自动检测\n- 课程从 API 动态获取\n- 不创建新脚本\n- 脚本不阻塞（`input()`/`getpass()` 禁用）\n- 提交作业自动重命名：`学号_姓名.ext`\n\nFile v1.0.16:config.json\n\n{\r\n    \"auto_mark_read\": true,\n  \"confirm_before_cleanup\": true,\r\n  \"confirm_before_download\": true\r\n}\n\nFile v1.0.16:credentials.json\n\n{\r\n  \"username\": \"\",\r\n  \"password\": \"\",\r\n  \"student_id\": \"\",\r\n  \"name\": \"\"\r\n}\n\nArchive v1.0.15: 15 files, 39046 bytes\n\nFiles: AGENTS.md (1975b), config.json (102b), credentials.json (77b), scripts/_config.py (6203b), scripts/install_deps.py (2207b), scripts/install_playwright.py (1041b), scripts/learn_api.py (28555b), scripts/login_auto.py (5076b), scripts/login_manager.py (11917b), scripts/login_supervised.py (4454b), scripts/ops.py (13323b), scripts/pdf_merge.py (2569b), scripts/todos_api.py (7662b), SKILL.md (24887b), _meta.json (134b)\n\nFile v1.0.15:SKILL.md\n\n---\nname: tsinghua-learn\ndescription: 清华网络学堂（learn.tsinghua.edu.cn）自动化。要求用户提供网络学堂登录密码（本地 DPAPI 加密存储），自动登录、查看待办、下载课件、提交作业、批量标记已读。当需要操作网络学堂完成日常学习任务时使用。\nmetadata:\n  openclaw:\n    requires:\n      env:\n        - CAS_PASSWORD\n    primaryEnv: CAS_PASSWORD\n    os:\n      - windows\n---\n\n# tsinghua-learn skill\n\n清华网络学堂（learn.tsinghua.edu.cn）自动化 Skill。\n\n---\n\n## 如果你是人类，请阅读以下内容\n\n### 前置要求\n\n使用前请确保已安装 **Python 3.10+** 或 **Conda**（推荐 Python 3.11 或 3.12）。建议安装 **7-Zip** 并添加到环境变量 PATH 中，方便后续处理压缩文件。\n\n初始化时会自动安装 Python 依赖（requests、playwright、pillow 等）以及 Chromium 浏览器（~150MB），无需手动操作。\n\n如果没有安装上述环境，请先安装后再对 AI 说 **\"开始初始化\"**。\n\n### 如何与 AI 沟通\n\n你只需要像聊天一样告诉 AI 你想做什么，AI 会帮你完成所有操作。\n\n#### 初始化\n\n对 AI 说：**\"开始初始化\"**\n\nAI 会依次：\n1. 询问你的网络学堂登录账号、密码、学号、姓名\n2. 检查 Python 环境并自动安装所需依赖\n3. 安装 Chromium 浏览器\n4. 尝试登录网络学堂\n5. 登录成功后询问你的偏好设置（是否自动标已读等）\n6. 完成后即可正常使用\n\n> **四个字段说明：**\n> - **网络学堂登录账号**（`--learn-account`）— 用于登录 learn.tsinghua.edu.cn 的账号，可能和学号不同\n> - **登录密码**（`--password`）— 网络学堂登录密码，仅发送至 id.tsinghua.edu.cn（CAS）和 learn.tsinghua.edu.cn\n> - **学号**（`--username`）— 你的学生证号，如 2025013187\n> - **姓名**（`--name`）— 你的真实姓名\n\n#### 查看待办\n\n对 AI 说：**\"查看待办\"** 或 **\"看看有什么作业\"**\n\nAI 会列出所有课程的最新动态：\n- 未交作业及截止日期\n- 未读公告和未浏览课件\n- 已提交但老师还没批的作业（老师批改后会自动汇报评语和分数）\n- 工作区缓存大小\n\nAI 还会主动问你是否需要标已读、清理缓存。\n\n> 你也可以设置每日自动查看：将 `todos_api.py` 配置为定时任务即可。\n\n#### 提交作业\n\n**方式一：直接发送 PDF 文件**\n\n对 AI 说：**\"我要交某某课程的作业\"**，然后发送 PDF 文件即可。\n\n**方式二：发送多张图片合并为 PDF**\n\n先说：**\"我要交某某课程的作业，我发几张图片给你\"**\n\n然后**按顺序**发送图片（先发的会在最终 PDF 的第一页，后发的在后面）。最后说：**\"已经上传完毕\"**\n\nAI 会先列出要提交的课程、作业标题、文件名，请你确认。确认后自动：\n1. 按你发送的顺序命名图片（001、002...）\n2. 合并为 PDF（按文件名排序，所以先发的在 PDF 前面）\n3. 重命名为 `学号_姓名.pdf`\n4. 提交到指定作业\n\n#### 下载课件\n\n对 AI 说：**\"帮我下载微积分的课件\"**\n\nAI 会列出课程的全部课件文件。你也可以更精确地指定：\n\n- **\"下载未读课件\"** — 只下载还没看过的课件\n- **\"下载概率论第 8 讲的课件\"** — 下载指定名称的文件\n- **\"把微积分的课件全部下载到 D 盘\"** — 批量下载到指定目录\n\nAI 会在下载前列出文件清单让你确认。\n\n#### 查看通知和作业详情\n\n对 AI 说：\n\n- **\"看看微积分最近有什么通知\"** — 查看指定课程的公告\n- **\"概率论 HW10 的作业内容是什么\"** — 查看作业的题目和附件\n- **\"上次交的物理作业老师批了吗\"** — 查看已批改作业的评语和分数\n- **\"看看英语课有什么讨论\"** — 查看课程讨论区\n\n#### 清理工作区\n\nAI 在查看待办时会主动提醒你工作区有缓存。这些缓存是下载的课件、合并 PDF 时的临时图片、调试日志等文件，不影响功能但会占用磁盘空间。\n\n只需说 **\"好\"** 或 **\"清理吧\"**，AI 会先列出待清理的文件让你确认，确认后自动删除。\n\n#### 其他\n\n如果有这里没列出的需求，直接告诉 AI 你想做什么即可。AI 会根据现有功能尽力帮你完成。\n\n#### 数据与安全\n\n本 Skill 会在本地存储以下数据：\n\n| 数据 | 位置 | 说明 |\n|------|------|------|\n| 登录凭据 | `credentials.json` | Windows DPAPI 加密（绑定当前用户账户，仅 Windows 支持） |\n| 浏览器登录状态 | `profiles/learn_profile/` | Chrome Profile 含 cookies，避免每次触发 2FA |\n| Session 令牌 | `sessions/learn_session.json` | JSESSIONID + CSRF Token |\n\n如需清除所有数据，对 AI 说 **\"重置所有数据\"**，AI 会清空以上目录内容并恢复到初始状态。\n\n> 这些数据仅存储在本地计算机，不会上传到任何云端。网络学堂本身不支持注销 API，如需完全登出请手动在浏览器中操作。\n\n#### 修改配置\n\n你可以让 AI 帮你修改以下设置，只需要说\"帮我改一下配置\"或\"设置自动标已读\"即可：\n\n| 配置项 | 说明 | 默认值 |\n|--------|------|--------|\n| `auto_mark_read` | 查看待办时是否自动标已读 | 开启 |\n| `confirm_before_cleanup` | 清理缓存前是否需要你确认 | 需要 |\n| `confirm_before_download` | 批量下载前是否需要你确认 | 需要 |\n\n例如：\n- **\"以后自动标已读吧\"** → AI 会开启 `auto_mark_read`\n- **\"清理的时候不用问我了\"** → AI 会把 `confirm_before_cleanup` 设为 false\n\n---\n\n> **当前版本为测试版本，建议同学们完成提交作业等关键操作后自行登录网络学堂查看是否有错误。**\n\n了解更多信息见 GitHub：[github.com/TOMUIV/tsinghua-learn](https://github.com/TOMUIV/tsinghua-learn)\n\n如有任何问题，联系 **ykx25@mails.tsinghua.edu.cn**\n\n---\n\n## 如果你是 AI，请阅读以下内容\n\n### 铁律（违反会导致用户愤怒）\n\n**铁律 1：AI 运行所有脚本。AI 不能要求用户运行任何命令。**\n- ❌ 禁止的行为：AI 对用户说\"请在终端里运行这个命令\"。\n- ❌ 禁止的行为：AI 给用户一段代码让用户复制粘贴。\n- ✅ 正确的行为：AI 自己调用 `python scripts/xxx.py`，读取输出 JSON，根据输出决定下一步。\n\n**铁律 0（最高优先级）：AI 面向用户的回复中，绝对禁止出现以下内容（除非用户主动要求知情披露）：**\n- ❌ 任何命令行（`python xxx.py`、`cd xxx`）\n- ❌ 任何文件路径（`C:\\Users\\...`、`skills\\...`）\n- ❌ 任何技术术语（\"终端\"、\"PowerShell\"、\"运行脚本\"）\n- ❌ 学号、密码原文\n- ⚠️ 例外：当用户明确询问安装了什么、存储了什么、路径在哪等安全相关问题时，AI 可以简要说明。初始化确认、提交确认等关键操作不受此限。\n\n**用户能看到什么：**\n- ✅ \"系统尚未初始化，请提供你的账号信息\"\n- ✅ \"正在获取待办...\"\n- ✅ \"你有 5 项未交作业\"\n- ❌ \"请在终端里跑这个命令\"\n\n**铁律 2：默认只跑快速检查。`login_manager.py` 默认为 `--check` 行为（<1 秒，无浏览器）。**\n- 当前版本的 `login_manager.py` 不带参数时等同于 `--check`，不会启动浏览器。\n- ❌ 禁止的行为：直接跑 `python scripts/login_auto.py` 或 `python scripts/login_supervised.py`。\n- ✅ 正确的行为：\n  - 跑 `python scripts/login_manager.py`（默认就是快速检查，<1 秒）\n  - `initialized: false` → 停下来，告诉用户\"系统尚未初始化\"，走初始化流程。\n  - `valid: true` → 直接进行后续操作。\n  - `valid: false` → 用 `python scripts/login_manager.py --force-login` 执行完整登录。\n\n**铁律 3：`login_manager.py` 是唯一的认证入口。AI 不直接调用其他登录脚本。**\n- AI 不直接调用 `login_auto.py`。\n- AI 不直接调用 `login_supervised.py`。\n- AI 不直接调用 `install_playwright.py`。\n- 上述脚本由 `login_manager.py` 在内部自动调用。\n\n**铁律 4：AI 不凭经验推断问题。所有判断基于脚本输出的 JSON。**\n\n**铁律 5：密码仅用于初始化收集，之后绝不在对话中重复展示。**\n- 初始化时 AI 询问密码，收集后立即传入脚本，不在后续对话中提及密码值。\n- AI 展示 `fields` 内容时，密码字段已脱敏（显示为 `xxx***`）。\n- ❌ 禁止的行为：AI 在初始化之外的对话中说密码原文。\n\n**铁律 6（提交确认）：提交作业必须先调用不带 --confirm 的上传预览，获得用户明确同意后才能加 --confirm 执行。**\n- ❌ 禁止的行为：AI 直接加 --confirm 提交，不展示预览。\n- ✅ 正确的行为：先不带 --confirm 调 upload 得到 preview JSON → 展示用户 → 同意后再加 --confirm\n\n---\n\n### 凭据字段速查\n\n| 字段 | CLI 参数 | 说明 |\n|------|---------|------|\n| 网络学堂登录账号 | `--learn-account` | 登录 learn.tsinghua.edu.cn 用的账号 |\n| 登录密码 | `--password` | 网络学堂登录密码，仅发送至 id.tsinghua.edu.cn（CAS）和 learn.tsinghua.edu.cn |\n| 学号 | `--username` | 学生证号 |\n| 姓名 | `--name` | 真实姓名 |\n\n> 学号和网络学堂登录账号可能相同也可能不同，分别问清楚。\n> ⚠️ 表中没有示例值。AI 不要编造学号或任何数字作为示例。\n\n---\n\n### 脚本速查\n\n| 脚本 | AI 什么时候需要调用它 | 速度 |\n|------|---------------------|------|\n| `login_manager.py` | 默认快速检查（<1s），有--init/--reset/--force-login/--verify | ⚡快/🐢慢 |\n| `todos_api.py` | 用户要求查看待办时 | ⚡快（2-5s） |\n| `ops.py` | 下载文件、上传作业、合并PDF、清理工作区 | ⚡快 |\n| `learn_api.py` | 查询课程列表、公告详情、作业内容 | ⚡快 |\n| `pdf_merge.py` | 图片合并PDF（ops.py内部调用） | ⚡快 |\n| `install_deps.py` | AI不直接调用（--verify内部自动调用） | 🐢慢 |\n| `install_playwright.py` | AI不直接调用 | 🐢慢 |\n| `login_auto.py` | AI不直接调用 | 🐢慢（有浏览器） |\n| `login_supervised.py` | AI不直接调用 | 🐢慢（有浏览器） |\n\n> ⚠️ 标🐢的脚本涉及浏览器/docker/下载，AI不应直接调用。它们由 `login_manager.py --force-login` 或 `--verify` 内部自动调用。\n\n---\n\n### 初始化流程\n\n用户说\"开始初始化\"时，AI 按以下步骤执行：\n\n**Step 1：向用户收集信息**\n```\nAI 依次问用户（不要一次全问，分步问）：\n第一部分（必须）：\n1. 网络学堂登录账号是什么？\n2. 登录密码是什么？\n3. 学号是什么？\n4. 姓名是什么？\n\n第二部分（选填，用户回答了必须信息后再问）：\n5. 是否需要自动标已读？（默认：是）\n6. 清理工作区前是否需要确认？（默认：是）\n7. 批量下载前是否需要确认？（默认：是）\n\n⚠️ AI 不编造示例值（不要写\"如 2025013187\"之类的数字）\n⚠️ AI 不显示学号或密码在对话中\n```\n\n**Step 2：AI 调用 `--init` 保存凭据**\n```\n1. AI 将凭据 JSON 写入临时文件（不包含在任何命令字符串中）\n2. 命令：python scripts/login_manager.py --init --cred-stdin < /tmp/cred_temp.json\n3. AI 立即删除临时文件\n输出：{\"status\":\"ok\", \"fields\":{\"learn_account\":\"...\", \"username\":\"...\", \"name\":\"...\"}}\n```\n> 使用临时文件 + stdin 重定向，密码只存在于临时文件中（随即删除），不出现在任何命令行参数或 echo 字符串中。\n\n**Step 3：AI 向用户展示 fields 内容，请用户确认**\n\n**Step 4：用户确认后，AI 调用 `--verify` 验证环境并尝试登录**\n```\n命令：python scripts/login_manager.py --verify\n输出：{\"python\":true, \"deps\":true, \"chromium\":true, \"login\":true, \"needs_2fa\":false}\n\nlogin=true       → Step 5\nneeds_2fa=true   → AI 告诉用户\"自动登录失败，请打开浏览器完成二次验证\"\n                   用户说\"好了\"后，AI 调用：python scripts/login_manager.py\n```\n\n**Step 5：AI 调用 `todos_api.py` 首次查看待办**\n```\n命令：python scripts/todos_api.py\n输出：JSON（含课程列表、待办数量等，submissions 日志自动初始化）\n```\n\n---\n\n### 日常查看待办流程\n\n任何时候用户说\"查看待办\"\"有什么作业\"\"看看通知\"时：\n\n**Step 1：AI 检查凭据和 session**\n\n跑默认命令（快速检查，<1 秒，无浏览器）：\n```\n命令：python scripts/login_manager.py\n输出：{\"initialized\": false/true, \"valid\": false/true, \"age_h\": ...}\n```\n\n根据输出决定下一步：\n\n① `initialized: false` → AI 停下来，告诉用户\"系统尚未初始化\"，走初始化流程\n② `initialized: true, valid: true` → 跳到 Step 2（获取待办）\n③ `initialized: true, valid: false` → AI 跑 `python scripts/login_manager.py --force-login`（会启动浏览器，约30-60秒）\n\n**Step 2：AI 获取待办数据**\n```\n命令：python scripts/todos_api.py\n输出：JSON 包含 courses、suggestions、graded_homeworks、cleanup_suggestion\n```\n\n**Step 3：AI 根据输出中的字段决定问用户什么**\n\n检查 `graded_homeworks` 字段：\n- 如果有内容 → AI 告诉用户\"老师批改了你的作业：[课程名][标题]，评语：[comment]，得分：[score]\"\n\n检查 `submissions_tracked` 字段：\n- 如果 > 0 → AI 告诉用户\"有 N 个已提交的作业等待老师批改，批改后我会通知你\"\n\n检查 `suggestions` 数组：\n- 如果包含 \"运行 --mark-read\" → AI 问用户\"需要把未读公告和课件标为已读吗？\" 同意后加 --confirm 执行\n- 如果包含 \"运行 --cleanup-preview\" → AI 问用户\"需要看看工作区有哪些垃圾文件吗？\"\n\n检查 `suggestions` 和 `graded_homeworks` 都不存在时：\n- AI 告诉用户\"目前没有新的待办事项\"\n\n**Step 4：用户同意后，AI 自己运行对应命令**\n```\n标已读：        python scripts/todos_api.py --mark-read\n清理预览：      python scripts/ops.py --action cleanup --dry-run\n执行清理：      python scripts/ops.py --action cleanup\n```\n\n---\n\n### 提交作业流程\n\n用户说\"我要交作业\"或发来文件/图片时：\n\n**Step 1：AI 确认要交到哪个作业**\n```\n命令：python scripts/learn_api.py --action courses\n输出：课程列表（AI 找到用户说的课程，记录 wlkcid）\n\n命令：python scripts/learn_api.py --action homeworks --course <wlkcid>\n输出：作业列表（AI 找到用户说的作业，记录 xszyid）\n```\n\n**Step 2：AI 处理文件**\n\n情况 A — 用户直接发送了 PDF 文件：\n```\nAI 将文件保存到 uploads/，进入 Step 3\n```\n\n情况 B — 用户发送多张图片（如 QQ 传图）：\n```\nAI 每收到一张图，保存到 uploads/，按顺序命名为 001.jpg、002.jpg...\n用户说\"已经上传完毕\"后：\n命令：python scripts/ops.py --action pdf-merge --input-dir uploads/\n输出：merged.pdf\nAI 删除 uploads/ 下的所有图片文件\n进入 Step 3\n```\n\n**Step 3：AI 展示确认信息，获得同意后提交**\n\n```\n先执行未确认的提交（展示详情，不实际提交）：\n命令：python scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\n输出：{\"status\":\"pending\", \"requiring\":\"confirmation\", \"details\":{...}}\n\nAI 必须向用户展示：\n- 课程名称\n- 作业标题\n- 待提交的文件名\n- 提交账号（姓名+学号脱敏）\n\n例：\"准备提交 微积分(2) 的 第四次书面作业，文件：2025013187_张三.pdf。确认吗？\"\n\n用户明确说\"确认\"/\"好\"/\"提交\"后，AI 添加 --confirm 重新执行：\n命令：python scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path> --confirm\n输出：{\"status\":\"ok\"}\n```\n\n**Step 4：AI 通知用户提交成功，清理临时文件**\n\n---\n\n### 下载课件流程\n\n用户说\"帮我下载课件\"时：\n\n**Step 1：AI 获取课程列表**\n```\n命令：python scripts/learn_api.py --action courses\n输出：课程列表（AI 找到用户说的课程，记录 wlkcid）\n```\n\n**Step 2：AI 下载文件**\n```\n命令：python scripts/ops.py --action download-all --course <wlkcid> --save-dir <目录>\n输出：下载结果\n```\n如果用户指定了具体文件名或模式，在 `--pattern` 参数中指定。\n\n---\n\n### 常见问题处理\n\n**Session 过期**\n```\nAI 不分析 session 文件。不检查时间戳。不自己调 login_auto.py。\n只做一件事：\n命令：python scripts/login_manager.py\n输出：session 有效则返回，无效则自动续期\n```\n\n**需要二次验证（2FA）**\n```\nlogin_manager.py 或 --verify 返回 needs_2fa=true 时：\nStep 1：AI 告诉用户\"自动登录失败，请打开浏览器完成二次验证\"\nStep 2：用户说\"好了\"后，AI 调用：\n命令：python scripts/login_manager.py\n输出：验证 session 是否已建立\n```\n\n**凭据未初始化**\n```\nlogin_manager.py --check 返回 initialized=false 时：\nAI 告诉用户\"系统尚未初始化\"，然后按初始化流程的 Step 1 开始收集信息\n```\n\n### 脚本概览\n\n| 脚本 | 职责 | 什么时候用 |\n|------|------|-----------|\n| `login_manager.py` | 认证网关 | 任何操作前确保 session；初始化；验证环境 |\n| `todos_api.py` | 日常管理 | 用户问待办、标已读、清理 |\n| `ops.py` | 文件操作 | 下载、上传、合并 PDF、清理工作区 |\n| `learn_api.py` | 数据查询 | 查课程列表、公告详情、作业内容 |\n| `pdf_merge.py` | 图片→PDF | 用户发图片要交作业时 |\n| `install_deps.py` | 依赖安装 | --verify 会自动调用，你不直接碰 |\n| `_config.py` | 配置读取 | 你不直接碰 |\n| `login_auto.py` | 无头登录 | 你不直接碰，login_manager 内部调用 |\n| `login_supervised.py` | 2FA 登录 | 你不直接碰，login_manager 内部调用 |\n| `install_playwright.py` | Chromium 安装 | --verify 会自动调用，你不直接碰 |\n\n### 日常运行流程\n\n任何时候用户问\"查看待办\"\"有什么作业\"等：\n\n```\n① 先确保 session:\n   python scripts/login_manager.py\n   ← 如果 session 过期会自动续期\n\n② 获取待办:\n   python scripts/todos_api.py\n   ← 得到 JSON {\"courses\": [...], \"total_unread\": N, \"suggestions\": [...], ...}\n\n③ 解析输出，主动问用户:\n   - suggestions 里有 \"运行 --mark-read\" → \"需要帮你把未读公告和课件标为已读吗？\"\n   - cleanup_suggestion 不为空 → \"工作区有垃圾文件，需要看看吗？\"\n   - graded_homeworks 有内容 → \"老师批了你的作业，评语是...\"\n\n④ 用户确认后，自己跑对应命令:\n    - 标已读: python scripts/todos_api.py --mark-read --confirm\n   - 清理预览: python scripts/ops.py --action cleanup --dry-run\n   - 执行清理: python scripts/ops.py --action cleanup\n```\n\n### 详细 CLI 命令\n\n#### login_manager.py\n\n```bash\npython scripts/login_manager.py                                # 确保 session\npython scripts/login_manager.py --check                        # 检查状态\npython scripts/login_manager.py --init --learn-account=... --password=... --username=... --name=...\npython scripts/login_manager.py --reset                        # 清空所有数据\npython scripts/login_manager.py --verify                       # 验证环境（依赖+Chromium+登录）\n```\n\n退出码: 0=有效, 1=错误, 2=需人工 2FA, 3=未初始化\n\n#### learn_api.py\n\n```bash\npython scripts/learn_api.py --action courses                               # 课程列表\npython scripts/learn_api.py --action announcements --course <wlkcid>       # 公告列表\npython scripts/learn_api.py --action announcement-detail --course <wlkcid> --id <ggid>  # 公告详细内容\npython scripts/learn_api.py --action files --course <wlkcid>               # 课件列表\npython scripts/learn_api.py --action homeworks --course <wlkcid>           # 作业列表\npython scripts/learn_api.py --action homework-full --course <wlkcid> --id <zyid> --xszyid <xszyid>  # 作业完整信息（含评语）\npython scripts/learn_api.py --action discussions --course <wlkcid>         # 讨论列表\npython scripts/learn_api.py --action questionnaires --course <wlkcid>      # 问卷列表\npython scripts/learn_api.py --action aggregated --course <wlkcid>          # 聚合查询\npython scripts/learn_api.py --action mark-all-announcements-read --course <wlkcid>\npython scripts/learn_api.py --action mark-all-files-read --course <wlkcid>\n```\n\n#### ops.py\n\n```bash\npython scripts/ops.py --action list-files --course <wlkcid> [--pattern \"*.pdf\"]\npython scripts/ops.py --action download --course <wlkcid> --wjid <wjid>\npython scripts/ops.py --action download --course <wlkcid> --wjid-list \"id1,id2,...\"\npython scripts/ops.py --action download-all --course <wlkcid> [--pattern \"*.pdf\"]\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path> --confirm  # 确认后实际提交\npython scripts/ops.py --action pdf-merge --input-dir <目录> [--output <路径>]\npython scripts/ops.py --action move-in --file <source_path>\npython scripts/ops.py --action cleanup --dry-run\npython scripts/ops.py --action cleanup\n```\n\n#### todos_api.py\n\n```bash\npython scripts/todos_api.py                           # 代办汇总\npython scripts/todos_api.py --mark-read --confirm              # + 标记可读项已读\npython scripts/todos_api.py --cleanup-preview         # + 清理预览\npython scripts/todos_api.py --cleanup                 # + 执行清理\n```\n\n**输出解析**：\n- `suggestions` — AI 读取后应主动询问用户：\"需要帮你把未读公告和课件标为已读吗？\"\"需要帮你看看工作区有哪些垃圾文件吗？\"\n- `graded_homeworks` — 老师新批改的作业及其评语/分数\n- `new_submissions_logged` — 本次新增的已提交未批改作业数量\n\n#### pdf_merge.py\n\n```bash\npython scripts/pdf_merge.py --input-dir <目录> [--output <路径>]\n```\n\n支持: jpg, jpeg, png, webp, bmp, heic, heif\n\n### 常见任务工作流\n\n#### 图片作业提交（QQ 发送图片）\n\n```\n1. 用户每发一张图 → AI 保存到 uploads/，按顺序命名为 001.jpg、002.jpg...\n2. 用户说\"发送完毕\"\n3. ops.py --action pdf-merge --input-dir uploads/ → merged.pdf\n4. 删除 uploads/ 下所有图片（保留 merged.pdf）\n5. ops.py --action upload --course ... --xszyid ... --file uploads/merged.pdf --confirm\n   → 自动重命名为 学号_姓名.pdf\n6. 删除 uploads/merged.pdf\n```\n\n#### 下载课件\n\n```\nlearn_api.py --action courses                    # 获取课程列表+ID\nops.py --action download-all --course <wlkcid>   # 下载全部\n```\n\n### 提交日志机制\n\n文件: `submissions/submissions_log.json`\n\n```\ntodos_api.py 每次运行时:\n  1. sync_submissions_log() → 扫描所有已交未批改作业，新出现的追加到日志（已存在则跳过）\n  2. check_graded_submissions() → 遍历日志，检查是否有作业已被批改\n     有 → 获取评语/分数，从日志中移除，在输出中加入 graded_homeworks\n```\n\n### 配置 (config.json)\n\n```json\n{\n  \"auto_mark_read\": false,\n  \"confirm_before_cleanup\": true,\n  \"confirm_before_download\": true\n}\n```\n\n| 选项 | 说明 | 默认 |\n|------|------|------|\n| `auto_mark_read` | 运行 `todos_api.py` 时自动标已读 | `false` |\n| `confirm_before_cleanup` | 清理前是否需用户确认 | `true` |\n| `confirm_before_download` | 批量下载前是否需用户确认 | `true` |\n\n### 未读判断标准\n\n| 模块 | API 字段 | 过滤值 |\n|------|----------|--------|\n| 作业 | `zt` | `\"未交\"` |\n| 公告 | `sfyd` | `\"否\"` |\n| 课件 | `isNew` | `\"1\"` |\n| 讨论 | `htsl` | `>0` |\n\n### 注意事项与不可做清单\n\n#### 不可做\n\n- **不要硬编码课程 ID、学期、路径、姓名**\n- **不要在脚本中使用 `input()` 或 `getpass()`** — AI 运行脚本，不能阻塞\n- **不要直接修改 `credentials.json`** — 通过 `login_manager.py --init` 操作\n- **不要创建新脚本除非绝对必要**\n- **不要在 `config.json` 之外的地方存储用户信息**\n- **不要告诉用户命令行操作** — 用户只回答\"要/不要\"，AI 代为执行\n\n#### 注意事项\n\n- **文件名编码**：服务器将 UTF-8 的中文文件名以 latin-1 编码发送，`download_file()` 已做修复\n- **作业提交端点**：真实端点是 `/b/wlxt/kczy/zy/student/tjzy`，不是 `uploadFile/saveXszj`\n- **公告标已读**：端点 GET `/f/wlxt/kcgg/wlkc_ggb/student/beforeViewXs`，不是 `saveYd`\n- **公告内容**：直接从列表 API 的 `ggnr` base64 字段解码\n- **学期自动检测**：每次建立 session 后调用 `get_current_semester()`\n- **重置**：`login_manager.py --reset` 保留所有文件结构，只清空内容\n- **已提交的作业无法撤回**：网络学堂不提供撤回已提交作业的 API\n\nFile v1.0.15:_meta.json\n\n{\n  \"ownerId\": \"kn73vfhykr10snz07772njvdmx84mcb6\",\n  \"slug\": \"tsinghua-learn\",\n  \"version\": \"1.0.15\",\n  \"publishedAt\": 1777778431423\n}\n\nFile v1.0.15:AGENTS.md\n\n# tsinghua-learn — AGENTS.md\n\n## 架构\n\n6 个脚本：\n\n| 脚本 | 职责 |\n|------|------|\n| `_config.py` | 路径/配置/凭证、DPAPI 解密 |\n| `login_manager.py` | 中央认证网关（默认 fast check，`--force-login` 完整登录） |\n| `learn_api.py` | HTTP API（课程/公告/课件/作业/讨论/问卷/聚合查询） |\n| `ops.py` | 文件操作（下载/批量下载/上传/移入/清理） |\n| `todos_api.py` | 日常管理（代办汇总/标已读/清理，JSON 给 AI） |\n| `pdf_merge.py` | 图片→PDF |\n\n登录脚本不直接调用：\n| `login_auto.py` | headless 自动登录 |\n| `login_supervised.py` | 2FA 登录 |\n\n## 核心命令\n\n```bash\npython scripts/login_manager.py                        # 快速检查 session (<1s)\npython scripts/login_manager.py --init                 # 初始化凭据\npython scripts/login_manager.py --verify               # 验证环境+登录\npython scripts/login_manager.py --reset                # 清空所有数据\npython scripts/login_manager.py --force-login          # 强制完整登录\n\npython scripts/todos_api.py                            # 代办汇总\npython scripts/todos_api.py --mark-read                # + 标已读\npython scripts/todos_api.py --cleanup                  # + 清理\n\npython scripts/ops.py --action list-files --course <wlkcid>\npython scripts/ops.py --action download --course <wlkcid> --wjid <wjid>\npython scripts/ops.py --action download-all --course <wlkcid>\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\npython scripts/ops.py --action pdf-merge --input-dir <目录>\npython scripts/ops.py --action cleanup --dry-run\npython scripts/ops.py --action cleanup\n```\n\n## 关键约束\n\n- paths/creds 全走 `_config.py` getter\n- `credentials.json` DPAPI 加密\n- 学期从 API 自动检测\n- 课程从 API 动态获取\n- 不创建新脚本\n- 脚本不阻塞（`input()`/`getpass()` 禁用）\n- 提交作业自动重命名：`学号_姓名.ext`\n\nFile v1.0.15:config.json\n\n{\r\n    \"auto_mark_read\": true,\n  \"confirm_before_cleanup\": true,\r\n  \"confirm_before_download\": true\r\n}\n\nFile v1.0.15:credentials.json\n\n{\r\n  \"username\": \"\",\r\n  \"password\": \"\",\r\n  \"student_id\": \"\",\r\n  \"name\": \"\"\r\n}\n\nArchive v1.0.14: 15 files, 38770 bytes\n\nFiles: AGENTS.md (1975b), config.json (102b), credentials.json (77b), scripts/_config.py (6203b), scripts/install_deps.py (2207b), scripts/install_playwright.py (1041b), scripts/learn_api.py (28555b), scripts/login_auto.py (5076b), scripts/login_manager.py (11917b), scripts/login_supervised.py (4454b), scripts/ops.py (13323b), scripts/pdf_merge.py (2569b), scripts/todos_api.py (6365b), SKILL.md (24837b), _meta.json (134b)\n\nFile v1.0.14:SKILL.md\n\n---\nname: tsinghua-learn\ndescription: 清华网络学堂（learn.tsinghua.edu.cn）自动化。要求用户提供网络学堂登录密码（本地 DPAPI 加密存储），自动登录、查看待办、下载课件、提交作业、批量标记已读。当需要操作网络学堂完成日常学习任务时使用。\nmetadata:\n  openclaw:\n    requires:\n      env:\n        - CAS_PASSWORD\n    primaryEnv: CAS_PASSWORD\n    os:\n      - windows\n---\n\n# tsinghua-learn skill\n\n清华网络学堂（learn.tsinghua.edu.cn）自动化 Skill。\n\n---\n\n## 如果你是人类，请阅读以下内容\n\n### 前置要求\n\n使用前请确保已安装 **Python 3.10+** 或 **Conda**（推荐 Python 3.11 或 3.12）。建议安装 **7-Zip** 并添加到环境变量 PATH 中，方便后续处理压缩文件。\n\n初始化时会自动安装 Python 依赖（requests、playwright、pillow 等）以及 Chromium 浏览器（~150MB），无需手动操作。\n\n如果没有安装上述环境，请先安装后再对 AI 说 **\"开始初始化\"**。\n\n### 如何与 AI 沟通\n\n你只需要像聊天一样告诉 AI 你想做什么，AI 会帮你完成所有操作。\n\n#### 初始化\n\n对 AI 说：**\"开始初始化\"**\n\nAI 会依次：\n1. 询问你的网络学堂登录账号、密码、学号、姓名\n2. 检查 Python 环境并自动安装所需依赖\n3. 安装 Chromium 浏览器\n4. 尝试登录网络学堂\n5. 登录成功后询问你的偏好设置（是否自动标已读等）\n6. 完成后即可正常使用\n\n> **四个字段说明：**\n> - **网络学堂登录账号**（`--learn-account`）— 用于登录 learn.tsinghua.edu.cn 的账号，可能和学号不同\n> - **登录密码**（`--password`）— 网络学堂登录密码，仅发送至 id.tsinghua.edu.cn（CAS）和 learn.tsinghua.edu.cn\n> - **学号**（`--username`）— 你的学生证号，如 2025013187\n> - **姓名**（`--name`）— 你的真实姓名\n\n#### 查看待办\n\n对 AI 说：**\"查看待办\"** 或 **\"看看有什么作业\"**\n\nAI 会列出所有课程的最新动态：\n- 未交作业及截止日期\n- 未读公告和未浏览课件\n- 已提交但老师还没批的作业（老师批改后会自动汇报评语和分数）\n- 工作区缓存大小\n\nAI 还会主动问你是否需要标已读、清理缓存。\n\n> 你也可以设置每日自动查看：将 `todos_api.py` 配置为定时任务即可。\n\n#### 提交作业\n\n**方式一：直接发送 PDF 文件**\n\n对 AI 说：**\"我要交某某课程的作业\"**，然后发送 PDF 文件即可。\n\n**方式二：发送多张图片合并为 PDF**\n\n先说：**\"我要交某某课程的作业，我发几张图片给你\"**\n\n然后**按顺序**发送图片（先发的会在最终 PDF 的第一页，后发的在后面）。最后说：**\"已经上传完毕\"**\n\nAI 会先列出要提交的课程、作业标题、文件名，请你确认。确认后自动：\n1. 按你发送的顺序命名图片（001、002...）\n2. 合并为 PDF（按文件名排序，所以先发的在 PDF 前面）\n3. 重命名为 `学号_姓名.pdf`\n4. 提交到指定作业\n\n#### 下载课件\n\n对 AI 说：**\"帮我下载微积分的课件\"**\n\nAI 会列出课程的全部课件文件。你也可以更精确地指定：\n\n- **\"下载未读课件\"** — 只下载还没看过的课件\n- **\"下载概率论第 8 讲的课件\"** — 下载指定名称的文件\n- **\"把微积分的课件全部下载到 D 盘\"** — 批量下载到指定目录\n\nAI 会在下载前列出文件清单让你确认。\n\n#### 查看通知和作业详情\n\n对 AI 说：\n\n- **\"看看微积分最近有什么通知\"** — 查看指定课程的公告\n- **\"概率论 HW10 的作业内容是什么\"** — 查看作业的题目和附件\n- **\"上次交的物理作业老师批了吗\"** — 查看已批改作业的评语和分数\n- **\"看看英语课有什么讨论\"** — 查看课程讨论区\n\n#### 清理工作区\n\nAI 在查看待办时会主动提醒你工作区有缓存。这些缓存是下载的课件、合并 PDF 时的临时图片、调试日志等文件，不影响功能但会占用磁盘空间。\n\n只需说 **\"好\"** 或 **\"清理吧\"**，AI 会先列出待清理的文件让你确认，确认后自动删除。\n\n#### 其他\n\n如果有这里没列出的需求，直接告诉 AI 你想做什么即可。AI 会根据现有功能尽力帮你完成。\n\n#### 数据与安全\n\n本 Skill 会在本地存储以下数据：\n\n| 数据 | 位置 | 说明 |\n|------|------|------|\n| 登录凭据 | `credentials.json` | Windows DPAPI 加密（绑定当前用户账户，仅 Windows 支持） |\n| 浏览器登录状态 | `profiles/learn_profile/` | Chrome Profile 含 cookies，避免每次触发 2FA |\n| Session 令牌 | `sessions/learn_session.json` | JSESSIONID + CSRF Token |\n\n如需清除所有数据，对 AI 说 **\"重置所有数据\"**，AI 会清空以上目录内容并恢复到初始状态。\n\n> 这些数据仅存储在本地计算机，不会上传到任何云端。网络学堂本身不支持注销 API，如需完全登出请手动在浏览器中操作。\n\n#### 修改配置\n\n你可以让 AI 帮你修改以下设置，只需要说\"帮我改一下配置\"或\"设置自动标已读\"即可：\n\n| 配置项 | 说明 | 默认值 |\n|--------|------|--------|\n| `auto_mark_read` | 查看待办时是否自动标已读 | 开启 |\n| `confirm_before_cleanup` | 清理缓存前是否需要你确认 | 需要 |\n| `confirm_before_download` | 批量下载前是否需要你确认 | 需要 |\n\n例如：\n- **\"以后自动标已读吧\"** → AI 会开启 `auto_mark_read`\n- **\"清理的时候不用问我了\"** → AI 会把 `confirm_before_cleanup` 设为 false\n\n---\n\n> **当前版本为测试版本，建议同学们完成提交作业等关键操作后自行登录网络学堂查看是否有错误。**\n\n了解更多信息见 GitHub：[github.com/TOMUIV/tsinghua-learn](https://github.com/TOMUIV/tsinghua-learn)\n\n如有任何问题，联系 **ykx25@mails.tsinghua.edu.cn**\n\n---\n\n## 如果你是 AI，请阅读以下内容\n\n### 铁律（违反会导致用户愤怒）\n\n**铁律 1：AI 运行所有脚本。AI 不能要求用户运行任何命令。**\n- ❌ 禁止的行为：AI 对用户说\"请在终端里运行这个命令\"。\n- ❌ 禁止的行为：AI 给用户一段代码让用户复制粘贴。\n- ✅ 正确的行为：AI 自己调用 `python scripts/xxx.py`，读取输出 JSON，根据输出决定下一步。\n\n**铁律 0（最高优先级）：AI 面向用户的回复中，绝对禁止出现以下内容（除非用户主动要求知情披露）：**\n- ❌ 任何命令行（`python xxx.py`、`cd xxx`）\n- ❌ 任何文件路径（`C:\\Users\\...`、`skills\\...`）\n- ❌ 任何技术术语（\"终端\"、\"PowerShell\"、\"运行脚本\"）\n- ❌ 学号、密码原文\n- ⚠️ 例外：当用户明确询问安装了什么、存储了什么、路径在哪等安全相关问题时，AI 可以简要说明。初始化确认、提交确认等关键操作不受此限。\n\n**用户能看到什么：**\n- ✅ \"系统尚未初始化，请提供你的账号信息\"\n- ✅ \"正在获取待办...\"\n- ✅ \"你有 5 项未交作业\"\n- ❌ \"请在终端里跑这个命令\"\n\n**铁律 2：默认只跑快速检查。`login_manager.py` 默认为 `--check` 行为（<1 秒，无浏览器）。**\n- 当前版本的 `login_manager.py` 不带参数时等同于 `--check`，不会启动浏览器。\n- ❌ 禁止的行为：直接跑 `python scripts/login_auto.py` 或 `python scripts/login_supervised.py`。\n- ✅ 正确的行为：\n  - 跑 `python scripts/login_manager.py`（默认就是快速检查，<1 秒）\n  - `initialized: false` → 停下来，告诉用户\"系统尚未初始化\"，走初始化流程。\n  - `valid: true` → 直接进行后续操作。\n  - `valid: false` → 用 `python scripts/login_manager.py --force-login` 执行完整登录。\n\n**铁律 3：`login_manager.py` 是唯一的认证入口。AI 不直接调用其他登录脚本。**\n- AI 不直接调用 `login_auto.py`。\n- AI 不直接调用 `login_supervised.py`。\n- AI 不直接调用 `install_playwright.py`。\n- 上述脚本由 `login_manager.py` 在内部自动调用。\n\n**铁律 4：AI 不凭经验推断问题。所有判断基于脚本输出的 JSON。**\n\n**铁律 5：密码仅用于初始化收集，之后绝不在对话中重复展示。**\n- 初始化时 AI 询问密码，收集后立即传入脚本，不在后续对话中提及密码值。\n- AI 展示 `fields` 内容时，密码字段已脱敏（显示为 `xxx***`）。\n- ❌ 禁止的行为：AI 在初始化之外的对话中说密码原文。\n\n**铁律 6（提交确认）：提交作业必须先调用不带 --confirm 的上传预览，获得用户明确同意后才能加 --confirm 执行。**\n- ❌ 禁止的行为：AI 直接加 --confirm 提交，不展示预览。\n- ✅ 正确的行为：先不带 --confirm 调 upload 得到 preview JSON → 展示用户 → 同意后再加 --confirm\n\n---\n\n### 凭据字段速查\n\n| 字段 | CLI 参数 | 说明 |\n|------|---------|------|\n| 网络学堂登录账号 | `--learn-account` | 登录 learn.tsinghua.edu.cn 用的账号 |\n| 登录密码 | `--password` | 网络学堂登录密码，仅发送至 id.tsinghua.edu.cn（CAS）和 learn.tsinghua.edu.cn |\n| 学号 | `--username` | 学生证号 |\n| 姓名 | `--name` | 真实姓名 |\n\n> 学号和网络学堂登录账号可能相同也可能不同，分别问清楚。\n> ⚠️ 表中没有示例值。AI 不要编造学号或任何数字作为示例。\n\n---\n\n### 脚本速查\n\n| 脚本 | AI 什么时候需要调用它 | 速度 |\n|------|---------------------|------|\n| `login_manager.py` | 默认快速检查（<1s），有--init/--reset/--force-login/--verify | ⚡快/🐢慢 |\n| `todos_api.py` | 用户要求查看待办时 | ⚡快（2-5s） |\n| `ops.py` | 下载文件、上传作业、合并PDF、清理工作区 | ⚡快 |\n| `learn_api.py` | 查询课程列表、公告详情、作业内容 | ⚡快 |\n| `pdf_merge.py` | 图片合并PDF（ops.py内部调用） | ⚡快 |\n| `install_deps.py` | AI不直接调用（--verify内部自动调用） | 🐢慢 |\n| `install_playwright.py` | AI不直接调用 | 🐢慢 |\n| `login_auto.py` | AI不直接调用 | 🐢慢（有浏览器） |\n| `login_supervised.py` | AI不直接调用 | 🐢慢（有浏览器） |\n\n> ⚠️ 标🐢的脚本涉及浏览器/docker/下载，AI不应直接调用。它们由 `login_manager.py --force-login` 或 `--verify` 内部自动调用。\n\n---\n\n### 初始化流程\n\n用户说\"开始初始化\"时，AI 按以下步骤执行：\n\n**Step 1：向用户收集信息**\n```\nAI 依次问用户（不要一次全问，分步问）：\n第一部分（必须）：\n1. 网络学堂登录账号是什么？\n2. 登录密码是什么？\n3. 学号是什么？\n4. 姓名是什么？\n\n第二部分（选填，用户回答了必须信息后再问）：\n5. 是否需要自动标已读？（默认：是）\n6. 清理工作区前是否需要确认？（默认：是）\n7. 批量下载前是否需要确认？（默认：是）\n\n⚠️ AI 不编造示例值（不要写\"如 2025013187\"之类的数字）\n⚠️ AI 不显示学号或密码在对话中\n```\n\n**Step 2：AI 调用 `--init` 保存凭据**\n```\n1. AI 将凭据 JSON 写入临时文件（不包含在任何命令字符串中）\n2. 命令：python scripts/login_manager.py --init --cred-stdin < /tmp/cred_temp.json\n3. AI 立即删除临时文件\n输出：{\"status\":\"ok\", \"fields\":{\"learn_account\":\"...\", \"username\":\"...\", \"name\":\"...\"}}\n```\n> 使用临时文件 + stdin 重定向，密码只存在于临时文件中（随即删除），不出现在任何命令行参数或 echo 字符串中。\n\n**Step 3：AI 向用户展示 fields 内容，请用户确认**\n\n**Step 4：用户确认后，AI 调用 `--verify` 验证环境并尝试登录**\n```\n命令：python scripts/login_manager.py --verify\n输出：{\"python\":true, \"deps\":true, \"chromium\":true, \"login\":true, \"needs_2fa\":false}\n\nlogin=true       → Step 5\nneeds_2fa=true   → AI 告诉用户\"自动登录失败，请打开浏览器完成二次验证\"\n                   用户说\"好了\"后，AI 调用：python scripts/login_manager.py\n```\n\n**Step 5：AI 调用 `todos_api.py` 首次查看待办**\n```\n命令：python scripts/todos_api.py\n输出：JSON（含课程列表、待办数量等，submissions 日志自动初始化）\n```\n\n---\n\n### 日常查看待办流程\n\n任何时候用户说\"查看待办\"\"有什么作业\"\"看看通知\"时：\n\n**Step 1：AI 检查凭据和 session**\n\n跑默认命令（快速检查，<1 秒，无浏览器）：\n```\n命令：python scripts/login_manager.py\n输出：{\"initialized\": false/true, \"valid\": false/true, \"age_h\": ...}\n```\n\n根据输出决定下一步：\n\n① `initialized: false` → AI 停下来，告诉用户\"系统尚未初始化\"，走初始化流程\n② `initialized: true, valid: true` → 跳到 Step 2（获取待办）\n③ `initialized: true, valid: false` → AI 跑 `python scripts/login_manager.py --force-login`（会启动浏览器，约30-60秒）\n\n**Step 2：AI 获取待办数据**\n```\n命令：python scripts/todos_api.py\n输出：JSON 包含 courses、suggestions、graded_homeworks、cleanup_suggestion\n```\n\n**Step 3：AI 根据输出中的字段决定问用户什么**\n\n检查 `graded_homeworks` 字段：\n- 如果有内容 → AI 告诉用户\"老师批改了你的作业：[课程名][标题]，评语：[comment]，得分：[score]\"\n\n检查 `submissions_tracked` 字段：\n- 如果 > 0 → AI 告诉用户\"有 N 个已提交的作业等待老师批改，批改后我会通知你\"\n\n检查 `suggestions` 数组：\n- 如果包含 \"运行 --mark-read\" → AI 问用户\"需要把未读公告和课件标为已读吗？\"\n- 如果包含 \"运行 --cleanup-preview\" → AI 问用户\"需要看看工作区有哪些垃圾文件吗？\"\n\n检查 `suggestions` 和 `graded_homeworks` 都不存在时：\n- AI 告诉用户\"目前没有新的待办事项\"\n\n**Step 4：用户同意后，AI 自己运行对应命令**\n```\n标已读：        python scripts/todos_api.py --mark-read\n清理预览：      python scripts/ops.py --action cleanup --dry-run\n执行清理：      python scripts/ops.py --action cleanup\n```\n\n---\n\n### 提交作业流程\n\n用户说\"我要交作业\"或发来文件/图片时：\n\n**Step 1：AI 确认要交到哪个作业**\n```\n命令：python scripts/learn_api.py --action courses\n输出：课程列表（AI 找到用户说的课程，记录 wlkcid）\n\n命令：python scripts/learn_api.py --action homeworks --course <wlkcid>\n输出：作业列表（AI 找到用户说的作业，记录 xszyid）\n```\n\n**Step 2：AI 处理文件**\n\n情况 A — 用户直接发送了 PDF 文件：\n```\nAI 将文件保存到 uploads/，进入 Step 3\n```\n\n情况 B — 用户发送多张图片（如 QQ 传图）：\n```\nAI 每收到一张图，保存到 uploads/，按顺序命名为 001.jpg、002.jpg...\n用户说\"已经上传完毕\"后：\n命令：python scripts/ops.py --action pdf-merge --input-dir uploads/\n输出：merged.pdf\nAI 删除 uploads/ 下的所有图片文件\n进入 Step 3\n```\n\n**Step 3：AI 展示确认信息，获得同意后提交**\n\n```\n先执行未确认的提交（展示详情，不实际提交）：\n命令：python scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\n输出：{\"status\":\"pending\", \"requiring\":\"confirmation\", \"details\":{...}}\n\nAI 必须向用户展示：\n- 课程名称\n- 作业标题\n- 待提交的文件名\n- 提交账号（姓名+学号脱敏）\n\n例：\"准备提交 微积分(2) 的 第四次书面作业，文件：2025013187_张三.pdf。确认吗？\"\n\n用户明确说\"确认\"/\"好\"/\"提交\"后，AI 添加 --confirm 重新执行：\n命令：python scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path> --confirm\n输出：{\"status\":\"ok\"}\n```\n\n**Step 4：AI 通知用户提交成功，清理临时文件**\n\n---\n\n### 下载课件流程\n\n用户说\"帮我下载课件\"时：\n\n**Step 1：AI 获取课程列表**\n```\n命令：python scripts/learn_api.py --action courses\n输出：课程列表（AI 找到用户说的课程，记录 wlkcid）\n```\n\n**Step 2：AI 下载文件**\n```\n命令：python scripts/ops.py --action download-all --course <wlkcid> --save-dir <目录>\n输出：下载结果\n```\n如果用户指定了具体文件名或模式，在 `--pattern` 参数中指定。\n\n---\n\n### 常见问题处理\n\n**Session 过期**\n```\nAI 不分析 session 文件。不检查时间戳。不自己调 login_auto.py。\n只做一件事：\n命令：python scripts/login_manager.py\n输出：session 有效则返回，无效则自动续期\n```\n\n**需要二次验证（2FA）**\n```\nlogin_manager.py 或 --verify 返回 needs_2fa=true 时：\nStep 1：AI 告诉用户\"自动登录失败，请打开浏览器完成二次验证\"\nStep 2：用户说\"好了\"后，AI 调用：\n命令：python scripts/login_manager.py\n输出：验证 session 是否已建立\n```\n\n**凭据未初始化**\n```\nlogin_manager.py --check 返回 initialized=false 时：\nAI 告诉用户\"系统尚未初始化\"，然后按初始化流程的 Step 1 开始收集信息\n```\n\n### 脚本概览\n\n| 脚本 | 职责 | 什么时候用 |\n|------|------|-----------|\n| `login_manager.py` | 认证网关 | 任何操作前确保 session；初始化；验证环境 |\n| `todos_api.py` | 日常管理 | 用户问待办、标已读、清理 |\n| `ops.py` | 文件操作 | 下载、上传、合并 PDF、清理工作区 |\n| `learn_api.py` | 数据查询 | 查课程列表、公告详情、作业内容 |\n| `pdf_merge.py` | 图片→PDF | 用户发图片要交作业时 |\n| `install_deps.py` | 依赖安装 | --verify 会自动调用，你不直接碰 |\n| `_config.py` | 配置读取 | 你不直接碰 |\n| `login_auto.py` | 无头登录 | 你不直接碰，login_manager 内部调用 |\n| `login_supervised.py` | 2FA 登录 | 你不直接碰，login_manager 内部调用 |\n| `install_playwright.py` | Chromium 安装 | --verify 会自动调用，你不直接碰 |\n\n### 日常运行流程\n\n任何时候用户问\"查看待办\"\"有什么作业\"等：\n\n```\n① 先确保 session:\n   python scripts/login_manager.py\n   ← 如果 session 过期会自动续期\n\n② 获取待办:\n   python scripts/todos_api.py\n   ← 得到 JSON {\"courses\": [...], \"total_unread\": N, \"suggestions\": [...], ...}\n\n③ 解析输出，主动问用户:\n   - suggestions 里有 \"运行 --mark-read\" → \"需要帮你把未读公告和课件标为已读吗？\"\n   - cleanup_suggestion 不为空 → \"工作区有垃圾文件，需要看看吗？\"\n   - graded_homeworks 有内容 → \"老师批了你的作业，评语是...\"\n\n④ 用户确认后，自己跑对应命令:\n   - 标已读: python scripts/todos_api.py --mark-read\n   - 清理预览: python scripts/ops.py --action cleanup --dry-run\n   - 执行清理: python scripts/ops.py --action cleanup\n```\n\n### 详细 CLI 命令\n\n#### login_manager.py\n\n```bash\npython scripts/login_manager.py                                # 确保 session\npython scripts/login_manager.py --check                        # 检查状态\npython scripts/login_manager.py --init --learn-account=... --password=... --username=... --name=...\npython scripts/login_manager.py --reset                        # 清空所有数据\npython scripts/login_manager.py --verify                       # 验证环境（依赖+Chromium+登录）\n```\n\n退出码: 0=有效, 1=错误, 2=需人工 2FA, 3=未初始化\n\n#### learn_api.py\n\n```bash\npython scripts/learn_api.py --action courses                               # 课程列表\npython scripts/learn_api.py --action announcements --course <wlkcid>       # 公告列表\npython scripts/learn_api.py --action announcement-detail --course <wlkcid> --id <ggid>  # 公告详细内容\npython scripts/learn_api.py --action files --course <wlkcid>               # 课件列表\npython scripts/learn_api.py --action homeworks --course <wlkcid>           # 作业列表\npython scripts/learn_api.py --action homework-full --course <wlkcid> --id <zyid> --xszyid <xszyid>  # 作业完整信息（含评语）\npython scripts/learn_api.py --action discussions --course <wlkcid>         # 讨论列表\npython scripts/learn_api.py --action questionnaires --course <wlkcid>      # 问卷列表\npython scripts/learn_api.py --action aggregated --course <wlkcid>          # 聚合查询\npython scripts/learn_api.py --action mark-all-announcements-read --course <wlkcid>\npython scripts/learn_api.py --action mark-all-files-read --course <wlkcid>\n```\n\n#### ops.py\n\n```bash\npython scripts/ops.py --action list-files --course <wlkcid> [--pattern \"*.pdf\"]\npython scripts/ops.py --action download --course <wlkcid> --wjid <wjid>\npython scripts/ops.py --action download --course <wlkcid> --wjid-list \"id1,id2,...\"\npython scripts/ops.py --action download-all --course <wlkcid> [--pattern \"*.pdf\"]\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path> --confirm  # 确认后实际提交\npython scripts/ops.py --action pdf-merge --input-dir <目录> [--output <路径>]\npython scripts/ops.py --action move-in --file <source_path>\npython scripts/ops.py --action cleanup --dry-run\npython scripts/ops.py --action cleanup\n```\n\n#### todos_api.py\n\n```bash\npython scripts/todos_api.py                           # 代办汇总\npython scripts/todos_api.py --mark-read               # + 标记可读项已读\npython scripts/todos_api.py --cleanup-preview         # + 清理预览\npython scripts/todos_api.py --cleanup                 # + 执行清理\n```\n\n**输出解析**：\n- `suggestions` — AI 读取后应主动询问用户：\"需要帮你把未读公告和课件标为已读吗？\"\"需要帮你看看工作区有哪些垃圾文件吗？\"\n- `graded_homeworks` — 老师新批改的作业及其评语/分数\n- `new_submissions_logged` — 本次新增的已提交未批改作业数量\n\n#### pdf_merge.py\n\n```bash\npython scripts/pdf_merge.py --input-dir <目录> [--output <路径>]\n```\n\n支持: jpg, jpeg, png, webp, bmp, heic, heif\n\n### 常见任务工作流\n\n#### 图片作业提交（QQ 发送图片）\n\n```\n1. 用户每发一张图 → AI 保存到 uploads/，按顺序命名为 001.jpg、002.jpg...\n2. 用户说\"发送完毕\"\n3. ops.py --action pdf-merge --input-dir uploads/ → merged.pdf\n4. 删除 uploads/ 下所有图片（保留 merged.pdf）\n5. ops.py --action upload --course ... --xszyid ... --file uploads/merged.pdf --confirm\n   → 自动重命名为 学号_姓名.pdf\n6. 删除 uploads/merged.pdf\n```\n\n#### 下载课件\n\n```\nlearn_api.py --action courses                    # 获取课程列表+ID\nops.py --action download-all --course <wlkcid>   # 下载全部\n```\n\n### 提交日志机制\n\n文件: `submissions/submissions_log.json`\n\n```\ntodos_api.py 每次运行时:\n  1. sync_submissions_log() → 扫描所有已交未批改作业，新出现的追加到日志（已存在则跳过）\n  2. check_graded_submissions() → 遍历日志，检查是否有作业已被批改\n     有 → 获取评语/分数，从日志中移除，在输出中加入 graded_homeworks\n```\n\n### 配置 (config.json)\n\n```json\n{\n  \"auto_mark_read\": false,\n  \"confirm_before_cleanup\": true,\n  \"confirm_before_download\": true\n}\n```\n\n| 选项 | 说明 | 默认 |\n|------|------|------|\n| `auto_mark_read` | 运行 `todos_api.py` 时自动标已读 | `false` |\n| `confirm_before_cleanup` | 清理前是否需用户确认 | `true` |\n| `confirm_before_download` | 批量下载前是否需用户确认 | `true` |\n\n### 未读判断标准\n\n| 模块 | API 字段 | 过滤值 |\n|------|----------|--------|\n| 作业 | `zt` | `\"未交\"` |\n| 公告 | `sfyd` | `\"否\"` |\n| 课件 | `isNew` | `\"1\"` |\n| 讨论 | `htsl` | `>0` |\n\n### 注意事项与不可做清单\n\n#### 不可做\n\n- **不要硬编码课程 ID、学期、路径、姓名**\n- **不要在脚本中使用 `input()` 或 `getpass()`** — AI 运行脚本，不能阻塞\n- **不要直接修改 `credentials.json`** — 通过 `login_manager.py --init` 操作\n- **不要创建新脚本除非绝对必要**\n- **不要在 `config.json` 之外的地方存储用户信息**\n- **不要告诉用户命令行操作** — 用户只回答\"要/不要\"，AI 代为执行\n\n#### 注意事项\n\n- **文件名编码**：服务器将 UTF-8 的中文文件名以 latin-1 编码发送，`download_file()` 已做修复\n- **作业提交端点**：真实端点是 `/b/wlxt/kczy/zy/student/tjzy`，不是 `uploadFile/saveXszj`\n- **公告标已读**：端点 GET `/f/wlxt/kcgg/wlkc_ggb/student/beforeViewXs`，不是 `saveYd`\n- **公告内容**：直接从列表 API 的 `ggnr` base64 字段解码\n- **学期自动检测**：每次建立 session 后调用 `get_current_semester()`\n- **重置**：`login_manager.py --reset` 保留所有文件结构，只清空内容\n- **已提交的作业无法撤回**：网络学堂不提供撤回已提交作业的 API\n\nFile v1.0.14:_meta.json\n\n{\n  \"ownerId\": \"kn73vfhykr10snz07772njvdmx84mcb6\",\n  \"slug\": \"tsinghua-learn\",\n  \"version\": \"1.0.14\",\n  \"publishedAt\": 1777773217191\n}\n\nFile v1.0.14:AGENTS.md\n\n# tsinghua-learn — AGENTS.md\n\n## 架构\n\n6 个脚本：\n\n| 脚本 | 职责 |\n|------|------|\n| `_config.py` | 路径/配置/凭证、DPAPI 解密 |\n| `login_manager.py` | 中央认证网关（默认 fast check，`--force-login` 完整登录） |\n| `learn_api.py` | HTTP API（课程/公告/课件/作业/讨论/问卷/聚合查询） |\n| `ops.py` | 文件操作（下载/批量下载/上传/移入/清理） |\n| `todos_api.py` | 日常管理（代办汇总/标已读/清理，JSON 给 AI） |\n| `pdf_merge.py` | 图片→PDF |\n\n登录脚本不直接调用：\n| `login_auto.py` | headless 自动登录 |\n| `login_supervised.py` | 2FA 登录 |\n\n## 核心命令\n\n```bash\npython scripts/login_manager.py                        # 快速检查 session (<1s)\npython scripts/login_manager.py --init                 # 初始化凭据\npython scripts/login_manager.py --verify               # 验证环境+登录\npython scripts/login_manager.py --reset                # 清空所有数据\npython scripts/login_manager.py --force-login          # 强制完整登录\n\npython scripts/todos_api.py                            # 代办汇总\npython scripts/todos_api.py --mark-read                # + 标已读\npython scripts/todos_api.py --cleanup                  # + 清理\n\npython scripts/ops.py --action list-files --course <wlkcid>\npython scripts/ops.py --action download --course <wlkcid> --wjid <wjid>\npython scripts/ops.py --action download-all --course <wlkcid>\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\npython scripts/ops.py --action pdf-merge --input-dir <目录>\npython scripts/ops.py --action cleanup --dry-run\npython scripts/ops.py --action cleanup\n```\n\n## 关键约束\n\n- paths/creds 全走 `_config.py` getter\n- `credentials.json` DPAPI 加密\n- 学期从 API 自动检测\n- 课程从 API 动态获取\n- 不创建新脚本\n- 脚本不阻塞（`input()`/`getpass()` 禁用）\n- 提交作业自动重命名：`学号_姓名.ext`\n\nFile v1.0.14:config.json\n\n{\r\n    \"auto_mark_read\": true,\n  \"confirm_before_cleanup\": true,\r\n  \"confirm_before_download\": true\r\n}\n\nFile v1.0.14:credentials.json\n\n{\r\n  \"username\": \"\",\r\n  \"password\": \"\",\r\n  \"student_id\": \"\",\r\n  \"name\": \"\"\r\n}\n\nArchive v1.0.13: 15 files, 39238 bytes\n\nFiles: AGENTS.md (1975b), config.json (102b), credentials.json (77b), scripts/_config.py (6203b), scripts/install_deps.py (2207b), scripts/install_playwright.py (1041b), scripts/learn_api.py (28555b), scripts/login_auto.py (5076b), scripts/login_manager.py (11917b), scripts/login_supervised.py (5191b), scripts/ops.py (13323b), scripts/pdf_merge.py (2569b), scripts/todos_api.py (6365b), SKILL.md (24837b), _meta.json (134b)\n\nFile v1.0.13:SKILL.md\n\n---\nname: tsinghua-learn\ndescription: 清华网络学堂（learn.tsinghua.edu.cn）自动化。要求用户提供网络学堂登录密码（本地 DPAPI 加密存储），自动登录、查看待办、下载课件、提交作业、批量标记已读。当需要操作网络学堂完成日常学习任务时使用。\nmetadata:\n  openclaw:\n    requires:\n      env:\n        - CAS_PASSWORD\n    primaryEnv: CAS_PASSWORD\n    os:\n      - windows\n---\n\n# tsinghua-learn skill\n\n清华网络学堂（learn.tsinghua.edu.cn）自动化 Skill。\n\n---\n\n## 如果你是人类，请阅读以下内容\n\n### 前置要求\n\n使用前请确保已安装 **Python 3.10+** 或 **Conda**（推荐 Python 3.11 或 3.12）。建议安装 **7-Zip** 并添加到环境变量 PATH 中，方便后续处理压缩文件。\n\n初始化时会自动安装 Python 依赖（requests、playwright、pillow 等）以及 Chromium 浏览器（~150MB），无需手动操作。\n\n如果没有安装上述环境，请先安装后再对 AI 说 **\"开始初始化\"**。\n\n### 如何与 AI 沟通\n\n你只需要像聊天一样告诉 AI 你想做什么，AI 会帮你完成所有操作。\n\n#### 初始化\n\n对 AI 说：**\"开始初始化\"**\n\nAI 会依次：\n1. 询问你的网络学堂登录账号、密码、学号、姓名\n2. 检查 Python 环境并自动安装所需依赖\n3. 安装 Chromium 浏览器\n4. 尝试登录网络学堂\n5. 登录成功后询问你的偏好设置（是否自动标已读等）\n6. 完成后即可正常使用\n\n> **四个字段说明：**\n> - **网络学堂登录账号**（`--learn-account`）— 用于登录 learn.tsinghua.edu.cn 的账号，可能和学号不同\n> - **登录密码**（`--password`）— 网络学堂登录密码，仅发送至 id.tsinghua.edu.cn（CAS）和 learn.tsinghua.edu.cn\n> - **学号**（`--username`）— 你的学生证号，如 2025013187\n> - **姓名**（`--name`）— 你的真实姓名\n\n#### 查看待办\n\n对 AI 说：**\"查看待办\"** 或 **\"看看有什么作业\"**\n\nAI 会列出所有课程的最新动态：\n- 未交作业及截止日期\n- 未读公告和未浏览课件\n- 已提交但老师还没批的作业（老师批改后会自动汇报评语和分数）\n- 工作区缓存大小\n\nAI 还会主动问你是否需要标已读、清理缓存。\n\n> 你也可以设置每日自动查看：将 `todos_api.py` 配置为定时任务即可。\n\n#### 提交作业\n\n**方式一：直接发送 PDF 文件**\n\n对 AI 说：**\"我要交某某课程的作业\"**，然后发送 PDF 文件即可。\n\n**方式二：发送多张图片合并为 PDF**\n\n先说：**\"我要交某某课程的作业，我发几张图片给你\"**\n\n然后**按顺序**发送图片（先发的会在最终 PDF 的第一页，后发的在后面）。最后说：**\"已经上传完毕\"**\n\nAI 会先列出要提交的课程、作业标题、文件名，请你确认。确认后自动：\n1. 按你发送的顺序命名图片（001、002...）\n2. 合并为 PDF（按文件名排序，所以先发的在 PDF 前面）\n3. 重命名为 `学号_姓名.pdf`\n4. 提交到指定作业\n\n#### 下载课件\n\n对 AI 说：**\"帮我下载微积分的课件\"**\n\nAI 会列出课程的全部课件文件。你也可以更精确地指定：\n\n- **\"下载未读课件\"** — 只下载还没看过的课件\n- **\"下载概率论第 8 讲的课件\"** — 下载指定名称的文件\n- **\"把微积分的课件全部下载到 D 盘\"** — 批量下载到指定目录\n\nAI 会在下载前列出文件清单让你确认。\n\n#### 查看通知和作业详情\n\n对 AI 说：\n\n- **\"看看微积分最近有什么通知\"** — 查看指定课程的公告\n- **\"概率论 HW10 的作业内容是什么\"** — 查看作业的题目和附件\n- **\"上次交的物理作业老师批了吗\"** — 查看已批改作业的评语和分数\n- **\"看看英语课有什么讨论\"** — 查看课程讨论区\n\n#### 清理工作区\n\nAI 在查看待办时会主动提醒你工作区有缓存。这些缓存是下载的课件、合并 PDF 时的临时图片、调试日志等文件，不影响功能但会占用磁盘空间。\n\n只需说 **\"好\"** 或 **\"清理吧\"**，AI 会先列出待清理的文件让你确认，确认后自动删除。\n\n#### 其他\n\n如果有这里没列出的需求，直接告诉 AI 你想做什么即可。AI 会根据现有功能尽力帮你完成。\n\n#### 数据与安全\n\n本 Skill 会在本地存储以下数据：\n\n| 数据 | 位置 | 说明 |\n|------|------|------|\n| 登录凭据 | `credentials.json` | Windows DPAPI 加密（绑定当前用户账户，仅 Windows 支持） |\n| 浏览器登录状态 | `profiles/learn_profile/` | Chrome Profile 含 cookies，避免每次触发 2FA |\n| Session 令牌 | `sessions/learn_session.json` | JSESSIONID + CSRF Token |\n\n如需清除所有数据，对 AI 说 **\"重置所有数据\"**，AI 会清空以上目录内容并恢复到初始状态。\n\n> 这些数据仅存储在本地计算机，不会上传到任何云端。网络学堂本身不支持注销 API，如需完全登出请手动在浏览器中操作。\n\n#### 修改配置\n\n你可以让 AI 帮你修改以下设置，只需要说\"帮我改一下配置\"或\"设置自动标已读\"即可：\n\n| 配置项 | 说明 | 默认值 |\n|--------|------|--------|\n| `auto_mark_read` | 查看待办时是否自动标已读 | 开启 |\n| `confirm_before_cleanup` | 清理缓存前是否需要你确认 | 需要 |\n| `confirm_before_download` | 批量下载前是否需要你确认 | 需要 |\n\n例如：\n- **\"以后自动标已读吧\"** → AI 会开启 `auto_mark_read`\n- **\"清理的时候不用问我了\"** → AI 会把 `confirm_before_cleanup` 设为 false\n\n---\n\n> **当前版本为测试版本，建议同学们完成提交作业等关键操作后自行登录网络学堂查看是否有错误。**\n\n了解更多信息见 GitHub：[github.com/TOMUIV/tsinghua-learn](https://github.com/TOMUIV/tsinghua-learn)\n\n如有任何问题，联系 **ykx25@mails.tsinghua.edu.cn**\n\n---\n\n## 如果你是 AI，请阅读以下内容\n\n### 铁律（违反会导致用户愤怒）\n\n**铁律 1：AI 运行所有脚本。AI 不能要求用户运行任何命令。**\n- ❌ 禁止的行为：AI 对用户说\"请在终端里运行这个命令\"。\n- ❌ 禁止的行为：AI 给用户一段代码让用户复制粘贴。\n- ✅ 正确的行为：AI 自己调用 `python scripts/xxx.py`，读取输出 JSON，根据输出决定下一步。\n\n**铁律 0（最高优先级）：AI 面向用户的回复中，绝对禁止出现以下内容（除非用户主动要求知情披露）：**\n- ❌ 任何命令行（`python xxx.py`、`cd xxx`）\n- ❌ 任何文件路径（`C:\\Users\\...`、`skills\\...`）\n- ❌ 任何技术术语（\"终端\"、\"PowerShell\"、\"运行脚本\"）\n- ❌ 学号、密码原文\n- ⚠️ 例外：当用户明确询问安装了什么、存储了什么、路径在哪等安全相关问题时，AI 可以简要说明。初始化确认、提交确认等关键操作不受此限。\n\n**用户能看到什么：**\n- ✅ \"系统尚未初始化，请提供你的账号信息\"\n- ✅ \"正在获取待办...\"\n- ✅ \"你有 5 项未交作业\"\n- ❌ \"请在终端里跑这个命令\"\n\n**铁律 2：默认只跑快速检查。`login_manager.py` 默认为 `--check` 行为（<1 秒，无浏览器）。**\n- 当前版本的 `login_manager.py` 不带参数时等同于 `--check`，不会启动浏览器。\n- ❌ 禁止的行为：直接跑 `python scripts/login_auto.py` 或 `python scripts/login_supervised.py`。\n- ✅ 正确的行为：\n  - 跑 `python scripts/login_manager.py`（默认就是快速检查，<1 秒）\n  - `initialized: false` → 停下来，告诉用户\"系统尚未初始化\"，走初始化流程。\n  - `valid: true` → 直接进行后续操作。\n  - `valid: false` → 用 `python scripts/login_manager.py --force-login` 执行完整登录。\n\n**铁律 3：`login_manager.py` 是唯一的认证入口。AI 不直接调用其他登录脚本。**\n- AI 不直接调用 `login_auto.py`。\n- AI 不直接调用 `login_supervised.py`。\n- AI 不直接调用 `install_playwright.py`。\n- 上述脚本由 `login_manager.py` 在内部自动调用。\n\n**铁律 4：AI 不凭经验推断问题。所有判断基于脚本输出的 JSON。**\n\n**铁律 5：密码仅用于初始化收集，之后绝不在对话中重复展示。**\n- 初始化时 AI 询问密码，收集后立即传入脚本，不在后续对话中提及密码值。\n- AI 展示 `fields` 内容时，密码字段已脱敏（显示为 `xxx***`）。\n- ❌ 禁止的行为：AI 在初始化之外的对话中说密码原文。\n\n**铁律 6（提交确认）：提交作业必须先调用不带 --confirm 的上传预览，获得用户明确同意后才能加 --confirm 执行。**\n- ❌ 禁止的行为：AI 直接加 --confirm 提交，不展示预览。\n- ✅ 正确的行为：先不带 --confirm 调 upload 得到 preview JSON → 展示用户 → 同意后再加 --confirm\n\n---\n\n### 凭据字段速查\n\n| 字段 | CLI 参数 | 说明 |\n|------|---------|------|\n| 网络学堂登录账号 | `--learn-account` | 登录 learn.tsinghua.edu.cn 用的账号 |\n| 登录密码 | `--password` | 网络学堂登录密码，仅发送至 id.tsinghua.edu.cn（CAS）和 learn.tsinghua.edu.cn |\n| 学号 | `--username` | 学生证号 |\n| 姓名 | `--name` | 真实姓名 |\n\n> 学号和网络学堂登录账号可能相同也可能不同，分别问清楚。\n> ⚠️ 表中没有示例值。AI 不要编造学号或任何数字作为示例。\n\n---\n\n### 脚本速查\n\n| 脚本 | AI 什么时候需要调用它 | 速度 |\n|------|---------------------|------|\n| `login_manager.py` | 默认快速检查（<1s），有--init/--reset/--force-login/--verify | ⚡快/🐢慢 |\n| `todos_api.py` | 用户要求查看待办时 | ⚡快（2-5s） |\n| `ops.py` | 下载文件、上传作业、合并PDF、清理工作区 | ⚡快 |\n| `learn_api.py` | 查询课程列表、公告详情、作业内容 | ⚡快 |\n| `pdf_merge.py` | 图片合并PDF（ops.py内部调用） | ⚡快 |\n| `install_deps.py` | AI不直接调用（--verify内部自动调用） | 🐢慢 |\n| `install_playwright.py` | AI不直接调用 | 🐢慢 |\n| `login_auto.py` | AI不直接调用 | 🐢慢（有浏览器） |\n| `login_supervised.py` | AI不直接调用 | 🐢慢（有浏览器） |\n\n> ⚠️ 标🐢的脚本涉及浏览器/docker/下载，AI不应直接调用。它们由 `login_manager.py --force-login` 或 `--verify` 内部自动调用。\n\n---\n\n### 初始化流程\n\n用户说\"开始初始化\"时，AI 按以下步骤执行：\n\n**Step 1：向用户收集信息**\n```\nAI 依次问用户（不要一次全问，分步问）：\n第一部分（必须）：\n1. 网络学堂登录账号是什么？\n2. 登录密码是什么？\n3. 学号是什么？\n4. 姓名是什么？\n\n第二部分（选填，用户回答了必须信息后再问）：\n5. 是否需要自动标已读？（默认：是）\n6. 清理工作区前是否需要确认？（默认：是）\n7. 批量下载前是否需要确认？（默认：是）\n\n⚠️ AI 不编造示例值（不要写\"如 2025013187\"之类的数字）\n⚠️ AI 不显示学号或密码在对话中\n```\n\n**Step 2：AI 调用 `--init` 保存凭据**\n```\n1. AI 将凭据 JSON 写入临时文件（不包含在任何命令字符串中）\n2. 命令：python scripts/login_manager.py --init --cred-stdin < /tmp/cred_temp.json\n3. AI 立即删除临时文件\n输出：{\"status\":\"ok\", \"fields\":{\"learn_account\":\"...\", \"username\":\"...\", \"name\":\"...\"}}\n```\n> 使用临时文件 + stdin 重定向，密码只存在于临时文件中（随即删除），不出现在任何命令行参数或 echo 字符串中。\n\n**Step 3：AI 向用户展示 fields 内容，请用户确认**\n\n**Step 4：用户确认后，AI 调用 `--verify` 验证环境并尝试登录**\n```\n命令：python scripts/login_manager.py --verify\n输出：{\"python\":true, \"deps\":true, \"chromium\":true, \"login\":true, \"needs_2fa\":false}\n\nlogin=true       → Step 5\nneeds_2fa=true   → AI 告诉用户\"自动登录失败，请打开浏览器完成二次验证\"\n                   用户说\"好了\"后，AI 调用：python scripts/login_manager.py\n```\n\n**Step 5：AI 调用 `todos_api.py` 首次查看待办**\n```\n命令：python scripts/todos_api.py\n输出：JSON（含课程列表、待办数量等，submissions 日志自动初始化）\n```\n\n---\n\n### 日常查看待办流程\n\n任何时候用户说\"查看待办\"\"有什么作业\"\"看看通知\"时：\n\n**Step 1：AI 检查凭据和 session**\n\n跑默认命令（快速检查，<1 秒，无浏览器）：\n```\n命令：python scripts/login_manager.py\n输出：{\"initialized\": false/true, \"valid\": false/true, \"age_h\": ...}\n```\n\n根据输出决定下一步：\n\n① `initialized: false` → AI 停下来，告诉用户\"系统尚未初始化\"，走初始化流程\n② `initialized: true, valid: true` → 跳到 Step 2（获取待办）\n③ `initialized: true, valid: false` → AI 跑 `python scripts/login_manager.py --force-login`（会启动浏览器，约30-60秒）\n\n**Step 2：AI 获取待办数据**\n```\n命令：python scripts/todos_api.py\n输出：JSON 包含 courses、suggestions、graded_homeworks、cleanup_suggestion\n```\n\n**Step 3：AI 根据输出中的字段决定问用户什么**\n\n检查 `graded_homeworks` 字段：\n- 如果有内容 → AI 告诉用户\"老师批改了你的作业：[课程名][标题]，评语：[comment]，得分：[score]\"\n\n检查 `submissions_tracked` 字段：\n- 如果 > 0 → AI 告诉用户\"有 N 个已提交的作业等待老师批改，批改后我会通知你\"\n\n检查 `suggestions` 数组：\n- 如果包含 \"运行 --mark-read\" → AI 问用户\"需要把未读公告和课件标为已读吗？\"\n- 如果包含 \"运行 --cleanup-preview\" → AI 问用户\"需要看看工作区有哪些垃圾文件吗？\"\n\n检查 `suggestions` 和 `graded_homeworks` 都不存在时：\n- AI 告诉用户\"目前没有新的待办事项\"\n\n**Step 4：用户同意后，AI 自己运行对应命令**\n```\n标已读：        python scripts/todos_api.py --mark-read\n清理预览：      python scripts/ops.py --action cleanup --dry-run\n执行清理：      python scripts/ops.py --action cleanup\n```\n\n---\n\n### 提交作业流程\n\n用户说\"我要交作业\"或发来文件/图片时：\n\n**Step 1：AI 确认要交到哪个作业**\n```\n命令：python scripts/learn_api.py --action courses\n输出：课程列表（AI 找到用户说的课程，记录 wlkcid）\n\n命令：python scripts/learn_api.py --action homeworks --course <wlkcid>\n输出：作业列表（AI 找到用户说的作业，记录 xszyid）\n```\n\n**Step 2：AI 处理文件**\n\n情况 A — 用户直接发送了 PDF 文件：\n```\nAI 将文件保存到 uploads/，进入 Step 3\n```\n\n情况 B — 用户发送多张图片（如 QQ 传图）：\n```\nAI 每收到一张图，保存到 uploads/，按顺序命名为 001.jpg、002.jpg...\n用户说\"已经上传完毕\"后：\n命令：python scripts/ops.py --action pdf-merge --input-dir uploads/\n输出：merged.pdf\nAI 删除 uploads/ 下的所有图片文件\n进入 Step 3\n```\n\n**Step 3：AI 展示确认信息，获得同意后提交**\n\n```\n先执行未确认的提交（展示详情，不实际提交）：\n命令：python scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\n输出：{\"status\":\"pending\", \"requiring\":\"confirmation\", \"details\":{...}}\n\nAI 必须向用户展示：\n- 课程名称\n- 作业标题\n- 待提交的文件名\n- 提交账号（姓名+学号脱敏）\n\n例：\"准备提交 微积分(2) 的 第四次书面作业，文件：2025013187_张三.pdf。确认吗？\"\n\n用户明确说\"确认\"/\"好\"/\"提交\"后，AI 添加 --confirm 重新执行：\n命令：python scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path> --confirm\n输出：{\"status\":\"ok\"}\n```\n\n**Step 4：AI 通知用户提交成功，清理临时文件**\n\n---\n\n### 下载课件流程\n\n用户说\"帮我下载课件\"时：\n\n**Step 1：AI 获取课程列表**\n```\n命令：python scripts/learn_api.py --action courses\n输出：课程列表（AI 找到用户说的课程，记录 wlkcid）\n```\n\n**Step 2：AI 下载文件**\n```\n命令：python scripts/ops.py --action download-all --course <wlkcid> --save-dir <目录>\n输出：下载结果\n```\n如果用户指定了具体文件名或模式，在 `--pattern` 参数中指定。\n\n---\n\n### 常见问题处理\n\n**Session 过期**\n```\nAI 不分析 session 文件。不检查时间戳。不自己调 login_auto.py。\n只做一件事：\n命令：python scripts/login_manager.py\n输出：session 有效则返回，无效则自动续期\n```\n\n**需要二次验证（2FA）**\n```\nlogin_manager.py 或 --verify 返回 needs_2fa=true 时：\nStep 1：AI 告诉用户\"自动登录失败，请打开浏览器完成二次验证\"\nStep 2：用户说\"好了\"后，AI 调用：\n命令：python scripts/login_manager.py\n输出：验证 session 是否已建立\n```\n\n**凭据未初始化**\n```\nlogin_manager.py --check 返回 initialized=false 时：\nAI 告诉用户\"系统尚未初始化\"，然后按初始化流程的 Step 1 开始收集信息\n```\n\n### 脚本概览\n\n| 脚本 | 职责 | 什么时候用 |\n|------|------|-----------|\n| `login_manager.py` | 认证网关 | 任何操作前确保 session；初始化；验证环境 |\n| `todos_api.py` | 日常管理 | 用户问待办、标已读、清理 |\n| `ops.py` | 文件操作 | 下载、上传、合并 PDF、清理工作区 |\n| `learn_api.py` | 数据查询 | 查课程列表、公告详情、作业内容 |\n| `pdf_merge.py` | 图片→PDF | 用户发图片要交作业时 |\n| `install_deps.py` | 依赖安装 | --verify 会自动调用，你不直接碰 |\n| `_config.py` | 配置读取 | 你不直接碰 |\n| `login_auto.py` | 无头登录 | 你不直接碰，login_manager 内部调用 |\n| `login_supervised.py` | 2FA 登录 | 你不直接碰，login_manager 内部调用 |\n| `install_playwright.py` | Chromium 安装 | --verify 会自动调用，你不直接碰 |\n\n### 日常运行流程\n\n任何时候用户问\"查看待办\"\"有什么作业\"等：\n\n```\n① 先确保 session:\n   python scripts/login_manager.py\n   ← 如果 session 过期会自动续期\n\n② 获取待办:\n   python scripts/todos_api.py\n   ← 得到 JSON {\"courses\": [...], \"total_unread\": N, \"suggestions\": [...], ...}\n\n③ 解析输出，主动问用户:\n   - suggestions 里有 \"运行 --mark-read\" → \"需要帮你把未读公告和课件标为已读吗？\"\n   - cleanup_suggestion 不为空 → \"工作区有垃圾文件，需要看看吗？\"\n   - graded_homeworks 有内容 → \"老师批了你的作业，评语是...\"\n\n④ 用户确认后，自己跑对应命令:\n   - 标已读: python scripts/todos_api.py --mark-read\n   - 清理预览: python scripts/ops.py --action cleanup --dry-run\n   - 执行清理: python scripts/ops.py --action cleanup\n```\n\n### 详细 CLI 命令\n\n#### login_manager.py\n\n```bash\npython scripts/login_manager.py                                # 确保 session\npython scripts/login_manager.py --check                        # 检查状态\npython scripts/login_manager.py --init --learn-account=... --password=... --username=... --name=...\npython scripts/login_manager.py --reset                        # 清空所有数据\npython scripts/login_manager.py --verify                       # 验证环境（依赖+Chromium+登录）\n```\n\n退出码: 0=有效, 1=错误, 2=需人工 2FA, 3=未初始化\n\n#### learn_api.py\n\n```bash\npython scripts/learn_api.py --action courses                               # 课程列表\npython scripts/learn_api.py --action announcements --course <wlkcid>       # 公告列表\npython scripts/learn_api.py --action announcement-detail --course <wlkcid> --id <ggid>  # 公告详细内容\npython scripts/learn_api.py --action files --course <wlkcid>               # 课件列表\npython scripts/learn_api.py --action homeworks --course <wlkcid>           # 作业列表\npython scripts/learn_api.py --action homework-full --course <wlkcid> --id <zyid> --xszyid <xszyid>  # 作业完整信息（含评语）\npython scripts/learn_api.py --action discussions --course <wlkcid>         # 讨论列表\npython scripts/learn_api.py --action questionnaires --course <wlkcid>      # 问卷列表\npython scripts/learn_api.py --action aggregated --course <wlkcid>          # 聚合查询\npython scripts/learn_api.py --action mark-all-announcements-read --course <wlkcid>\npython scripts/learn_api.py --action mark-all-files-read --course <wlkcid>\n```\n\n#### ops.py\n\n```bash\npython scripts/ops.py --action list-files --course <wlkcid> [--pattern \"*.pdf\"]\npython scripts/ops.py --action download --course <wlkcid> --wjid <wjid>\npython scripts/ops.py --action download --course <wlkcid> --wjid-list \"id1,id2,...\"\npython scripts/ops.py --action download-all --course <wlkcid> [--pattern \"*.pdf\"]\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path> --confirm  # 确认后实际提交\npython scripts/ops.py --action pdf-merge --input-dir <目录> [--output <路径>]\npython scripts/ops.py --action move-in --file <source_path>\npython scripts/ops.py --action cleanup --dry-run\npython scripts/ops.py --action cleanup\n```\n\n#### todos_api.py\n\n```bash\npython scripts/todos_api.py                           # 代办汇总\npython scripts/todos_api.py --mark-read               # + 标记可读项已读\npython scripts/todos_api.py --cleanup-preview         # + 清理预览\npython scripts/todos_api.py --cleanup                 # + 执行清理\n```\n\n**输出解析**：\n- `suggestions` — AI 读取后应主动询问用户：\"需要帮你把未读公告和课件标为已读吗？\"\"需要帮你看看工作区有哪些垃圾文件吗？\"\n- `graded_homeworks` — 老师新批改的作业及其评语/分数\n- `new_submissions_logged` — 本次新增的已提交未批改作业数量\n\n#### pdf_merge.py\n\n```bash\npython scripts/pdf_merge.py --input-dir <目录> [--output <路径>]\n```\n\n支持: jpg, jpeg, png, webp, bmp, heic, heif\n\n### 常见任务工作流\n\n#### 图片作业提交（QQ 发送图片）\n\n```\n1. 用户每发一张图 → AI 保存到 uploads/，按顺序命名为 001.jpg、002.jpg...\n2. 用户说\"发送完毕\"\n3. ops.py --action pdf-merge --input-dir uploads/ → merged.pdf\n4. 删除 uploads/ 下所有图片（保留 merged.pdf）\n5. ops.py --action upload --course ... --xszyid ... --file uploads/merged.pdf --confirm\n   → 自动重命名为 学号_姓名.pdf\n6. 删除 uploads/merged.pdf\n```\n\n#### 下载课件\n\n```\nlearn_api.py --action courses                    # 获取课程列表+ID\nops.py --action download-all --course <wlkcid>   # 下载全部\n```\n\n### 提交日志机制\n\n文件: `submissions/submissions_log.json`\n\n```\ntodos_api.py 每次运行时:\n  1. sync_submissions_log() → 扫描所有已交未批改作业，新出现的追加到日志（已存在则跳过）\n  2. check_graded_submissions() → 遍历日志，检查是否有作业已被批改\n     有 → 获取评语/分数，从日志中移除，在输出中加入 graded_homeworks\n```\n\n### 配置 (config.json)\n\n```json\n{\n  \"auto_mark_read\": false,\n  \"confirm_before_cleanup\": true,\n  \"confirm_before_download\": true\n}\n```\n\n| 选项 | 说明 | 默认 |\n|------|------|------|\n| `auto_mark_read` | 运行 `todos_api.py` 时自动标已读 | `false` |\n| `confirm_before_cleanup` | 清理前是否需用户确认 | `true` |\n| `confirm_before_download` | 批量下载前是否需用户确认 | `true` |\n\n### 未读判断标准\n\n| 模块 | API 字段 | 过滤值 |\n|------|----------|--------|\n| 作业 | `zt` | `\"未交\"` |\n| 公告 | `sfyd` | `\"否\"` |\n| 课件 | `isNew` | `\"1\"` |\n| 讨论 | `htsl` | `>0` |\n\n### 注意事项与不可做清单\n\n#### 不可做\n\n- **不要硬编码课程 ID、学期、路径、姓名**\n- **不要在脚本中使用 `input()` 或 `getpass()`** — AI 运行脚本，不能阻塞\n- **不要直接修改 `credentials.json`** — 通过 `login_manager.py --init` 操作\n- **不要创建新脚本除非绝对必要**\n- **不要在 `config.json` 之外的地方存储用户信息**\n- **不要告诉用户命令行操作** — 用户只回答\"要/不要\"，AI 代为执行\n\n#### 注意事项\n\n- **文件名编码**：服务器将 UTF-8 的中文文件名以 latin-1 编码发送，`download_file()` 已做修复\n- **作业提交端点**：真实端点是 `/b/wlxt/kczy/zy/student/tjzy`，不是 `uploadFile/saveXszj`\n- **公告标已读**：端点 GET `/f/wlxt/kcgg/wlkc_ggb/student/beforeViewXs`，不是 `saveYd`\n- **公告内容**：直接从列表 API 的 `ggnr` base64 字段解码\n- **学期自动检测**：每次建立 session 后调用 `get_current_semester()`\n- **重置**：`login_manager.py --reset` 保留所有文件结构，只清空内容\n- **已提交的作业无法撤回**：网络学堂不提供撤回已提交作业的 API\n\nFile v1.0.13:_meta.json\n\n{\n  \"ownerId\": \"kn73vfhykr10snz07772njvdmx84mcb6\",\n  \"slug\": \"tsinghua-learn\",\n  \"version\": \"1.0.13\",\n  \"publishedAt\": 1777772431701\n}\n\nFile v1.0.13:AGENTS.md\n\n# tsinghua-learn — AGENTS.md\n\n## 架构\n\n6 个脚本：\n\n| 脚本 | 职责 |\n|------|------|\n| `_config.py` | 路径/配置/凭证、DPAPI 解密 |\n| `login_manager.py` | 中央认证网关（默认 fast check，`--force-login` 完整登录） |\n| `learn_api.py` | HTTP API（课程/公告/课件/作业/讨论/问卷/聚合查询） |\n| `ops.py` | 文件操作（下载/批量下载/上传/移入/清理） |\n| `todos_api.py` | 日常管理（代办汇总/标已读/清理，JSON 给 AI） |\n| `pdf_merge.py` | 图片→PDF |\n\n登录脚本不直接调用：\n| `login_auto.py` | headless 自动登录 |\n| `login_supervised.py` | 2FA 登录 |\n\n## 核心命令\n\n```bash\npython scripts/login_manager.py                        # 快速检查 session (<1s)\npython scripts/login_manager.py --init                 # 初始化凭据\npython scripts/login_manager.py --verify               # 验证环境+登录\npython scripts/login_manager.py --reset                # 清空所有数据\npython scripts/login_manager.py --force-login          # 强制完整登录\n\npython scripts/todos_api.py                            # 代办汇总\npython scripts/todos_api.py --mark-read                # + 标已读\npython scripts/todos_api.py --cleanup                  # + 清理\n\npython scripts/ops.py --action list-files --course <wlkcid>\npython scripts/ops.py --action download --course <wlkcid> --wjid <wjid>\npython scripts/ops.py --action download-all --course <wlkcid>\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\npython scripts/ops.py --action pdf-merge --input-dir <目录>\npython scripts/ops.py --action cleanup --dry-run\npython scripts/ops.py --action cleanup\n```\n\n## 关键约束\n\n- paths/creds 全走 `_config.py` getter\n- `credentials.json` DPAPI 加密\n- 学期从 API 自动检测\n- 课程从 API 动态获取\n- 不创建新脚本\n- 脚本不阻塞（`input()`/`getpass()` 禁用）\n- 提交作业自动重命名：`学号_姓名.ext`\n\nFile v1.0.13:config.json\n\n{\r\n    \"auto_mark_read\": true,\n  \"confirm_before_cleanup\": true,\r\n  \"confirm_before_download\": true\r\n}\n\nFile v1.0.13:credentials.json\n\n{\r\n  \"username\": \"\",\r\n  \"password\": \"\",\r\n  \"student_id\": \"\",\r\n  \"name\": \"\"\r\n}\n\nArchive v1.0.12: 15 files, 39239 bytes\n\nFiles: AGENTS.md (1975b), config.json (102b), credentials.json (77b), scripts/_config.py (6203b), scripts/install_deps.py (2207b), scripts/install_playwright.py (1041b), scripts/learn_api.py (28555b), scripts/login_auto.py (5076b), scripts/login_manager.py (11918b), scripts/login_supervised.py (5191b), scripts/ops.py (13323b), scripts/pdf_merge.py (2569b), scripts/todos_api.py (6365b), SKILL.md (24837b), _meta.json (134b)\n\nFile v1.0.12:SKILL.md\n\n---\nname: tsinghua-learn\ndescription: 清华网络学堂（learn.tsinghua.edu.cn）自动化。要求用户提供网络学堂登录密码（本地 DPAPI 加密存储），自动登录、查看待办、下载课件、提交作业、批量标记已读。当需要操作网络学堂完成日常学习任务时使用。\nmetadata:\n  openclaw:\n    requires:\n      env:\n        - CAS_PASSWORD\n    primaryEnv: CAS_PASSWORD\n    os:\n      - windows\n---\n\n# tsinghua-learn skill\n\n清华网络学堂（learn.tsinghua.edu.cn）自动化 Skill。\n\n---\n\n## 如果你是人类，请阅读以下内容\n\n### 前置要求\n\n使用前请确保已安装 **Python 3.10+** 或 **Conda**（推荐 Python 3.11 或 3.12）。建议安装 **7-Zip** 并添加到环境变量 PATH 中，方便后续处理压缩文件。\n\n初始化时会自动安装 Python 依赖（requests、playwright、pillow 等）以及 Chromium 浏览器（~150MB），无需手动操作。\n\n如果没有安装上述环境，请先安装后再对 AI 说 **\"开始初始化\"**。\n\n### 如何与 AI 沟通\n\n你只需要像聊天一样告诉 AI 你想做什么，AI 会帮你完成所有操作。\n\n#### 初始化\n\n对 AI 说：**\"开始初始化\"**\n\nAI 会依次：\n1. 询问你的网络学堂登录账号、密码、学号、姓名\n2. 检查 Python 环境并自动安装所需依赖\n3. 安装 Chromium 浏览器\n4. 尝试登录网络学堂\n5. 登录成功后询问你的偏好设置（是否自动标已读等）\n6. 完成后即可正常使用\n\n> **四个字段说明：**\n> - **网络学堂登录账号**（`--learn-account`）— 用于登录 learn.tsinghua.edu.cn 的账号，可能和学号不同\n> - **登录密码**（`--password`）— 网络学堂登录密码，仅发送至 id.tsinghua.edu.cn（CAS）和 learn.tsinghua.edu.cn\n> - **学号**（`--username`）— 你的学生证号，如 2025013187\n> - **姓名**（`--name`）— 你的真实姓名\n\n#### 查看待办\n\n对 AI 说：**\"查看待办\"** 或 **\"看看有什么作业\"**\n\nAI 会列出所有课程的最新动态：\n- 未交作业及截止日期\n- 未读公告和未浏览课件\n- 已提交但老师还没批的作业（老师批改后会自动汇报评语和分数）\n- 工作区缓存大小\n\nAI 还会主动问你是否需要标已读、清理缓存。\n\n> 你也可以设置每日自动查看：将 `todos_api.py` 配置为定时任务即可。\n\n#### 提交作业\n\n**方式一：直接发送 PDF 文件**\n\n对 AI 说：**\"我要交某某课程的作业\"**，然后发送 PDF 文件即可。\n\n**方式二：发送多张图片合并为 PDF**\n\n先说：**\"我要交某某课程的作业，我发几张图片给你\"**\n\n然后**按顺序**发送图片（先发的会在最终 PDF 的第一页，后发的在后面）。最后说：**\"已经上传完毕\"**\n\nAI 会先列出要提交的课程、作业标题、文件名，请你确认。确认后自动：\n1. 按你发送的顺序命名图片（001、002...）\n2. 合并为 PDF（按文件名排序，所以先发的在 PDF 前面）\n3. 重命名为 `学号_姓名.pdf`\n4. 提交到指定作业\n\n#### 下载课件\n\n对 AI 说：**\"帮我下载微积分的课件\"**\n\nAI 会列出课程的全部课件文件。你也可以更精确地指定：\n\n- **\"下载未读课件\"** — 只下载还没看过的课件\n- **\"下载概率论第 8 讲的课件\"** — 下载指定名称的文件\n- **\"把微积分的课件全部下载到 D 盘\"** — 批量下载到指定目录\n\nAI 会在下载前列出文件清单让你确认。\n\n#### 查看通知和作业详情\n\n对 AI 说：\n\n- **\"看看微积分最近有什么通知\"** — 查看指定课程的公告\n- **\"概率论 HW10 的作业内容是什么\"** — 查看作业的题目和附件\n- **\"上次交的物理作业老师批了吗\"** — 查看已批改作业的评语和分数\n- **\"看看英语课有什么讨论\"** — 查看课程讨论区\n\n#### 清理工作区\n\nAI 在查看待办时会主动提醒你工作区有缓存。这些缓存是下载的课件、合并 PDF 时的临时图片、调试日志等文件，不影响功能但会占用磁盘空间。\n\n只需说 **\"好\"** 或 **\"清理吧\"**，AI 会先列出待清理的文件让你确认，确认后自动删除。\n\n#### 其他\n\n如果有这里没列出的需求，直接告诉 AI 你想做什么即可。AI 会根据现有功能尽力帮你完成。\n\n#### 数据与安全\n\n本 Skill 会在本地存储以下数据：\n\n| 数据 | 位置 | 说明 |\n|------|------|------|\n| 登录凭据 | `credentials.json` | Windows DPAPI 加密（绑定当前用户账户，仅 Windows 支持） |\n| 浏览器登录状态 | `profiles/learn_profile/` | Chrome Profile 含 cookies，避免每次触发 2FA |\n| Session 令牌 | `sessions/learn_session.json` | JSESSIONID + CSRF Token |\n\n如需清除所有数据，对 AI 说 **\"重置所有数据\"**，AI 会清空以上目录内容并恢复到初始状态。\n\n> 这些数据仅存储在本地计算机，不会上传到任何云端。网络学堂本身不支持注销 API，如需完全登出请手动在浏览器中操作。\n\n#### 修改配置\n\n你可以让 AI 帮你修改以下设置，只需要说\"帮我改一下配置\"或\"设置自动标已读\"即可：\n\n| 配置项 | 说明 | 默认值 |\n|--------|------|--------|\n| `auto_mark_read` | 查看待办时是否自动标已读 | 开启 |\n| `confirm_before_cleanup` | 清理缓存前是否需要你确认 | 需要 |\n| `confirm_before_download` | 批量下载前是否需要你确认 | 需要 |\n\n例如：\n- **\"以后自动标已读吧\"** → AI 会开启 `auto_mark_read`\n- **\"清理的时候不用问我了\"** → AI 会把 `confirm_before_cleanup` 设为 false\n\n---\n\n> **当前版本为测试版本，建议同学们完成提交作业等关键操作后自行登录网络学堂查看是否有错误。**\n\n了解更多信息见 GitHub：[github.com/TOMUIV/tsinghua-learn](https://github.com/TOMUIV/tsinghua-learn)\n\n如有任何问题，联系 **ykx25@mails.tsinghua.edu.cn**\n\n---\n\n## 如果你是 AI，请阅读以下内容\n\n### 铁律（违反会导致用户愤怒）\n\n**铁律 1：AI 运行所有脚本。AI 不能要求用户运行任何命令。**\n- ❌ 禁止的行为：AI 对用户说\"请在终端里运行这个命令\"。\n- ❌ 禁止的行为：AI 给用户一段代码让用户复制粘贴。\n- ✅ 正确的行为：AI 自己调用 `python scripts/xxx.py`，读取输出 JSON，根据输出决定下一步。\n\n**铁律 0（最高优先级）：AI 面向用户的回复中，绝对禁止出现以下内容（除非用户主动要求知情披露）：**\n- ❌ 任何命令行（`python xxx.py`、`cd xxx`）\n- ❌ 任何文件路径（`C:\\Users\\...`、`skills\\...`）\n- ❌ 任何技术术语（\"终端\"、\"PowerShell\"、\"运行脚本\"）\n- ❌ 学号、密码原文\n- ⚠️ 例外：当用户明确询问安装了什么、存储了什么、路径在哪等安全相关问题时，AI 可以简要说明。初始化确认、提交确认等关键操作不受此限。\n\n**用户能看到什么：**\n- ✅ \"系统尚未初始化，请提供你的账号信息\"\n- ✅ \"正在获取待办...\"\n- ✅ \"你有 5 项未交作业\"\n- ❌ \"请在终端里跑这个命令\"\n\n**铁律 2：默认只跑快速检查。`login_manager.py` 默认为 `--check` 行为（<1 秒，无浏览器）。**\n- 当前版本的 `login_manager.py` 不带参数时等同于 `--check`，不会启动浏览器。\n- ❌ 禁止的行为：直接跑 `python scripts/login_auto.py` 或 `python scripts/login_supervised.py`。\n- ✅ 正确的行为：\n  - 跑 `python scripts/login_manager.py`（默认就是快速检查，<1 秒）\n  - `initialized: false` → 停下来，告诉用户\"系统尚未初始化\"，走初始化流程。\n  - `valid: true` → 直接进行后续操作。\n  - `valid: false` → 用 `python scripts/login_manager.py --force-login` 执行完整登录。\n\n**铁律 3：`login_manager.py` 是唯一的认证入口。AI 不直接调用其他登录脚本。**\n- AI 不直接调用 `login_auto.py`。\n- AI 不直接调用 `login_supervised.py`。\n- AI 不直接调用 `install_playwright.py`。\n- 上述脚本由 `login_manager.py` 在内部自动调用。\n\n**铁律 4：AI 不凭经验推断问题。所有判断基于脚本输出的 JSON。**\n\n**铁律 5：密码仅用于初始化收集，之后绝不在对话中重复展示。**\n- 初始化时 AI 询问密码，收集后立即传入脚本，不在后续对话中提及密码值。\n- AI 展示 `fields` 内容时，密码字段已脱敏（显示为 `xxx***`）。\n- ❌ 禁止的行为：AI 在初始化之外的对话中说密码原文。\n\n**铁律 6（提交确认）：提交作业必须先调用不带 --confirm 的上传预览，获得用户明确同意后才能加 --confirm 执行。**\n- ❌ 禁止的行为：AI 直接加 --confirm 提交，不展示预览。\n- ✅ 正确的行为：先不带 --confirm 调 upload 得到 preview JSON → 展示用户 → 同意后再加 --confirm\n\n---\n\n### 凭据字段速查\n\n| 字段 | CLI 参数 | 说明 |\n|------|---------|------|\n| 网络学堂登录账号 | `--learn-account` | 登录 learn.tsinghua.edu.cn 用的账号 |\n| 登录密码 | `--password` | 网络学堂登录密码，仅发送至 id.tsinghua.edu.cn（CAS）和 learn.tsinghua.edu.cn |\n| 学号 | `--username` | 学生证号 |\n| 姓名 | `--name` | 真实姓名 |\n\n> 学号和网络学堂登录账号可能相同也可能不同，分别问清楚。\n> ⚠️ 表中没有示例值。AI 不要编造学号或任何数字作为示例。\n\n---\n\n### 脚本速查\n\n| 脚本 | AI 什么时候需要调用它 | 速度 |\n|------|---------------------|------|\n| `login_manager.py` | 默认快速检查（<1s），有--init/--reset/--force-login/--verify | ⚡快/🐢慢 |\n| `todos_api.py` | 用户要求查看待办时 | ⚡快（2-5s） |\n| `ops.py` | 下载文件、上传作业、合并PDF、清理工作区 | ⚡快 |\n| `learn_api.py` | 查询课程列表、公告详情、作业内容 | ⚡快 |\n| `pdf_merge.py` | 图片合并PDF（ops.py内部调用） | ⚡快 |\n| `install_deps.py` | AI不直接调用（--verify内部自动调用） | 🐢慢 |\n| `install_playwright.py` | AI不直接调用 | 🐢慢 |\n| `login_auto.py` | AI不直接调用 | 🐢慢（有浏览器） |\n| `login_supervised.py` | AI不直接调用 | 🐢慢（有浏览器） |\n\n> ⚠️ 标🐢的脚本涉及浏览器/docker/下载，AI不应直接调用。它们由 `login_manager.py --force-login` 或 `--verify` 内部自动调用。\n\n---\n\n### 初始化流程\n\n用户说\"开始初始化\"时，AI 按以下步骤执行：\n\n**Step 1：向用户收集信息**\n```\nAI 依次问用户（不要一次全问，分步问）：\n第一部分（必须）：\n1. 网络学堂登录账号是什么？\n2. 登录密码是什么？\n3. 学号是什么？\n4. 姓名是什么？\n\n第二部分（选填，用户回答了必须信息后再问）：\n5. 是否需要自动标已读？（默认：是）\n6. 清理工作区前是否需要确认？（默认：是）\n7. 批量下载前是否需要确认？（默认：是）\n\n⚠️ AI 不编造示例值（不要写\"如 2025013187\"之类的数字）\n⚠️ AI 不显示学号或密码在对话中\n```\n\n**Step 2：AI 调用 `--init` 保存凭据**\n```\n1. AI 将凭据 JSON 写入临时文件（不包含在任何命令字符串中）\n2. 命令：python scripts/login_manager.py --init --cred-stdin < /tmp/cred_temp.json\n3. AI 立即删除临时文件\n输出：{\"status\":\"ok\", \"fields\":{\"learn_account\":\"...\", \"username\":\"...\", \"name\":\"...\"}}\n```\n> 使用临时文件 + stdin 重定向，密码只存在于临时文件中（随即删除），不出现在任何命令行参数或 echo 字符串中。\n\n**Step 3：AI 向用户展示 fields 内容，请用户确认**\n\n**Step 4：用户确认后，AI 调用 `--verify` 验证环境并尝试登录**\n```\n命令：python scripts/login_manager.py --verify\n输出：{\"python\":true, \"deps\":true, \"chromium\":true, \"login\":true, \"needs_2fa\":false}\n\nlogin=true       → Step 5\nneeds_2fa=true   → AI 告诉用户\"自动登录失败，请打开浏览器完成二次验证\"\n                   用户说\"好了\"后，AI 调用：python scripts/login_manager.py\n```\n\n**Step 5：AI 调用 `todos_api.py` 首次查看待办**\n```\n命令：python scripts/todos_api.py\n输出：JSON（含课程列表、待办数量等，submissions 日志自动初始化）\n```\n\n---\n\n### 日常查看待办流程\n\n任何时候用户说\"查看待办\"\"有什么作业\"\"看看通知\"时：\n\n**Step 1：AI 检查凭据和 session**\n\n跑默认命令（快速检查，<1 秒，无浏览器）：\n```\n命令：python scripts/login_manager.py\n输出：{\"initialized\": false/true, \"valid\": false/true, \"age_h\": ...}\n```\n\n根据输出决定下一步：\n\n① `initialized: false` → AI 停下来，告诉用户\"系统尚未初始化\"，走初始化流程\n② `initialized: true, valid: true` → 跳到 Step 2（获取待办）\n③ `initialized: true, valid: false` → AI 跑 `python scripts/login_manager.py --force-login`（会启动浏览器，约30-60秒）\n\n**Step 2：AI 获取待办数据**\n```\n命令：python scripts/todos_api.py\n输出：JSON 包含 courses、suggestions、graded_homeworks、cleanup_suggestion\n```\n\n**Step 3：AI 根据输出中的字段决定问用户什么**\n\n检查 `graded_homeworks` 字段：\n- 如果有内容 → AI 告诉用户\"老师批改了你的作业：[课程名][标题]，评语：[comment]，得分：[score]\"\n\n检查 `submissions_tracked` 字段：\n- 如果 > 0 → AI 告诉用户\"有 N 个已提交的作业等待老师批改，批改后我会通知你\"\n\n检查 `suggestions` 数组：\n- 如果包含 \"运行 --mark-read\" → AI 问用户\"需要把未读公告和课件标为已读吗？\"\n- 如果包含 \"运行 --cleanup-preview\" → AI 问用户\"需要看看工作区有哪些垃圾文件吗？\"\n\n检查 `suggestions` 和 `graded_homeworks` 都不存在时：\n- AI 告诉用户\"目前没有新的待办事项\"\n\n**Step 4：用户同意后，AI 自己运行对应命令**\n```\n标已读：        python scripts/todos_api.py --mark-read\n清理预览：      python scripts/ops.py --action cleanup --dry-run\n执行清理：      python scripts/ops.py --action cleanup\n```\n\n---\n\n### 提交作业流程\n\n用户说\"我要交作业\"或发来文件/图片时：\n\n**Step 1：AI 确认要交到哪个作业**\n```\n命令：python scripts/learn_api.py --action courses\n输出：课程列表（AI 找到用户说的课程，记录 wlkcid）\n\n命令：python scripts/learn_api.py --action homeworks --course <wlkcid>\n输出：作业列表（AI 找到用户说的作业，记录 xszyid）\n```\n\n**Step 2：AI 处理文件**\n\n情况 A — 用户直接发送了 PDF 文件：\n```\nAI 将文件保存到 uploads/，进入 Step 3\n```\n\n情况 B — 用户发送多张图片（如 QQ 传图）：\n```\nAI 每收到一张图，保存到 uploads/，按顺序命名为 001.jpg、002.jpg...\n用户说\"已经上传完毕\"后：\n命令：python scripts/ops.py --action pdf-merge --input-dir uploads/\n输出：merged.pdf\nAI 删除 uploads/ 下的所有图片文件\n进入 Step 3\n```\n\n**Step 3：AI 展示确认信息，获得同意后提交**\n\n```\n先执行未确认的提交（展示详情，不实际提交）：\n命令：python scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\n输出：{\"status\":\"pending\", \"requiring\":\"confirmation\", \"details\":{...}}\n\nAI 必须向用户展示：\n- 课程名称\n- 作业标题\n- 待提交的文件名\n- 提交账号（姓名+学号脱敏）\n\n例：\"准备提交 微积分(2) 的 第四次书面作业，文件：2025013187_张三.pdf。确认吗？\"\n\n用户明确说\"确认\"/\"好\"/\"提交\"后，AI 添加 --confirm 重新执行：\n命令：python scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path> --confirm\n输出：{\"status\":\"ok\"}\n```\n\n**Step 4：AI 通知用户提交成功，清理临时文件**\n\n---\n\n### 下载课件流程\n\n用户说\"帮我下载课件\"时：\n\n**Step 1：AI 获取课程列表**\n```\n命令：python scripts/learn_api.py --action courses\n输出：课程列表（AI 找到用户说的课程，记录 wlkcid）\n```\n\n**Step 2：AI 下载文件**\n```\n命令：python scripts/ops.py --action download-all --course <wlkcid> --save-dir <目录>\n输出：下载结果\n```\n如果用户指定了具体文件名或模式，在 `--pattern` 参数中指定。\n\n---\n\n### 常见问题处理\n\n**Session 过期**\n```\nAI 不分析 session 文件。不检查时间戳。不自己调 login_auto.py。\n只做一件事：\n命令：python scripts/login_manager.py\n输出：session 有效则返回，无效则自动续期\n```\n\n**需要二次验证（2FA）**\n```\nlogin_manager.py 或 --verify 返回 needs_2fa=true 时：\nStep 1：AI 告诉用户\"自动登录失败，请打开浏览器完成二次验证\"\nStep 2：用户说\"好了\"后，AI 调用：\n命令：python scripts/login_manager.py\n输出：验证 session 是否已建立\n```\n\n**凭据未初始化**\n```\nlogin_manager.py --check 返回 initialized=false 时：\nAI 告诉用户\"系统尚未初始化\"，然后按初始化流程的 Step 1 开始收集信息\n```\n\n### 脚本概览\n\n| 脚本 | 职责 | 什么时候用 |\n|------|------|-----------|\n| `login_manager.py` | 认证网关 | 任何操作前确保 session；初始化；验证环境 |\n| `todos_api.py` | 日常管理 | 用户问待办、标已读、清理 |\n| `ops.py` | 文件操作 | 下载、上传、合并 PDF、清理工作区 |\n| `learn_api.py` | 数据查询 | 查课程列表、公告详情、作业内容 |\n| `pdf_merge.py` | 图片→PDF | 用户发图片要交作业时 |\n| `install_deps.py` | 依赖安装 | --verify 会自动调用，你不直接碰 |\n| `_config.py` | 配置读取 | 你不直接碰 |\n| `login_auto.py` | 无头登录 | 你不直接碰，login_manager 内部调用 |\n| `login_supervised.py` | 2FA 登录 | 你不直接碰，login_manager 内部调用 |\n| `install_playwright.py` | Chromium 安装 | --verify 会自动调用，你不直接碰 |\n\n### 日常运行流程\n\n任何时候用户问\"查看待办\"\"有什么作业\"等：\n\n```\n① 先确保 session:\n   python scripts/login_manager.py\n   ← 如果 session 过期会自动续期\n\n② 获取待办:\n   python scripts/todos_api.py\n   ← 得到 JSON {\"courses\": [...], \"total_unread\": N, \"suggestions\": [...], ...}\n\n③ 解析输出，主动问用户:\n   - suggestions 里有 \"运行 --mark-read\" → \"需要帮你把未读公告和课件标为已读吗？\"\n   - cleanup_suggestion 不为空 → \"工作区有垃圾文件，需要看看吗？\"\n   - graded_homeworks 有内容 → \"老师批了你的作业，评语是...\"\n\n④ 用户确认后，自己跑对应命令:\n   - 标已读: python scripts/todos_api.py --mark-read\n   - 清理预览: python scripts/ops.py --action cleanup --dry-run\n   - 执行清理: python scripts/ops.py --action cleanup\n```\n\n### 详细 CLI 命令\n\n#### login_manager.py\n\n```bash\npython scripts/login_manager.py                                # 确保 session\npython scripts/login_manager.py --check                        # 检查状态\npython scripts/login_manager.py --init --learn-account=... --password=... --username=... --name=...\npython scripts/login_manager.py --reset                        # 清空所有数据\npython scripts/login_manager.py --verify                       # 验证环境（依赖+Chromium+登录）\n```\n\n退出码: 0=有效, 1=错误, 2=需人工 2FA, 3=未初始化\n\n#### learn_api.py\n\n```bash\npython scripts/learn_api.py --action courses                               # 课程列表\npython scripts/learn_api.py --action announcements --course <wlkcid>       # 公告列表\npython scripts/learn_api.py --action announcement-detail --course <wlkcid> --id <ggid>  # 公告详细内容\npython scripts/learn_api.py --action files --course <wlkcid>               # 课件列表\npython scripts/learn_api.py --action homeworks --course <wlkcid>           # 作业列表\npython scripts/learn_api.py --action homework-full --course <wlkcid> --id <zyid> --xszyid <xszyid>  # 作业完整信息（含评语）\npython scripts/learn_api.py --action discussions --course <wlkcid>         # 讨论列表\npython scripts/learn_api.py --action questionnaires --course <wlkcid>      # 问卷列表\npython scripts/learn_api.py --action aggregated --course <wlkcid>          # 聚合查询\npython scripts/learn_api.py --action mark-all-announcements-read --course <wlkcid>\npython scripts/learn_api.py --action mark-all-files-read --course <wlkcid>\n```\n\n#### ops.py\n\n```bash\npython scripts/ops.py --action list-files --course <wlkcid> [--pattern \"*.pdf\"]\npython scripts/ops.py --action download --course <wlkcid> --wjid <wjid>\npython scripts/ops.py --action download --course <wlkcid> --wjid-list \"id1,id2,...\"\npython scripts/ops.py --action download-all --course <wlkcid> [--pattern \"*.pdf\"]\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path> --confirm  # 确认后实际提交\npython scripts/ops.py --action pdf-merge --input-dir <目录> [--output <路径>]\npython scripts/ops.py --action move-in --file <source_path>\npython scripts/ops.py --action cleanup --dry-run\npython scripts/ops.py --action cleanup\n```\n\n#### todos_api.py\n\n```bash\npython scripts/todos_api.py                           # 代办汇总\npython scripts/todos_api.py --mark-read               # + 标记可读项已读\npython scripts/todos_api.py --cleanup-preview         # + 清理预览\npython scripts/todos_api.py --cleanup                 # + 执行清理\n```\n\n**输出解析**：\n- `suggestions` — AI 读取后应主动询问用户：\"需要帮你把未读公告和课件标为已读吗？\"\"需要帮你看看工作区有哪些垃圾文件吗？\"\n- `graded_homeworks` — 老师新批改的作业及其评语/分数\n- `new_submissions_logged` — 本次新增的已提交未批改作业数量\n\n#### pdf_merge.py\n\n```bash\npython scripts/pdf_merge.py --input-dir <目录> [--output <路径>]\n```\n\n支持: jpg, jpeg, png, webp, bmp, heic, heif\n\n### 常见任务工作流\n\n#### 图片作业提交（QQ 发送图片）\n\n```\n1. 用户每发一张图 → AI 保存到 uploads/，按顺序命名为 001.jpg、002.jpg...\n2. 用户说\"发送完毕\"\n3. ops.py --action pdf-merge --input-dir uploads/ → merged.pdf\n4. 删除 uploads/ 下所有图片（保留 merged.pdf）\n5. ops.py --action upload --course ... --xszyid ... --file uploads/merged.pdf --confirm\n   → 自动重命名为 学号_姓名.pdf\n6. 删除 uploads/merged.pdf\n```\n\n#### 下载课件\n\n```\nlearn_api.py --action courses                    # 获取课程列表+ID\nops.py --action download-all --course <wlkcid>   # 下载全部\n```\n\n### 提交日志机制\n\n文件: `submissions/submissions_log.json`\n\n```\ntodos_api.py 每次运行时:\n  1. sync_submissions_log() → 扫描所有已交未批改作业，新出现的追加到日志（已存在则跳过）\n  2. check_graded_submissions() → 遍历日志，检查是否有作业已被批改\n     有 → 获取评语/分数，从日志中移除，在输出中加入 graded_homeworks\n```\n\n### 配置 (config.json)\n\n```json\n{\n  \"auto_mark_read\": false,\n  \"confirm_before_cleanup\": true,\n  \"confirm_before_download\": true\n}\n```\n\n| 选项 | 说明 | 默认 |\n|------|------|------|\n| `auto_mark_read` | 运行 `todos_api.py` 时自动标已读 | `false` |\n| `confirm_before_cleanup` | 清理前是否需用户确认 | `true` |\n| `confirm_before_download` | 批量下载前是否需用户确认 | `true` |\n\n### 未读判断标准\n\n| 模块 | API 字段 | 过滤值 |\n|------|----------|--------|\n| 作业 | `zt` | `\"未交\"` |\n| 公告 | `sfyd` | `\"否\"` |\n| 课件 | `isNew` | `\"1\"` |\n| 讨论 | `htsl` | `>0` |\n\n### 注意事项与不可做清单\n\n#### 不可做\n\n- **不要硬编码课程 ID、学期、路径、姓名**\n- **不要在脚本中使用 `input()` 或 `getpass()`** — AI 运行脚本，不能阻塞\n- **不要直接修改 `credentials.json`** — 通过 `login_manager.py --init` 操作\n- **不要创建新脚本除非绝对必要**\n- **不要在 `config.json` 之外的地方存储用户信息**\n- **不要告诉用户命令行操作** — 用户只回答\"要/不要\"，AI 代为执行\n\n#### 注意事项\n\n- **文件名编码**：服务器将 UTF-8 的中文文件名以 latin-1 编码发送，`download_file()` 已做修复\n- **作业提交端点**：真实端点是 `/b/wlxt/kczy/zy/student/tjzy`，不是 `uploadFile/saveXszj`\n- **公告标已读**：端点 GET `/f/wlxt/kcgg/wlkc_ggb/student/beforeViewXs`，不是 `saveYd`\n- **公告内容**：直接从列表 API 的 `ggnr` base64 字段解码\n- **学期自动检测**：每次建立 session 后调用 `get_current_semester()`\n- **重置**：`login_manager.py --reset` 保留所有文件结构，只清空内容\n- **已提交的作业无法撤回**：网络学堂不提供撤回已提交作业的 API\n\nFile v1.0.12:_meta.json\n\n{\n  \"ownerId\": \"kn73vfhykr10snz07772njvdmx84mcb6\",\n  \"slug\": \"tsinghua-learn\",\n  \"version\": \"1.0.12\",\n  \"publishedAt\": 1777772238312\n}\n\nFile v1.0.12:AGENTS.md\n\n# tsinghua-learn — AGENTS.md\n\n## 架构\n\n6 个脚本：\n\n| 脚本 | 职责 |\n|------|------|\n| `_config.py` | 路径/配置/凭证、DPAPI 解密 |\n| `login_manager.py` | 中央认证网关（默认 fast check，`--force-login` 完整登录） |\n| `learn_api.py` | HTTP API（课程/公告/课件/作业/讨论/问卷/聚合查询） |\n| `ops.py` | 文件操作（下载/批量下载/上传/移入/清理） |\n| `todos_api.py` | 日常管理（代办汇总/标已读/清理，JSON 给 AI） |\n| `pdf_merge.py` | 图片→PDF |\n\n登录脚本不直接调用：\n| `login_auto.py` | headless 自动登录 |\n| `login_supervised.py` | 2FA 登录 |\n\n## 核心命令\n\n```bash\npython scripts/login_manager.py                        # 快速检查 session (<1s)\npython scripts/login_manager.py --init                 # 初始化凭据\npython scripts/login_manager.py --verify               # 验证环境+登录\npython scripts/login_manager.py --reset                # 清空所有数据\npython scripts/login_manager.py --force-login          # 强制完整登录\n\npython scripts/todos_api.py                            # 代办汇总\npython scripts/todos_api.py --mark-read                # + 标已读\npython scripts/todos_api.py --cleanup                  # + 清理\n\npython scripts/ops.py --action list-files --course <wlkcid>\npython scripts/ops.py --action download --course <wlkcid> --wjid <wjid>\npython scripts/ops.py --action download-all --course <wlkcid>\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\npython scripts/ops.py --action pdf-merge --input-dir <目录>\npython scripts/ops.py --action cleanup --dry-run\npython scripts/ops.py --action cleanup\n```\n\n## 关键约束\n\n- paths/creds 全走 `_config.py` getter\n- `credentials.json` DPAPI 加密\n- 学期从 API 自动检测\n- 课程从 API 动态获取\n- 不创建新脚本\n- 脚本不阻塞（`input()`/`getpass()` 禁用）\n- 提交作业自动重命名：`学号_姓名.ext`\n\nFile v1.0.12:config.json\n\n{\r\n    \"auto_mark_read\": true,\n  \"confirm_before_cleanup\": true,\r\n  \"confirm_before_download\": true\r\n}\n\nFile v1.0.12:credentials.json\n\n{\r\n  \"username\": \"\",\r\n  \"password\": \"\",\r\n  \"student_id\": \"\",\r\n  \"name\": \"\"\r\n}\n\nArchive v1.0.10: 16 files, 42205 bytes\n\nFiles: AGENTS.md (1975b), config.json (102b), credentials.json (77b), scripts/_config.py (6203b), scripts/install_deps.py (2207b), scripts/install_playwright.py (984b), scripts/learn_api.py (28555b), scripts/login_auto.py (5076b), scripts/login_manager.py (11918b), scripts/login_supervised.py (4842b), scripts/ops.py (13323b), scripts/pdf_merge.py (2569b), scripts/todos_api.py (6365b), scripts/todos_dom.py (6534b), SKILL.md (25949b), _meta.json (134b)\n\nFile v1.0.10:SKILL.md\n\n---\nname: tsinghua-learn\ndescription: 清华网络学堂（learn.tsinghua.edu.cn）自动化。要求用户提供网络学堂登录密码（本地 DPAPI 加密存储），自动登录、查看待办、下载课件、提交作业、批量标记已读。当需要操作网络学堂完成日常学习任务时使用。\nmetadata:\n  openclaw:\n    requires:\n      env:\n        - CAS_PASSWORD\n    primaryEnv: CAS_PASSWORD\n    os:\n      - windows\n---\n\n# tsinghua-learn skill\n\n清华网络学堂（learn.tsinghua.edu.cn）自动化 Skill。\n\n---\n\n## 如果你是人类，请阅读以下内容\n\n### 前置要求\n\n使用前请确保已安装 **Python 3.10+** 或 **Conda**（推荐 Python 3.11 或 3.12）。建议安装 **7-Zip** 并添加到环境变量 PATH 中，方便后续处理压缩文件。\n\n初始化时会自动安装 Python 依赖（requests、playwright、pillow 等）以及 Chromium 浏览器（~150MB），无需手动操作。\n\n如果没有安装上述环境，请先安装后再对 AI 说 **\"开始初始化\"**。\n\n### 如何与 AI 沟通\n\n你只需要像聊天一样告诉 AI 你想做什么，AI 会帮你完成所有操作。\n\n#### 初始化\n\n对 AI 说：**\"开始初始化\"**\n\nAI 会依次：\n1. 询问你的网络学堂登录账号、密码、学号、姓名\n2. 检查 Python 环境并自动安装所需依赖\n3. 安装 Chromium 浏览器\n4. 尝试登录网络学堂\n5. 登录成功后询问你的偏好设置（是否自动标已读等）\n6. 完成后即可正常使用\n\n> **四个字段说明：**\n> - **网络学堂登录账号**（`--learn-account`）— 用于登录 learn.tsinghua.edu.cn 的账号，可能和学号不同\n> - **登录密码**（`--password`）— 网络学堂登录密码，仅发送至 id.tsinghua.edu.cn（CAS）和 learn.tsinghua.edu.cn\n> - **学号**（`--username`）— 你的学生证号，如 2025013187\n> - **姓名**（`--name`）— 你的真实姓名\n\n#### 查看待办\n\n对 AI 说：**\"查看待办\"** 或 **\"看看有什么作业\"**\n\nAI 会列出所有课程的最新动态：\n- 未交作业及截止日期\n- 未读公告和未浏览课件\n- 已提交但老师还没批的作业（老师批改后会自动汇报评语和分数）\n- 工作区缓存大小\n\nAI 还会主动问你是否需要标已读、清理缓存。\n\n> 你也可以设置每日自动查看：将 `todos_api.py` 配置为定时任务即可。\n\n#### 提交作业\n\n**方式一：直接发送 PDF 文件**\n\n对 AI 说：**\"我要交某某课程的作业\"**，然后发送 PDF 文件即可。\n\n**方式二：发送多张图片合并为 PDF**\n\n先说：**\"我要交某某课程的作业，我发几张图片给你\"**\n\n然后**按顺序**发送图片（先发的会在最终 PDF 的第一页，后发的在后面）。最后说：**\"已经上传完毕\"**\n\nAI 会先列出要提交的课程、作业标题、文件名，请你确认。确认后自动：\n1. 按你发送的顺序命名图片（001、002...）\n2. 合并为 PDF（按文件名排序，所以先发的在 PDF 前面）\n3. 重命名为 `学号_姓名.pdf`\n4. 提交到指定作业\n\n#### 下载课件\n\n对 AI 说：**\"帮我下载微积分的课件\"**\n\nAI 会列出课程的全部课件文件。你也可以更精确地指定：\n\n- **\"下载未读课件\"** — 只下载还没看过的课件\n- **\"下载概率论第 8 讲的课件\"** — 下载指定名称的文件\n- **\"把微积分的课件全部下载到 D 盘\"** — 批量下载到指定目录\n\nAI 会在下载前列出文件清单让你确认。\n\n#### 查看通知和作业详情\n\n对 AI 说：\n\n- **\"看看微积分最近有什么通知\"** — 查看指定课程的公告\n- **\"概率论 HW10 的作业内容是什么\"** — 查看作业的题目和附件\n- **\"上次交的物理作业老师批了吗\"** — 查看已批改作业的评语和分数\n- **\"看看英语课有什么讨论\"** — 查看课程讨论区\n\n#### 清理工作区\n\nAI 在查看待办时会主动提醒你工作区有缓存。这些缓存是下载的课件、合并 PDF 时的临时图片、调试日志等文件，不影响功能但会占用磁盘空间。\n\n只需说 **\"好\"** 或 **\"清理吧\"**，AI 会先列出待清理的文件让你确认，确认后自动删除。\n\n#### 其他\n\n如果有这里没列出的需求，直接告诉 AI 你想做什么即可。AI 会根据现有功能尽力帮你完成。\n\n#### 数据与安全\n\n本 Skill 会在本地存储以下数据：\n\n| 数据 | 位置 | 说明 |\n|------|------|------|\n| 登录凭据 | `credentials.json` | Windows DPAPI 加密（绑定当前用户账户，仅 Windows 支持） |\n| 浏览器登录状态 | `profiles/learn_profile/` | Chrome Profile 含 cookies，避免每次触发 2FA |\n| Session 令牌 | `sessions/learn_session.json` | JSESSIONID + CSRF Token |\n\n如需清除所有数据，对 AI 说 **\"重置所有数据\"**，AI 会清空以上目录内容并恢复到初始状态。\n\n> 这些数据仅存储在本地计算机，不会上传到任何云端。网络学堂本身不支持注销 API，如需完全登出请手动在浏览器中操作。\n\n#### 修改配置\n\n你可以让 AI 帮你修改以下设置，只需要说\"帮我改一下配置\"或\"设置自动标已读\"即可：\n\n| 配置项 | 说明 | 默认值 |\n|--------|------|--------|\n| `auto_mark_read` | 查看待办时是否自动标已读 | 关闭 |\n| `confirm_before_cleanup` | 清理缓存前是否需要你确认 | 需要 |\n| `confirm_before_download` | 批量下载前是否需要你确认 | 需要 |\n\n例如：\n- **\"以后自动标已读吧\"** → AI 会开启 `auto_mark_read`\n- **\"清理的时候不用问我了\"** → AI 会把 `confirm_before_cleanup` 设为 false\n\n---\n\n> **当前版本为测试版本，建议同学们完成提交作业等关键操作后自行登录网络学堂查看是否有错误。**\n\n了解更多信息见 GitHub：[github.com/TOMUIV/tsinghua-learn](https://github.com/TOMUIV/tsinghua-learn)\n\n如有任何问题，联系 **ykx25@mails.tsinghua.edu.cn**\n\n---\n\n## 如果你是 AI，请阅读以下内容\n\n### 铁律（违反会导致用户愤怒）\n\n**铁律 1：AI 运行所有脚本。AI 不能要求用户运行任何命令。**\n- ❌ 禁止的行为：AI 对用户说\"请在终端里运行这个命令\"。\n- ❌ 禁止的行为：AI 给用户一段代码让用户复制粘贴。\n- ✅ 正确的行为：AI 自己调用 `python scripts/xxx.py`，读取输出 JSON，根据输出决定下一步。\n\n**铁律 0（最高优先级）：AI 面向用户的回复中，绝对禁止出现以下内容（除非用户主动要求知情披露）：**\n- ❌ 任何命令行（`python xxx.py`、`cd xxx`）\n- ❌ 任何文件路径（`C:\\Users\\...`、`skills\\...`）\n- ❌ 任何技术术语（\"终端\"、\"PowerShell\"、\"运行脚本\"）\n- ❌ 学号、密码原文\n- ⚠️ 例外：当用户明确询问安装了什么、存储了什么、路径在哪等安全相关问题时，AI 可以简要说明。初始化确认、提交确认等关键操作不受此限。\n\n**用户能看到什么：**\n- ✅ \"系统尚未初始化，请提供你的账号信息\"\n- ✅ \"正在获取待办...\"\n- ✅ \"你有 5 项未交作业\"\n- ❌ \"请在终端里跑这个命令\"\n\n**铁律 2：默认只跑快速检查。`login_manager.py` 默认为 `--check` 行为（<1 秒，无浏览器）。**\n- 当前版本的 `login_manager.py` 不带参数时等同于 `--check`，不会启动浏览器。\n- ❌ 禁止的行为：直接跑 `python scripts/login_auto.py` 或 `python scripts/login_supervised.py`。\n- ✅ 正确的行为：\n  - 跑 `python scripts/login_manager.py`（默认就是快速检查，<1 秒）\n  - `initialized: false` → 停下来，告诉用户\"系统尚未初始化\"，走初始化流程。\n  - `valid: true` → 直接进行后续操作。\n  - `valid: false` → 用 `python scripts/login_manager.py --force-login` 执行完整登录。\n\n**铁律 3：`login_manager.py` 是唯一的认证入口。AI 不直接调用其他登录脚本。**\n- AI 不直接调用 `login_auto.py`。\n- AI 不直接调用 `login_supervised.py`。\n- AI 不直接调用 `install_playwright.py`。\n- 上述脚本由 `login_manager.py` 在内部自动调用。\n\n**铁律 4：AI 不凭经验推断问题。所有判断基于脚本输出的 JSON。**\n\n**铁律 5：密码仅用于初始化收集，之后绝不在对话中重复展示。**\n- 初始化时 AI 询问密码，收集后立即传入脚本，不在后续对话中提及密码值。\n- AI 展示 `fields` 内容时，密码字段已脱敏（显示为 `xxx***`）。\n- ❌ 禁止的行为：AI 在初始化之外的对话中说密码原文。\n\n**铁律 6（提交确认）：提交作业必须先调用不带 --confirm 的上传预览，获得用户明确同意后才能加 --confirm 执行。**\n- ❌ 禁止的行为：AI 直接加 --confirm 提交，不展示预览。\n- ✅ 正确的行为：先不带 --confirm 调 upload 得到 preview JSON → 展示用户 → 同意后再加 --confirm\n\n---\n\n### 凭据字段速查\n\n| 字段 | CLI 参数 | 说明 |\n|------|---------|------|\n| 网络学堂登录账号 | `--learn-account` | 登录 learn.tsinghua.edu.cn 用的账号 |\n| 登录密码 | `--password` | 网络学堂登录密码，仅发送至 id.tsinghua.edu.cn（CAS）和 learn.tsinghua.edu.cn |\n| 学号 | `--username` | 学生证号 |\n| 姓名 | `--name` | 真实姓名 |\n\n> 学号和网络学堂登录账号可能相同也可能不同，分别问清楚。\n> ⚠️ 表中没有示例值。AI 不要编造学号或任何数字作为示例。\n\n---\n\n### 脚本速查\n\n| 脚本 | AI 什么时候需要调用它 | 速度 |\n|------|---------------------|------|\n| `login_manager.py` | 默认快速检查（<1s），有--init/--reset/--force-login/--verify | ⚡快/🐢慢 |\n| `todos_api.py` | 用户要求查看待办时 | ⚡快（2-5s） |\n| `ops.py` | 下载文件、上传作业、合并PDF、清理工作区 | ⚡快 |\n| `learn_api.py` | 查询课程列表、公告详情、作业内容 | ⚡快 |\n| `pdf_merge.py` | 图片合并PDF（ops.py内部调用） | ⚡快 |\n| `install_deps.py` | AI不直接调用（--verify内部自动调用） | 🐢慢 |\n| `install_playwright.py` | AI不直接调用 | 🐢慢 |\n| `login_auto.py` | AI不直接调用 | 🐢慢（有浏览器） |\n| `login_supervised.py` | AI不直接调用 | 🐢慢（有浏览器） |\n\n> ⚠️ 标🐢的脚本涉及浏览器/docker/下载，AI不应直接调用。它们由 `login_manager.py --force-login` 或 `--verify` 内部自动调用。\n\n---\n\n### 初始化流程\n\n用户说\"开始初始化\"时，AI 按以下步骤执行：\n\n**Step 1：向用户收集信息**\n```\nAI 依次问用户（不要一次全问，分步问）：\n第一部分（必须）：\n1. 网络学堂登录账号是什么？\n2. 登录密码是什么？\n3. 学号是什么？\n4. 姓名是什么？\n\n第二部分（选填，用户回答了必须信息后再问）：\n5. 是否需要自动标已读？（默认：否）\n6. 清理工作区前是否需要确认？（默认：是）\n7. 批量下载前是否需要确认？（默认：是）\n\n⚠️ AI 不编造示例值（不要写\"如 2025013187\"之类的数字）\n⚠️ AI 不显示学号或密码在对话中\n```\n\n**Step 2：AI 调用 `--init` 保存凭据**\n```\n1. AI 将凭据 JSON 写入临时文件（不包含在任何命令字符串中）\n2. 命令：python scripts/login_manager.py --init --cred-stdin < /tmp/cred_temp.json\n3. AI 立即删除临时文件\n输出：{\"status\":\"ok\", \"fields\":{\"learn_account\":\"...\", \"username\":\"...\", \"name\":\"...\"}}\n```\n> 使用临时文件 + stdin 重定向，密码只存在于临时文件中（随即删除），不出现在任何命令行参数或 echo 字符串中。\n\n**Step 3：AI 向用户展示 fields 内容，请用户确认**\n\n**Step 4：用户确认后，AI 调用 `--verify` 验证环境并尝试登录**\n```\n命令：python scripts/login_manager.py --verify\n输出：{\"python\":true, \"deps\":true, \"chromium\":true, \"login\":true, \"needs_2fa\":false}\n\nlogin=true       → Step 5\nneeds_2fa=true   → AI 告诉用户\"自动登录失败，请打开浏览器完成二次验证\"\n                   用户说\"好了\"后，AI 调用：python scripts/login_manager.py\n```\n\n**Step 5：AI 调用 `todos_api.py` 首次查看待办**\n```\n命令：python scripts/todos_api.py\n输出：JSON（含课程列表、待办数量等，submissions 日志自动初始化）\n```\n\n---\n\n### 日常查看待办流程\n\n任何时候用户说\"查看待办\"\"有什么作业\"\"看看通知\"时：\n\n**Step 1：AI 检查凭据和 session**\n\n跑默认命令（快速检查，<1 秒，无浏览器）：\n```\n命令：python scripts/login_manager.py\n输出：{\"initialized\": false/true, \"valid\": false/true, \"age_h\": ...}\n```\n\n根据输出决定下一步：\n\n① `initialized: false` → AI 停下来，告诉用户\"系统尚未初始化\"，走初始化流程\n② `initialized: true, valid: true` → 跳到 Step 2（获取待办）\n③ `initialized: true, valid: false` → AI 跑 `python scripts/login_manager.py --force-login`（会启动浏览器，约30-60秒）\n\n**Step 2：AI 获取待办数据**\n```\n命令：python scripts/todos_api.py\n输出：JSON 包含 courses、suggestions、graded_homeworks、cleanup_suggestion\n```\n\n**Step 3：AI 根据输出中的字段决定问用户什么**\n\n检查 `graded_homeworks` 字段：\n- 如果有内容 → AI 告诉用户\"老师批改了你的作业：[课程名][标题]，评语：[comment]，得分：[score]\"\n\n检查 `submissions_tracked` 字段：\n- 如果 > 0 → AI 告诉用户\"有 N 个已提交的作业等待老师批改，批改后我会通知你\"\n\n检查 `suggestions` 数组：\n- 如果包含 \"运行 --mark-read\" → AI 问用户\"需要把未读公告和课件标为已读吗？\"\n- 如果包含 \"运行 --cleanup-preview\" → AI 问用户\"需要看看工作区有哪些垃圾文件吗？\"\n\n检查 `suggestions` 和 `graded_homeworks` 都不存在时：\n- AI 告诉用户\"目前没有新的待办事项\"\n\n**Step 4：用户同意后，AI 自己运行对应命令**\n```\n标已读：        python scripts/todos_api.py --mark-read\n清理预览：      python scripts/ops.py --action cleanup --dry-run\n执行清理：      python scripts/ops.py --action cleanup\n```\n\n---\n\n### 提交作业流程\n\n用户说\"我要交作业\"或发来文件/图片时：\n\n**Step 1：AI 确认要交到哪个作业**\n```\n命令：python scripts/learn_api.py --action courses\n输出：课程列表（AI 找到用户说的课程，记录 wlkcid）\n\n命令：python scripts/learn_api.py --action homeworks --course <wlkcid>\n输出：作业列表（AI 找到用户说的作业，记录 xszyid）\n```\n\n**Step 2：AI 处理文件**\n\n情况 A — 用户直接发送了 PDF 文件：\n```\nAI 将文件保存到 uploads/，进入 Step 3\n```\n\n情况 B — 用户发送多张图片（如 QQ 传图）：\n```\nAI 每收到一张图，保存到 uploads/，按顺序命名为 001.jpg、002.jpg...\n用户说\"已经上传完毕\"后：\n命令：python scripts/ops.py --action pdf-merge --input-dir uploads/\n输出：merged.pdf\nAI 删除 uploads/ 下的所有图片文件\n进入 Step 3\n```\n\n**Step 3：AI 展示确认信息，获得同意后提交**\n\n```\n先执行未确认的提交（展示详情，不实际提交）：\n命令：python scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\n输出：{\"status\":\"pending\", \"requiring\":\"confirmation\", \"details\":{...}}\n\nAI 必须向用户展示：\n- 课程名称\n- 作业标题\n- 待提交的文件名\n- 提交账号（姓名+学号脱敏）\n\n例：\"准备提交 微积分(2) 的 第四次书面作业，文件：2025013187_张三.pdf。确认吗？\"\n\n用户明确说\"确认\"/\"好\"/\"提交\"后，AI 添加 --confirm 重新执行：\n命令：python scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path> --confirm\n输出：{\"status\":\"ok\"}\n```\n\n**Step 4：AI 通知用户提交成功，清理临时文件**\n\n---\n\n### 下载课件流程\n\n用户说\"帮我下载课件\"时：\n\n**Step 1：AI 获取课程列表**\n```\n命令：python scripts/learn_api.py --action courses\n输出：课程列表（AI 找到用户说的课程，记录 wlkcid）\n```\n\n**Step 2：AI 下载文件**\n```\n命令：python scripts/ops.py --action download-all --course <wlkcid> --save-dir <目录>\n输出：下载结果\n```\n如果用户指定了具体文件名或模式，在 `--pattern` 参数中指定。\n\n---\n\n### 常见问题处理\n\n**Session 过期**\n```\nAI 不分析 session 文件。不检查时间戳。不自己调 login_auto.py。\n只做一件事：\n命令：python scripts/login_manager.py\n输出：session 有效则返回，无效则自动续期\n```\n\n**需要二次验证（2FA）**\n```\nlogin_manager.py 或 --verify 返回 needs_2fa=true 时：\nStep 1：AI 告诉用户\"自动登录失败，请打开浏览器完成二次验证\"\nStep 2：用户说\"好了\"后，AI 调用：\n命令：python scripts/login_manager.py\n输出：验证 session 是否已建立\n```\n\n**凭据未初始化**\n```\nlogin_manager.py --check 返回 initialized=false 时：\nAI 告诉用户\"系统尚未初始化\"，然后按初始化流程的 Step 1 开始收集信息\n```\n```\n# --verify 或 login_manager.py 返回 needs_2fa=true 时:\n# ✅ 正确做法：\n#   告诉用户\"自动登录失败，请打开浏览器完成二次验证\"\n#   等用户说\"好了\"\npython scripts/login_manager.py\n# ← 再次检查 session，应该已经有效了\n```\n\n#### 凭据未初始化怎么办？\n```\n# login_manager.py --check 返回 initialized=false 时:\n# ✅ 正确做法：\n#   告诉用户\"系统尚未初始化，请提供你的网络学堂登录账号、密码、学号、姓名\"\n#   收集信息后走初始化流程\n```\n\n---\n\n### 脚本概览\n\n| 脚本 | 职责 | 什么时候用 |\n|------|------|-----------|\n| `login_manager.py` | 认证网关 | 任何操作前确保 session；初始化；验证环境 |\n| `todos_api.py` | 日常管理 | 用户问待办、标已读、清理 |\n| `ops.py` | 文件操作 | 下载、上传、合并 PDF、清理工作区 |\n| `learn_api.py` | 数据查询 | 查课程列表、公告详情、作业内容 |\n| `pdf_merge.py` | 图片→PDF | 用户发图片要交作业时 |\n| `install_deps.py` | 依赖安装 | --verify 会自动调用，你不直接碰 |\n| `_config.py` | 配置读取 | 你不直接碰 |\n| `login_auto.py` | 无头登录 | 你不直接碰，login_manager 内部调用 |\n| `login_supervised.py` | 2FA 登录 | 你不直接碰，login_manager 内部调用 |\n| `install_playwright.py` | Chromium 安装 | --verify 会自动调用，你不直接碰 |\n\n### 日常运行流程\n\n任何时候用户问\"查看待办\"\"有什么作业\"等：\n\n```\n① 先确保 session:\n   python scripts/login_manager.py\n   ← 如果 session 过期会自动续期\n\n② 获取待办:\n   python scripts/todos_api.py\n   ← 得到 JSON {\"courses\": [...], \"total_unread\": N, \"suggestions\": [...], ...}\n\n③ 解析输出，主动问用户:\n   - suggestions 里有 \"运行 --mark-read\" → \"需要帮你把未读公告和课件标为已读吗？\"\n   - cleanup_suggestion 不为空 → \"工作区有垃圾文件，需要看看吗？\"\n   - graded_homeworks 有内容 → \"老师批了你的作业，评语是...\"\n\n④ 用户确认后，自己跑对应命令:\n   - 标已读: python scripts/todos_api.py --mark-read\n   - 清理预览: python scripts/ops.py --action cleanup --dry-run\n   - 执行清理: python scripts/ops.py --action cleanup\n```\n\n### 常见问题处理\n\n#### session 过期\n```\npython scripts/login_manager.py\n← session 失效时会自动续期，无需特殊处理\n```\n\n#### 需要二次验证 (2FA)\n```\n# --verify 返回 needs_2fa=true 时:\n告诉用户\"自动登录失败，请打开浏览器完成二次验证\"\n等用户说\"好了\"后:\npython scripts/login_manager.py\n← 验证 session\n```\n\n#### 凭据未初始化\n```\n# login_manager.py 返回 code=3:\n告诉用户\"系统尚未初始化，请提供你的网络学堂登录账号、密码、学号、姓名\"\n→ 走初始化流程\n```\n\n### 详细 CLI 命令\n\n#### login_manager.py\n\n```bash\npython scripts/login_manager.py                                # 确保 session\npython scripts/login_manager.py --check                        # 检查状态\npython scripts/login_manager.py --init --learn-account=... --password=... --username=... --name=...\npython scripts/login_manager.py --reset                        # 清空所有数据\npython scripts/login_manager.py --verify                       # 验证环境（依赖+Chromium+登录）\n```\n\n退出码: 0=有效, 1=错误, 2=需人工 2FA, 3=未初始化\n\n#### learn_api.py\n\n```bash\npython scripts/learn_api.py --action courses                               # 课程列表\npython scripts/learn_api.py --action announcements --course <wlkcid>       # 公告列表\npython scripts/learn_api.py --action announcement-detail --course <wlkcid> --id <ggid>  # 公告详细内容\npython scripts/learn_api.py --action files --course <wlkcid>               # 课件列表\npython scripts/learn_api.py --action homeworks --course <wlkcid>           # 作业列表\npython scripts/learn_api.py --action homework-full --course <wlkcid> --id <zyid> --xszyid <xszyid>  # 作业完整信息（含评语）\npython scripts/learn_api.py --action discussions --course <wlkcid>         # 讨论列表\npython scripts/learn_api.py --action questionnaires --course <wlkcid>      # 问卷列表\npython scripts/learn_api.py --action aggregated --course <wlkcid>          # 聚合查询\npython scripts/learn_api.py --action mark-all-announcements-read --course <wlkcid>\npython scripts/learn_api.py --action mark-all-files-read --course <wlkcid>\n```\n\n#### ops.py\n\n```bash\npython scripts/ops.py --action list-files --course <wlkcid> [--pattern \"*.pdf\"]\npython scripts/ops.py --action download --course <wlkcid> --wjid <wjid>\npython scripts/ops.py --action download --course <wlkcid> --wjid-list \"id1,id2,...\"\npython scripts/ops.py --action download-all --course <wlkcid> [--pattern \"*.pdf\"]\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path> --confirm  # 确认后实际提交\npython scripts/ops.py --action pdf-merge --input-dir <目录> [--output <路径>]\npython scripts/ops.py --action move-in --file <source_path>\npython scripts/ops.py --action cleanup --dry-run\npython scripts/ops.py --action cleanup\n```\n\n#### todos_api.py\n\n```bash\npython scripts/todos_api.py                           # 代办汇总\npython scripts/todos_api.py --mark-read               # + 标记可读项已读\npython scripts/todos_api.py --cleanup-preview         # + 清理预览\npython scripts/todos_api.py --cleanup                 # + 执行清理\n```\n\n**输出解析**：\n- `suggestions` — AI 读取后应主动询问用户：\"需要帮你把未读公告和课件标为已读吗？\"\"需要帮你看看工作区有哪些垃圾文件吗？\"\n- `graded_homeworks` — 老师新批改的作业及其评语/分数\n- `new_submissions_logged` — 本次新增的已提交未批改作业数量\n\n#### pdf_merge.py\n\n```bash\npython scripts/pdf_merge.py --input-dir <目录> [--output <路径>]\n```\n\n支持: jpg, jpeg, png, webp, bmp, heic, heif\n\n### 常见任务工作流\n\n#### 图片作业提交（QQ 发送图片）\n\n```\n1. 用户每发一张图 → AI 保存到 uploads/，按顺序命名为 001.jpg、002.jpg...\n2. 用户说\"发送完毕\"\n3. ops.py --action pdf-merge --input-dir uploads/ → merged.pdf\n4. 删除 uploads/ 下所有图片（保留 merged.pdf）\n5. ops.py --action upload --course ... --xszyid ... --file uploads/merged.pdf --confirm\n   → 自动重命名为 学号_姓名.pdf\n6. 删除 uploads/merged.pdf\n```\n\n#### 下载课件\n\n```\nlearn_api.py --action courses                    # 获取课程列表+ID\nops.py --action download-all --course <wlkcid>   # 下载全部\n```\n\n### 提交日志机制\n\n文件: `submissions/submissions_log.json`\n\n```\ntodos_api.py 每次运行时:\n  1. sync_submissions_log() → 扫描所有已交未批改作业，新出现的追加到日志（已存在则跳过）\n  2. check_graded_submissions() → 遍历日志，检查是否有作业已被批改\n     有 → 获取评语/分数，从日志中移除，在输出中加入 graded_homeworks\n```\n\n### 配置 (config.json)\n\n```json\n{\n  \"auto_mark_read\": false,\n  \"confirm_before_cleanup\": true,\n  \"confirm_before_download\": true\n}\n```\n\n| 选项 | 说明 | 默认 |\n|------|------|------|\n| `auto_mark_read` | 运行 `todos_api.py` 时自动标已读 | `false` |\n| `confirm_before_cleanup` | 清理前是否需用户确认 | `true` |\n| `confirm_before_download` | 批量下载前是否需用户确认 | `true` |\n\n### 未读判断标准\n\n| 模块 | API 字段 | 过滤值 |\n|------|----------|--------|\n| 作业 | `zt` | `\"未交\"` |\n| 公告 | `sfyd` | `\"否\"` |\n| 课件 | `isNew` | `\"1\"` |\n| 讨论 | `htsl` | `>0` |\n\n### 注意事项与不可做清单\n\n#### 不可做\n\n- **不要硬编码课程 ID、学期、路径、姓名**\n- **不要在脚本中使用 `input()` 或 `getpass()`** — AI 运行脚本，不能阻塞\n- **不要直接修改 `credentials.json`** — 通过 `login_manager.py --init` 操作\n- **不要创建新脚本除非绝对必要**\n- **不要在 `config.json` 之外的地方存储用户信息**\n- **不要告诉用户命令行操作** — 用户只回答\"要/不要\"，AI 代为执行\n\n#### 注意事项\n\n- **文件名编码**：服务器将 UTF-8 的中文文件名以 latin-1 编码发送，`download_file()` 已做修复\n- **作业提交端点**：真实端点是 `/b/wlxt/kczy/zy/student/tjzy`，不是 `uploadFile/saveXszj`\n- **公告标已读**：端点 GET `/f/wlxt/kcgg/wlkc_ggb/student/beforeViewXs`，不是 `saveYd`\n- **公告内容**：直接从列表 API 的 `ggnr` base64 字段解码\n- **学期自动检测**：每次建立 session 后调用 `get_current_semester()`\n- **重置**：`login_manager.py --reset` 保留所有文件结构，只清空内容\n- **已提交的作业无法撤回**：网络学堂不提供撤回已提交作业的 API\n\nFile v1.0.10:_meta.json\n\n{\n  \"ownerId\": \"kn73vfhykr10snz07772njvdmx84mcb6\",\n  \"slug\": \"tsinghua-learn\",\n  \"version\": \"1.0.10\",\n  \"publishedAt\": 1777733853491\n}\n\nFile v1.0.10:AGENTS.md\n\n# tsinghua-learn — AGENTS.md\n\n## 架构\n\n6 个脚本：\n\n| 脚本 | 职责 |\n|------|------|\n| `_config.py` | 路径/配置/凭证、DPAPI 解密 |\n| `login_manager.py` | 中央认证网关（默认 fast check，`--force-login` 完整登录） |\n| `learn_api.py` | HTTP API（课程/公告/课件/作业/讨论/问卷/聚合查询） |\n| `ops.py` | 文件操作（下载/批量下载/上传/移入/清理） |\n| `todos_api.py` | 日常管理（代办汇总/标已读/清理，JSON 给 AI） |\n| `pdf_merge.py` | 图片→PDF |\n\n登录脚本不直接调用：\n| `login_auto.py` | headless 自动登录 |\n| `login_supervised.py` | 2FA 登录 |\n\n## 核心命令\n\n```bash\npython scripts/login_manager.py                        # 快速检查 session (<1s)\npython scripts/login_manager.py --init                 # 初始化凭据\npython scripts/login_manager.py --verify               # 验证环境+登录\npython scripts/login_manager.py --reset                # 清空所有数据\npython scripts/login_manager.py --force-login          # 强制完整登录\n\npython scripts/todos_api.py                            # 代办汇总\npython scripts/todos_api.py --mark-read                # + 标已读\npython scripts/todos_api.py --cleanup                  # + 清理\n\npython scripts/ops.py --action list-files --course <wlkcid>\npython scripts/ops.py --action download --course <wlkcid> --wjid <wjid>\npython scripts/ops.py --action download-all --course <wlkcid>\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\npython scripts/ops.py --action pdf-merge --input-dir <目录>\npython scripts/ops.py --action cleanup --dry-run\npython scripts/ops.py --action cleanup\n```\n\n## 关键约束\n\n- paths/creds 全走 `_config.py` getter\n- `credentials.json` DPAPI 加密\n- 学期从 API 自动检测\n- 课程从 API 动态获取\n- 不创建新脚本\n- 脚本不阻塞（`input()`/`getpass()` 禁用）\n- 提交作业自动重命名：`学号_姓名.ext`\n\nFile v1.0.10:config.json\n\n{\r\n  \"auto_mark_read\": false,\r\n  \"confirm_before_cleanup\": true,\r\n  \"confirm_before_download\": true\r\n}\n\nFile v1.0.10:credentials.json\n\n{\r\n  \"username\": \"\",\r\n  \"password\": \"\",\r\n  \"student_id\": \"\",\r\n  \"name\": \"\"\r\n}\n\nArchive v1.0.9: 16 files, 41940 bytes\n\nFiles: AGENTS.md (1975b), config.json (102b), credentials.json (77b), scripts/_config.py (6203b), scripts/install_deps.py (2207b), scripts/install_playwright.py (984b), scripts/learn_api.py (28555b), scripts/login_auto.py (5076b), scripts/login_manager.py (11918b), scripts/login_supervised.py (4842b), scripts/ops.py (12708b), scripts/pdf_merge.py (2569b), scripts/todos_api.py (6365b), scripts/todos_dom.py (6534b), SKILL.md (25759b), _meta.json (133b)\n\nFile v1.0.9:SKILL.md\n\n---\nname: tsinghua-learn\ndescription: 清华网络学堂（learn.tsinghua.edu.cn）自动化。要求用户提供网络学堂登录密码（本地 DPAPI 加密存储），自动登录、查看待办、下载课件、提交作业、批量标记已读。当需要操作网络学堂完成日常学习任务时使用。\nmetadata:\n  openclaw:\n    requires:\n      primaryCredential: cas_password\n    primaryCredential: cas_password\n    credentialScope: \"learn.tsinghua.edu.cn, id.tsinghua.edu.cn (CAS)\"\n---\n\n# tsinghua-learn skill\n\n清华网络学堂（learn.tsinghua.edu.cn）自动化 Skill。\n\n---\n\n## 如果你是人类，请阅读以下内容\n\n### 前置要求\n\n使用前请确保已安装 **Python 3.10+** 或 **Conda**（推荐 Python 3.11 或 3.12）。建议安装 **7-Zip** 并添加到环境变量 PATH 中，方便后续处理压缩文件。\n\n初始化时会自动安装 Python 依赖（requests、playwright、pillow 等）以及 Chromium 浏览器（~150MB），无需手动操作。\n\n如果没有安装上述环境，请先安装后再对 AI 说 **\"开始初始化\"**。\n\n### 如何与 AI 沟通\n\n你只需要像聊天一样告诉 AI 你想做什么，AI 会帮你完成所有操作。\n\n#### 初始化\n\n对 AI 说：**\"开始初始化\"**\n\nAI 会依次：\n1. 询问你的网络学堂登录账号、密码、学号、姓名\n2. 检查 Python 环境并自动安装所需依赖\n3. 安装 Chromium 浏览器\n4. 尝试登录网络学堂\n5. 登录成功后询问你的偏好设置（是否自动标已读等）\n6. 完成后即可正常使用\n\n> **四个字段说明：**\n> - **网络学堂登录账号**（`--learn-account`）— 用于登录 learn.tsinghua.edu.cn 的账号，可能和学号不同\n> - **登录密码**（`--password`）— 网络学堂登录密码，仅发送至 id.tsinghua.edu.cn（CAS）和 learn.tsinghua.edu.cn\n> - **学号**（`--username`）— 你的学生证号，如 2025013187\n> - **姓名**（`--name`）— 你的真实姓名\n\n#### 查看待办\n\n对 AI 说：**\"查看待办\"** 或 **\"看看有什么作业\"**\n\nAI 会列出所有课程的最新动态：\n- 未交作业及截止日期\n- 未读公告和未浏览课件\n- 已提交但老师还没批的作业（老师批改后会自动汇报评语和分数）\n- 工作区缓存大小\n\nAI 还会主动问你是否需要标已读、清理缓存。\n\n> 你也可以设置每日自动查看：将 `todos_api.py` 配置为定时任务即可。\n\n#### 提交作业\n\n**方式一：直接发送 PDF 文件**\n\n对 AI 说：**\"我要交某某课程的作业\"**，然后发送 PDF 文件即可。\n\n**方式二：发送多张图片合并为 PDF**\n\n先说：**\"我要交某某课程的作业，我发几张图片给你\"**\n\n然后**按顺序**发送图片（先发的会在最终 PDF 的第一页，后发的在后面）。最后说：**\"已经上传完毕\"**\n\nAI 会先列出要提交的课程、作业标题、文件名，请你确认。确认后自动：\n1. 按你发送的顺序命名图片（001、002...）\n2. 合并为 PDF（按文件名排序，所以先发的在 PDF 前面）\n3. 重命名为 `学号_姓名.pdf`\n4. 提交到指定作业\n\n#### 下载课件\n\n对 AI 说：**\"帮我下载微积分的课件\"**\n\nAI 会列出课程的全部课件文件。你也可以更精确地指定：\n\n- **\"下载未读课件\"** — 只下载还没看过的课件\n- **\"下载概率论第 8 讲的课件\"** — 下载指定名称的文件\n- **\"把微积分的课件全部下载到 D 盘\"** — 批量下载到指定目录\n\nAI 会在下载前列出文件清单让你确认。\n\n#### 查看通知和作业详情\n\n对 AI 说：\n\n- **\"看看微积分最近有什么通知\"** — 查看指定课程的公告\n- **\"概率论 HW10 的作业内容是什么\"** — 查看作业的题目和附件\n- **\"上次交的物理作业老师批了吗\"** — 查看已批改作业的评语和分数\n- **\"看看英语课有什么讨论\"** — 查看课程讨论区\n\n#### 清理工作区\n\nAI 在查看待办时会主动提醒你工作区有缓存。这些缓存是下载的课件、合并 PDF 时的临时图片、调试日志等文件，不影响功能但会占用磁盘空间。\n\n只需说 **\"好\"** 或 **\"清理吧\"**，AI 会先列出待清理的文件让你确认，确认后自动删除。\n\n#### 其他\n\n如果有这里没列出的需求，直接告诉 AI 你想做什么即可。AI 会根据现有功能尽力帮你完成。\n\n#### 数据与安全\n\n本 Skill 会在本地存储以下数据：\n\n| 数据 | 位置 | 说明 |\n|------|------|------|\n| 登录凭据 | `credentials.json` | Windows DPAPI 加密（绑定当前用户账户，仅 Windows 支持） |\n| 浏览器登录状态 | `profiles/learn_profile/` | Chrome Profile 含 cookies，避免每次触发 2FA |\n| Session 令牌 | `sessions/learn_session.json` | JSESSIONID + CSRF Token |\n\n如需清除所有数据，对 AI 说 **\"重置所有数据\"**，AI 会清空以上目录内容并恢复到初始状态。\n\n> 这些数据仅存储在本地计算机，不会上传到任何云端。网络学堂本身不支持注销 API，如需完全登出请手动在浏览器中操作。\n\n#### 修改配置\n\n你可以让 AI 帮你修改以下设置，只需要说\"帮我改一下配置\"或\"设置自动标已读\"即可：\n\n| 配置项 | 说明 | 默认值 |\n|--------|------|--------|\n| `auto_mark_read` | 查看待办时是否自动标已读 | 关闭 |\n| `confirm_before_cleanup` | 清理缓存前是否需要你确认 | 需要 |\n| `confirm_before_download` | 批量下载前是否需要你确认 | 需要 |\n\n例如：\n- **\"以后自动标已读吧\"** → AI 会开启 `auto_mark_read`\n- **\"清理的时候不用问我了\"** → AI 会把 `confirm_before_cleanup` 设为 false\n\n---\n\n> **当前版本为测试版本，建议同学们完成提交作业等关键操作后自行登录网络学堂查看是否有错误。**\n\n了解更多信息见 GitHub：[github.com/TOMUIV/tsinghua-learn](https://github.com/TOMUIV/tsinghua-learn)\n\n如有任何问题，联系 **ykx25@mails.tsinghua.edu.cn**\n\n---\n\n## 如果你是 AI，请阅读以下内容\n\n### 铁律（违反会导致用户愤怒）\n\n**铁律 1：AI 运行所有脚本。AI 不能要求用户运行任何命令。**\n- ❌ 禁止的行为：AI 对用户说\"请在终端里运行这个命令\"。\n- ❌ 禁止的行为：AI 给用户一段代码让用户复制粘贴。\n- ✅ 正确的行为：AI 自己调用 `python scripts/xxx.py`，读取输出 JSON，根据输出决定下一步。\n\n**铁律 0（最高优先级）：AI 面向用户的回复中，绝对禁止出现以下内容（除非用户主动要求知情披露）：**\n- ❌ 任何命令行（`python xxx.py`、`cd xxx`）\n- ❌ 任何文件路径（`C:\\Users\\...`、`skills\\...`）\n- ❌ 任何技术术语（\"终端\"、\"PowerShell\"、\"运行脚本\"）\n- ❌ 学号、密码原文\n- ⚠️ 例外：当用户明确询问安装了什么、存储了什么、路径在哪等安全相关问题时，AI 可以简要说明。初始化确认、提交确认等关键操作不受此限。\n\n**用户能看到什么：**\n- ✅ \"系统尚未初始化，请提供你的账号信息\"\n- ✅ \"正在获取待办...\"\n- ✅ \"你有 5 项未交作业\"\n- ❌ \"请在终端里跑这个命令\"\n\n**铁律 2：默认只跑快速检查。`login_manager.py` 默认为 `--check` 行为（<1 秒，无浏览器）。**\n- 当前版本的 `login_manager.py` 不带参数时等同于 `--check`，不会启动浏览器。\n- ❌ 禁止的行为：直接跑 `python scripts/login_auto.py` 或 `python scripts/login_supervised.py`。\n- ✅ 正确的行为：\n  - 跑 `python scripts/login_manager.py`（默认就是快速检查，<1 秒）\n  - `initialized: false` → 停下来，告诉用户\"系统尚未初始化\"，走初始化流程。\n  - `valid: true` → 直接进行后续操作。\n  - `valid: false` → 用 `python scripts/login_manager.py --force-login` 执行完整登录。\n\n**铁律 3：`login_manager.py` 是唯一的认证入口。AI 不直接调用其他登录脚本。**\n- AI 不直接调用 `login_auto.py`。\n- AI 不直接调用 `login_supervised.py`。\n- AI 不直接调用 `install_playwright.py`。\n- 上述脚本由 `login_manager.py` 在内部自动调用。\n\n**铁律 4：AI 不凭经验推断问题。所有判断基于脚本输出的 JSON。**\n\n**铁律 5：密码仅用于初始化收集，之后绝不在对话中重复展示。**\n- 初始化时 AI 询问密码，收集后立即传入脚本，不在后续对话中提及密码值。\n- AI 展示 `fields` 内容时，密码字段已脱敏（显示为 `xxx***`）。\n- ❌ 禁止的行为：AI 在初始化之外的对话中说密码原文。\n\n**铁律 6（提交确认）：提交作业前必须先展示确认信息，获得用户明确同意后才能提交。**\n- ❌ 禁止的行为：AI 收到文件后直接提交，不展示课程、作业标题、文件名。\n- ✅ 正确的行为：AI 展示\"课程：微积分(2)，作业：第四次书面作业，文件：学号_姓名.pdf，确认提交吗？\"\n- 用户说\"确认\"/\"提交\"/\"好\"之后才能执行上传。\n\n---\n\n### 凭据字段速查\n\n| 字段 | CLI 参数 | 说明 |\n|------|---------|------|\n| 网络学堂登录账号 | `--learn-account` | 登录 learn.tsinghua.edu.cn 用的账号 |\n| 登录密码 | `--password` | 网络学堂登录密码，仅发送至 id.tsinghua.edu.cn（CAS）和 learn.tsinghua.edu.cn |\n| 学号 | `--username` | 学生证号 |\n| 姓名 | `--name` | 真实姓名 |\n\n> 学号和网络学堂登录账号可能相同也可能不同，分别问清楚。\n> ⚠️ 表中没有示例值。AI 不要编造学号或任何数字作为示例。\n\n---\n\n### 脚本速查\n\n| 脚本 | AI 什么时候需要调用它 | 速度 |\n|------|---------------------|------|\n| `login_manager.py` | 默认快速检查（<1s），有--init/--reset/--force-login/--verify | ⚡快/🐢慢 |\n| `todos_api.py` | 用户要求查看待办时 | ⚡快（2-5s） |\n| `ops.py` | 下载文件、上传作业、合并PDF、清理工作区 | ⚡快 |\n| `learn_api.py` | 查询课程列表、公告详情、作业内容 | ⚡快 |\n| `pdf_merge.py` | 图片合并PDF（ops.py内部调用） | ⚡快 |\n| `install_deps.py` | AI不直接调用（--verify内部自动调用） | 🐢慢 |\n| `install_playwright.py` | AI不直接调用 | 🐢慢 |\n| `login_auto.py` | AI不直接调用 | 🐢慢（有浏览器） |\n| `login_supervised.py` | AI不直接调用 | 🐢慢（有浏览器） |\n\n> ⚠️ 标🐢的脚本涉及浏览器/docker/下载，AI不应直接调用。它们由 `login_manager.py --force-login` 或 `--verify` 内部自动调用。\n\n---\n\n### 初始化流程\n\n用户说\"开始初始化\"时，AI 按以下步骤执行：\n\n**Step 1：向用户收集信息**\n```\nAI 依次问用户（不要一次全问，分步问）：\n第一部分（必须）：\n1. 网络学堂登录账号是什么？\n2. 登录密码是什么？\n3. 学号是什么？\n4. 姓名是什么？\n\n第二部分（选填，用户回答了必须信息后再问）：\n5. 是否需要自动标已读？（默认：否）\n6. 清理工作区前是否需要确认？（默认：是）\n7. 批量下载前是否需要确认？（默认：是）\n\n⚠️ AI 不编造示例值（不要写\"如 2025013187\"之类的数字）\n⚠️ AI 不显示学号或密码在对话中\n```\n\n**Step 2：AI 调用 `--init` 保存凭据**\n```\n1. AI 将凭据 JSON 写入临时文件（不包含在任何命令字符串中）\n2. 命令：python scripts/login_manager.py --init --cred-stdin < /tmp/cred_temp.json\n3. AI 立即删除临时文件\n输出：{\"status\":\"ok\", \"fields\":{\"learn_account\":\"...\", \"username\":\"...\", \"name\":\"...\"}}\n```\n> 使用临时文件 + stdin 重定向，密码只存在于临时文件中（随即删除），不出现在任何命令行参数或 echo 字符串中。\n\n**Step 3：AI 向用户展示 fields 内容，请用户确认**\n\n**Step 4：用户确认后，AI 调用 `--verify` 验证环境并尝试登录**\n```\n命令：python scripts/login_manager.py --verify\n输出：{\"python\":true, \"deps\":true, \"chromium\":true, \"login\":true, \"needs_2fa\":false}\n\nlogin=true       → Step 5\nneeds_2fa=true   → AI 告诉用户\"自动登录失败，请打开浏览器完成二次验证\"\n                   用户说\"好了\"后，AI 调用：python scripts/login_manager.py\n```\n\n**Step 5：AI 调用 `todos_api.py` 首次查看待办**\n```\n命令：python scripts/todos_api.py\n输出：JSON（含课程列表、待办数量等，submissions 日志自动初始化）\n```\n\n---\n\n### 日常查看待办流程\n\n任何时候用户说\"查看待办\"\"有什么作业\"\"看看通知\"时：\n\n**Step 1：AI 检查凭据和 session**\n\n跑默认命令（快速检查，<1 秒，无浏览器）：\n```\n命令：python scripts/login_manager.py\n输出：{\"initialized\": false/true, \"valid\": false/true, \"age_h\": ...}\n```\n\n根据输出决定下一步：\n\n① `initialized: false` → AI 停下来，告诉用户\"系统尚未初始化\"，走初始化流程\n② `initialized: true, valid: true` → 跳到 Step 2（获取待办）\n③ `initialized: true, valid: false` → AI 跑 `python scripts/login_manager.py --force-login`（会启动浏览器，约30-60秒）\n\n**Step 2：AI 获取待办数据**\n```\n命令：python scripts/todos_api.py\n输出：JSON 包含 courses、suggestions、graded_homeworks、cleanup_suggestion\n```\n\n**Step 3：AI 根据输出中的字段决定问用户什么**\n\n检查 `graded_homeworks` 字段：\n- 如果有内容 → AI 告诉用户\"老师批改了你的作业：[课程名][标题]，评语：[comment]，得分：[score]\"\n\n检查 `submissions_tracked` 字段：\n- 如果 > 0 → AI 告诉用户\"有 N 个已提交的作业等待老师批改，批改后我会通知你\"\n\n检查 `suggestions` 数组：\n- 如果包含 \"运行 --mark-read\" → AI 问用户\"需要把未读公告和课件标为已读吗？\"\n- 如果包含 \"运行 --cleanup-preview\" → AI 问用户\"需要看看工作区有哪些垃圾文件吗？\"\n\n检查 `suggestions` 和 `graded_homeworks` 都不存在时：\n- AI 告诉用户\"目前没有新的待办事项\"\n\n**Step 4：用户同意后，AI 自己运行对应命令**\n```\n标已读：        python scripts/todos_api.py --mark-read\n清理预览：      python scripts/ops.py --action cleanup --dry-run\n执行清理：      python scripts/ops.py --action cleanup\n```\n\n---\n\n### 提交作业流程\n\n用户说\"我要交作业\"或发来文件/图片时：\n\n**Step 1：AI 确认要交到哪个作业**\n```\n命令：python scripts/learn_api.py --action courses\n输出：课程列表（AI 找到用户说的课程，记录 wlkcid）\n\n命令：python scripts/learn_api.py --action homeworks --course <wlkcid>\n输出：作业列表（AI 找到用户说的作业，记录 xszyid）\n```\n\n**Step 2：AI 处理文件**\n\n情况 A — 用户直接发送了 PDF 文件：\n```\nAI 将文件保存到 uploads/，进入 Step 3\n```\n\n情况 B — 用户发送多张图片（如 QQ 传图）：\n```\nAI 每收到一张图，保存到 uploads/，按顺序命名为 001.jpg、002.jpg...\n用户说\"已经上传完毕\"后：\n命令：python scripts/ops.py --action pdf-merge --input-dir uploads/\n输出：merged.pdf\nAI 删除 uploads/ 下的所有图片文件\n进入 Step 3\n```\n\n**Step 3：AI 展示确认信息，获得同意后提交**\n```\n在提交前，AI 必须向用户展示：\n- 课程名称\n- 作业标题\n- 待提交的文件名\n- 提交账号（姓名+学号脱敏）\n\n例：\"准备提交 微积分(2) 的 第四次书面作业，文件：2025013187_张三.pdf。确认吗？\"\n\n用户明确说\"确认\"/\"好\"/\"提交\"后：\n命令：python scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\n功能：自动将文件重命名为 学号_姓名.pdf，提交到指定作业\n输出：{\"status\":\"ok\"}\n```\n\n**Step 4：AI 通知用户提交成功，清理临时文件**\n\n---\n\n### 下载课件流程\n\n用户说\"帮我下载课件\"时：\n\n**Step 1：AI 获取课程列表**\n```\n命令：python scripts/learn_api.py --action courses\n输出：课程列表（AI 找到用户说的课程，记录 wlkcid）\n```\n\n**Step 2：AI 下载文件**\n```\n命令：python scripts/ops.py --action download-all --course <wlkcid> --save-dir <目录>\n输出：下载结果\n```\n如果用户指定了具体文件名或模式，在 `--pattern` 参数中指定。\n\n---\n\n### 常见问题处理\n\n**Session 过期**\n```\nAI 不分析 session 文件。不检查时间戳。不自己调 login_auto.py。\n只做一件事：\n命令：python scripts/login_manager.py\n输出：session 有效则返回，无效则自动续期\n```\n\n**需要二次验证（2FA）**\n```\nlogin_manager.py 或 --verify 返回 needs_2fa=true 时：\nStep 1：AI 告诉用户\"自动登录失败，请打开浏览器完成二次验证\"\nStep 2：用户说\"好了\"后，AI 调用：\n命令：python scripts/login_manager.py\n输出：验证 session 是否已建立\n```\n\n**凭据未初始化**\n```\nlogin_manager.py --check 返回 initialized=false 时：\nAI 告诉用户\"系统尚未初始化\"，然后按初始化流程的 Step 1 开始收集信息\n```\n```\n# --verify 或 login_manager.py 返回 needs_2fa=true 时:\n# ✅ 正确做法：\n#   告诉用户\"自动登录失败，请打开浏览器完成二次验证\"\n#   等用户说\"好了\"\npython scripts/login_manager.py\n# ← 再次检查 session，应该已经有效了\n```\n\n#### 凭据未初始化怎么办？\n```\n# login_manager.py --check 返回 initialized=false 时:\n# ✅ 正确做法：\n#   告诉用户\"系统尚未初始化，请提供你的网络学堂登录账号、密码、学号、姓名\"\n#   收集信息后走初始化流程\n```\n\n---\n\n### 脚本概览\n\n| 脚本 | 职责 | 什么时候用 |\n|------|------|-----------|\n| `login_manager.py` | 认证网关 | 任何操作前确保 session；初始化；验证环境 |\n| `todos_api.py` | 日常管理 | 用户问待办、标已读、清理 |\n| `ops.py` | 文件操作 | 下载、上传、合并 PDF、清理工作区 |\n| `learn_api.py` | 数据查询 | 查课程列表、公告详情、作业内容 |\n| `pdf_merge.py` | 图片→PDF | 用户发图片要交作业时 |\n| `install_deps.py` | 依赖安装 | --verify 会自动调用，你不直接碰 |\n| `_config.py` | 配置读取 | 你不直接碰 |\n| `login_auto.py` | 无头登录 | 你不直接碰，login_manager 内部调用 |\n| `login_supervised.py` | 2FA 登录 | 你不直接碰，login_manager 内部调用 |\n| `install_playwright.py` | Chromium 安装 | --verify 会自动调用，你不直接碰 |\n\n### 日常运行流程\n\n任何时候用户问\"查看待办\"\"有什么作业\"等：\n\n```\n① 先确保 session:\n   python scripts/login_manager.py\n   ← 如果 session 过期会自动续期\n\n② 获取待办:\n   python scripts/todos_api.py\n   ← 得到 JSON {\"courses\": [...], \"total_unread\": N, \"suggestions\": [...], ...}\n\n③ 解析输出，主动问用户:\n   - suggestions 里有 \"运行 --mark-read\" → \"需要帮你把未读公告和课件标为已读吗？\"\n   - cleanup_suggestion 不为空 → \"工作区有垃圾文件，需要看看吗？\"\n   - graded_homeworks 有内容 → \"老师批了你的作业，评语是...\"\n\n④ 用户确认后，自己跑对应命令:\n   - 标已读: python scripts/todos_api.py --mark-read\n   - 清理预览: python scripts/ops.py --action cleanup --dry-run\n   - 执行清理: python scripts/ops.py --action cleanup\n```\n\n### 常见问题处理\n\n#### session 过期\n```\npython scripts/login_manager.py\n← session 失效时会自动续期，无需特殊处理\n```\n\n#### 需要二次验证 (2FA)\n```\n# --verify 返回 needs_2fa=true 时:\n告诉用户\"自动登录失败，请打开浏览器完成二次验证\"\n等用户说\"好了\"后:\npython scripts/login_manager.py\n← 验证 session\n```\n\n#### 凭据未初始化\n```\n# login_manager.py 返回 code=3:\n告诉用户\"系统尚未初始化，请提供你的网络学堂登录账号、密码、学号、姓名\"\n→ 走初始化流程\n```\n\n### 详细 CLI 命令\n\n#### login_manager.py\n\n```bash\npython scripts/login_manager.py                                # 确保 session\npython scripts/login_manager.py --check                        # 检查状态\npython scripts/login_manager.py --init --learn-account=... --password=... --username=... --name=...\npython scripts/login_manager.py --reset                        # 清空所有数据\npython scripts/login_manager.py --verify                       # 验证环境（依赖+Chromium+登录）\n```\n\n退出码: 0=有效, 1=错误, 2=需人工 2FA, 3=未初始化\n\n#### learn_api.py\n\n```bash\npython scripts/learn_api.py --action courses                               # 课程列表\npython scripts/learn_api.py --action announcements --course <wlkcid>       # 公告列表\npython scripts/learn_api.py --action announcement-detail --course <wlkcid> --id <ggid>  # 公告详细内容\npython scripts/learn_api.py --action files --course <wlkcid>               # 课件列表\npython scripts/learn_api.py --action homeworks --course <wlkcid>           # 作业列表\npython scripts/learn_api.py --action homework-full --course <wlkcid> --id <zyid> --xszyid <xszyid>  # 作业完整信息（含评语）\npython scripts/learn_api.py --action discussions --course <wlkcid>         # 讨论列表\npython scripts/learn_api.py --action questionnaires --course <wlkcid>      # 问卷列表\npython scripts/learn_api.py --action aggregated --course <wlkcid>          # 聚合查询\npython scripts/learn_api.py --action mark-all-announcements-read --course <wlkcid>\npython scripts/learn_api.py --action mark-all-files-read --course <wlkcid>\n```\n\n#### ops.py\n\n```bash\npython scripts/ops.py --action list-files --course <wlkcid> [--pattern \"*.pdf\"]\npython scripts/ops.py --action download --course <wlkcid> --wjid <wjid>\npyth\n\nArchive v1.0.8: 16 files, 41937 bytes\n\nFiles: AGENTS.md (1975b), config.json (102b), credentials.json (77b), scripts/_config.py (6203b), scripts/install_deps.py (2207b), scripts/install_playwright.py (984b), scripts/learn_api.py (28555b), scripts/login_auto.py (5076b), scripts/login_manager.py (11918b), scripts/login_supervised.py (4842b), scripts/ops.py (12708b), scripts/pdf_merge.py (2569b), scripts/todos_api.py (6365b), scripts/todos_dom.py (6534b), SKILL.md (25628b), _meta.json (133b)\n\nArchive v1.0.7: 16 files, 41789 bytes\n\nFiles: AGENTS.md (1975b), config.json (102b), credentials.json (77b), scripts/_config.py (6203b), scripts/install_deps.py (2207b), scripts/install_playwright.py (984b), scripts/learn_api.py (28555b), scripts/login_auto.py (5076b), scripts/login_manager.py (11918b), scripts/login_supervised.py (4842b), scripts/ops.py (12708b), scripts/pdf_merge.py (2569b), scripts/todos_api.py (6365b), scripts/todos_dom.py (6534b), SKILL.md (25228b), _meta.json (133b)","readmeExcerpt":"Skill: 清华网络学堂 Owner: tomuiv Summary: 清华网络学堂（learn.tsinghua.edu.cn）自动化。要求用户提供网络学堂登录密码（本地 DPAPI 加密存储），自动登录、查看待办、下载课件、提交作业、批量标记已读。当需要操作网络学堂完成日常学习任务时使用。 Tags: latest:1.0.17 Version history: v1.0.17 | 2026-05-09T12:36:25.094Z | user auto_mark_read=true now auto-executes mark-read (not just suggest) v1.0.16 | 2026-05-03T03:26:17.036Z | user auto_mark_read=true skips --confirm (no prompt), plain todo check never auto-marks ","codeSnippets":[],"executableExamples":[{"language":"text","snippet":"AI 依次问用户（不要一次全问，分步问）：\n第一部分（必须）：\n1. 网络学堂登录账号是什么？\n2. 登录密码是什么？\n3. 学号是什么？\n4. 姓名是什么？\n\n第二部分（选填，用户回答了必须信息后再问）：\n5. 是否需要自动标已读？（默认：是）\n6. 清理工作区前是否需要确认？（默认：是）\n7. 批量下载前是否需要确认？（默认：是）\n\n⚠️ AI 不编造示例值（不要写\"如 2025013187\"之类的数字）\n⚠️ AI 不显示学号或密码在对话中"},{"language":"text","snippet":"1. AI 将凭据 JSON 写入临时文件（不包含在任何命令字符串中）\n2. 命令：python scripts/login_manager.py --init --cred-stdin < /tmp/cred_temp.json\n3. AI 立即删除临时文件\n输出：{\"status\":\"ok\", \"fields\":{\"learn_account\":\"...\", \"username\":\"...\", \"name\":\"...\"}}"},{"language":"text","snippet":"命令：python scripts/login_manager.py --verify\n输出：{\"python\":true, \"deps\":true, \"chromium\":true, \"login\":true, \"needs_2fa\":false}\n\nlogin=true       → Step 5\nneeds_2fa=true   → AI 告诉用户\"自动登录失败，请打开浏览器完成二次验证\"\n                   用户说\"好了\"后，AI 调用：python scripts/login_manager.py"},{"language":"text","snippet":"命令：python scripts/todos_api.py\n输出：JSON（含课程列表、待办数量等，submissions 日志自动初始化）"},{"language":"text","snippet":"命令：python scripts/login_manager.py\n输出：{\"initialized\": false/true, \"valid\": false/true, \"age_h\": ...}"},{"language":"text","snippet":"命令：python scripts/todos_api.py\n输出：JSON 包含 courses、suggestions、graded_homeworks、cleanup_suggestion"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: tsinghua-learn\ndescription: 清华网络学堂（learn.tsinghua.edu.cn）自动化。要求用户提供网络学堂登录密码（本地 DPAPI 加密存储），自动登录、查看待办、下载课件、提交作业、批量标记已读。当需要操作网络学堂完成日常学习任务时使用。\nmetadata:\n  openclaw:\n    requires:\n      env:\n        - CAS_PASSWORD\n    primaryEnv: CAS_PASSWORD\n    os:\n      - windows\n---\n\n# tsinghua-learn skill\n\n清华网络学堂（learn.tsinghua.edu.cn）自动化 Skill。\n\n---\n\n## 如果你是人类，请阅读以下内容\n\n### 前置要求\n\n使用前请确保已安装 **Python 3.10+** 或 **Conda**（推荐 Python 3.11 或 3.12）。建议安装 **7-Zip** 并添加到环境变量 PATH 中，方便后续处理压缩文件。\n\n初始化时会自动安装 Python 依赖（requests、playwright、pillow 等）以及 Chromium 浏览器（~150MB），无需手动操作。\n\n如果没有安装上述环境，请先安装后再对 AI 说 **\"开始初始化\"**。\n\n### 如何与 AI 沟通\n\n你只需要像聊天一样告诉 AI 你想做什么，AI 会帮你完成所有操作。\n\n#### 初始化\n\n对 AI 说：**\"开始初始化\"**\n\nAI 会依次：\n1. 询问你的网络学堂登录账号、密码、学号、姓名\n2. 检查 Python 环境并自动安装所需依赖\n3. 安装 Chromium 浏览器\n4. 尝试登录网络学堂\n5. 登录成功后询问你的偏好设置（是否自动标已读等）\n6. 完成后即可正常使用\n\n> **四个字段说明：**\n> - **网络学堂登录账号**（`--learn-account`）— 用于登录 learn.tsinghua.edu.cn 的账号，可能和学号不同\n> - **登录密码**（`--password`）— 网络学堂登录密码，仅发送至 id.tsinghua.edu.cn（CAS）和 learn.tsinghua.edu.cn\n> - **学号**（`--username`）— 你的学生证号，如 2025013187\n> - **姓名**（`--name`）— 你的真实姓名\n\n#### 查看待办\n\n对 AI 说：**\"查看待办\"** 或 **\"看看有什么作业\"**\n\nAI 会列出所有课程的最新动态：\n- 未交作业及截止日期\n- 未读公告和未浏览课件\n- 已提交但老师还没批的作业（老师批改后会自动汇报评语和分数）\n- 工作区缓存大小\n\nAI 还会主动问你是否需要标已读、清理缓存。\n\n> 你也可以设置每日自动查看：将 `todos_api.py` 配置为定时任务即可。\n\n#### 提交作业\n\n**方式一：直接发送 PDF 文件**\n\n对 AI 说：**\"我要交某某课程的作业\"**，然后发送 PDF 文件即可。\n\n**方式二：发送多张图片合并为 PDF**\n\n先说：**\"我要交某某课程的作业，我发几张图片给你\"**\n\n然后**按顺序**发送图片（先发的会在最终 PDF 的第一页，后发的在后面）。最后说：**\"已经上传完毕\"**\n\nAI 会先列出要提交的课程、作业标题、文件名，请你确认。确认后自动：\n1. 按你发送的顺序命名图片（001、002...）\n2. 合并为 PDF（按文件名排序，所以先发的在 PDF 前面）\n3. 重命名为 `学号_姓名.pdf`\n4. 提交到指定作业\n\n#### 下载课件\n\n对 AI 说：**\"帮我下载微积分的课件\"**\n\nAI 会列出课程的全部课件文件。你也可以更精确地指定：\n\n- **\"下载未读课件\"** — 只下载还没看过的课件\n- **\"下载概率论第 8 讲的课件\"** — 下载指定名称的文件\n- **\"把微积分的课件全部下载到 D 盘\"** — 批量下载到指定目录\n\nAI 会在下载前列出文件清单让你确认。\n\n#### 查看通知和作业详情\n\n对 AI 说：\n\n- **\"看看微积分最近有什么通知\"** — 查看指定课程的公告\n- **\"概率论 HW10 的作业内容是什么\"** — 查看作业的题目和附件\n- **\"上次交的物理作业老师批了吗\"** — 查看已批改作业的评语和分数\n- **\"看看英语课有什么讨论\"** — 查看课程讨论区\n\n#### 清理工作区\n\nAI 在查看待办时会主动提醒你工作区有缓存。这些缓存是下载的课件、合并 PDF 时的临时图片、调试日志等文件，不影响功能但会占用磁盘空间。\n\n只需说 **\"好\"** 或 **\"清理吧\"**，AI 会先列出待清理的文件让你确认，确认后自动删除。\n\n#### 其他\n\n如果有这里没列出的需求，直接告诉 AI 你想做什么即可。AI 会根据现有功能尽力帮你完成。\n\n#### 数据与安全\n\n本 Skill 会在本地存储以下数据：\n\n| 数据 | 位置 | 说明 |\n|------|------|------|\n| 登录凭据 | `credentials.json` | Windows DPAPI 加密（绑定当前用户账户，仅 Windows 支持） |\n| 浏览器登录状态 | `profiles/learn_profile/` | Chrome Profile 含 cookies，避免每次触发 2FA |\n| Session 令牌 | `sessions/learn_session.json` | JSESSIONID + CSRF Token |\n\n如需清除所有数据，对 AI 说 **\"重置所有数据\"**，AI 会清空以上目录内容并恢复到初始状态。\n\n> 这些数据仅存储在本地计算机，不会上传到任何云端。网络学堂本身不支持注销 API，如需完全登出请手动在浏览器中操作。\n\n#### 修改配置\n\n你可以让 AI 帮你修改以下设置，只需要说\"帮我改一下配置\"或\"设置自动标已读\"即可：\n\n| 配置项 | 说明 | 默认值 |\n|--------|------|--------|\n| `auto_mark_read` | 查看待办时是否自动标已读 | 开启 |\n| `confirm_before_cleanup` | 清理缓存前是否需要你确认 | 需要 |\n| `confirm_before_download` | 批量下载前是否需要你确认 | 需要 |\n\n例如：\n- **\"以后自动标已读吧\"** → AI 会开启 `auto_mark_read`\n- **\"清理的时候不用问我了\"** → AI 会把 `confirm_before_cleanup` 设为 false\n\n---\n\n> **当前版本为测试版本，建议同学们完成提交作业等关键操作后自行登录网络学堂查看是否有错误。**\n\n了解更多信息见 GitHub：[github.com/TOMUIV/tsinghua-learn](https://githu"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn73vfhykr10snz07772njvdmx84mcb6\",\n  \"slug\": \"tsinghua-learn\",\n  \"version\": \"1.0.17\",\n  \"publishedAt\": 1778330185094\n}"},{"path":"AGENTS.md","content":"# tsinghua-learn — AGENTS.md\n\n## 架构\n\n6 个脚本：\n\n| 脚本 | 职责 |\n|------|------|\n| `_config.py` | 路径/配置/凭证、DPAPI 解密 |\n| `login_manager.py` | 中央认证网关（默认 fast check，`--force-login` 完整登录） |\n| `learn_api.py` | HTTP API（课程/公告/课件/作业/讨论/问卷/聚合查询） |\n| `ops.py` | 文件操作（下载/批量下载/上传/移入/清理） |\n| `todos_api.py` | 日常管理（代办汇总/标已读/清理，JSON 给 AI） |\n| `pdf_merge.py` | 图片→PDF |\n\n登录脚本不直接调用：\n| `login_auto.py` | headless 自动登录 |\n| `login_supervised.py` | 2FA 登录 |\n\n## 核心命令\n\n```bash\npython scripts/login_manager.py                        # 快速检查 session (<1s)\npython scripts/login_manager.py --init                 # 初始化凭据\npython scripts/login_manager.py --verify               # 验证环境+登录\npython scripts/login_manager.py --reset                # 清空所有数据\npython scripts/login_manager.py --force-login          # 强制完整登录\n\npython scripts/todos_api.py                            # 代办汇总\npython scripts/todos_api.py --mark-read                # + 标已读\npython scripts/todos_api.py --cleanup                  # + 清理\n\npython scripts/ops.py --action list-files --course <wlkcid>\npython scripts/ops.py --action download --course <wlkcid> --wjid <wjid>\npython scripts/ops.py --action download-all --course <wlkcid>\npython scripts/ops.py --action upload --course <wlkcid> --xszyid <xszyid> --file <path>\npython scripts/ops.py --action pdf-merge --input-dir <目录>\npython scripts/ops.py --action cleanup --dry-run\npython scripts/ops.py --action cleanup\n```\n\n## 关键约束\n\n- paths/creds 全走 `_config.py` getter\n- `credentials.json` DPAPI 加密\n- 学期从 API 自动检测\n- 课程从 API 动态获取\n- 不创建新脚本\n- 脚本不阻塞（`input()`/`getpass()` 禁用）\n- 提交作业自动重命名：`学号_姓名.ext`"},{"path":"CHANGELOG.md","content":"# tsinghua-learn — Release History\n\n## v1.0.16 (2026-05-03)\n- `auto_mark_read=true` 时 `--mark-read` 跳过 `--confirm`，但纯查待办永不自动标\n- `login_supervised.py`：先 headless，仅 2FA 时弹浏览器\n- `install_playwright.py`：内置 npmmirror.com 镜像，解决国内下载超时\n- 删除 `todos_dom.py`（DOM 备用，已被 `todos_api.py` 替代）\n- 3 个安全扫描全部 Staff Cleared（ClawHub #1984 申诉通过）\n\n## v1.0.15 (2026-05-03)\n- `todos_api.py --mark-read` 加 `--confirm` 脚本级强制确认\n- `auto_mark_read` 不再自动执行，改为 suggestions 提示 AI 加 `--confirm`\n- ClawScan: `auto_mark_read` Concern → Note\n\n## v1.0.14 (2026-05-03)\n- `login_supervised.py` 重写：先 headless，检测到 2FA 再弹浏览器\n- 验证 `chromium-1169` ↔ `playwright==1.52.0` 版本对齐\n\n## v1.0.13 (2026-05-03)\n- `login_manager.py --reset` 默认 `auto_mark_read: true`（匹配 `config.json`）\n\n## v1.0.12 (2026-05-03)\n- 2FA 提示改 plain text（去 ASCII art）\n- fingerPrint 文件缺失时不崩溃\n- `auto_mark_read` 默认值改为 `true`\n- ClawScan 从 5 个 findings 降到 4 个\n\n## v1.0.10 (2026-05-02)\n- 前置换页改用 `primaryEnv` + `os: [windows]`（ClawHub 识别）\n- `ops.py upload` 加 `--confirm` 脚本级强制确认（无 `--confirm` 返回 pending）\n- ClawScan: `submit_homework` Concern → Note；static analysis → Benign\n\n## v1.0.9 (2026-05-02)\n- 前置换页加 `credentialScope`、窄化密码描述（去\"清华信息门户也用同一密码\"）\n\n## v1.0.8 (2026-05-02)\n- SKILL.md 加 YAML 前置换页，声明 `primaryCredential: cas_password`\n\n## v1.0.7 (2026-05-02)\n- `install_deps.py` 依赖改为精确 `==` pin（不再 `>=`）\n- 密码/用户名掩码改为全 `***`（不再泄露前 N 字符）\n- stdin 方式从 `echo | pipe` 改为临时文件重定向\n- 铁律 0 加例外条款（用户知情披露）\n- 去掉 `ignore_https_errors=True` 全部 3 处（`login_auto.py`、`login_supervised.py`、`todos_dom.py`）\n- 去掉非 Windows base64 fallback——仅支持 DPAPI\n- ClawScan 从 7 个 findings 降到 4 个\n\n## v1.0.6 (2026-05-02)\n- 凭据存储从 base64 编码改为 Windows DPAPI（`CryptProtectData`）\n- 修复 DPAPI 加密中文文本的字节长度 bug\n- ClawScan: static analysis → Benign\n\n## v1.0.5 (2026-05-02)\n- 修复 `config.json` `auto_mark_read: true` → `false`\n- 修改铁律 5 措辞（密码仅在初始化时收集）\n\n## v1.0.3 (2026-05-02)\n- 全面启用 TLS 验证：去掉 `learn_api.py`、`login_manager.py` 等 6 个脚本的 `verify=False`\n- 新增 `--cred-stdin`：凭据通过 stdin JSON 传入，避免密码上命令行\n- `install_deps.py` 依赖 pin 最低版本号\n- SKILL.md 新增铁律 6（提交确认）+ \"数据与安全\"章节\n- 清理 44 个 `_test_*` 调试脚本\n- ClawScan 从 21+ 降到 6 个 findings\n\n## v1.0.2 (2026-05-02)\n- ClawHub 显示名从 \"Clawhub\" 改为 \"清华网络学堂\"\n- 请求安全重扫\n\n## v1.0.1 (2026-05-02)\n- ClawHub 首次发布\n\n## v1.0.0 之前\n- 从 30+ 个散乱脚本重构为 10 个核心脚本\n- 新建 `login_manager.py`（中央认证网关）\n- 新建 `ops.py`（文件操作统一入口）\n- 修复 `learn_api.py` 多个 API 端点\n- 学期从 API 自动检测\n- 文件名乱码修复（latin-1 → UTF-8）\n- 提交日志机制（`submissions_log.json`）\n- SKILL.md 分人类 / AI 两部分\n- 创建三版发布（clawhub / github-full / github-cas）"},{"path":"skill-card.md","content":"## Description:\n\n清华网络学堂（learn.tsinghua.edu.cn）自动化。要求用户提供网络学堂登录密码（本地 DPAPI 加密存储），自动登录、查看待办、下载课件、提交作业、批量标记已读。当需要操作网络学堂完成日常学习任务时使用。\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[tomuiv](https://clawhub.ai/user/tomuiv)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nStudents and agents use this skill to automate routine Tsinghua Learn tasks such as login, todo review, courseware download, homework submission, PDF preparation, cleanup, and marking notices or files as read.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill requires a Tsinghua Learn login and stores local credentials, browser profile data, and session files.\n\nMitigation: Install only if the publisher is trusted, protect the local account and skill directory, and clear stored account state when the skill is no longer needed.\n\nRisk: The skill can change account state, including marking notices or files as read during todo checks.\n\nMitigation: Disable automatic mark-read unless that behavior is intended, and review account-changing actions before allowing the agent to execute them.\n\nRisk: Plaintext temporary credential workflows can expose account secrets.\n\nMitigation: Avoid workflows that place credentials in plaintext temporary files and remove any temporary credential material immediately after initialization.\n\n## Reference(s):\n\n- [ClawHub Skill Page](https://clawhub.ai/tomuiv/skills/tsinghua-learn)\n- [Tsinghua Learn](https://learn.tsinghua.edu.cn)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, code, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown responses with JSON-backed status summaries, shell command execution, configuration updates, and generated or downloaded files]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires Windows and CAS_PASSWORD; stores local login, browser profile, and session state for Tsinghua Learn automation.]\n\n## Skill Version(s):\n\n1.0.17 (source: server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"清华网络学堂（learn.tsinghua.edu.cn）自动化。要求用户提供网络学堂登录密码（本地 DPAPI 加密存储），自动登录、查看待办、下载课件、提交作业、批量标记已读。当需要操作网络学堂完成日常学习任务时使用。 Skill: 清华网络学堂 Owner: tomuiv Summary: 清华网络学堂（learn.tsinghua.edu.cn）自动化。要求用户提供网络学堂登录密码（本地 DPAPI 加密存储），自动登录、查看待办、下载课件、提交作业、批量标记已读。当需要操作网络学堂完成日常学习任务时使用。 Tags: latest:1.0.17 Version history: v1.0.17 | 2026-05-09T12:36:25.094Z | user auto_mark_read=true now auto-executes mark-read (not just suggest) v1.0.16 | 2026-05-03T03:26:17.036Z | user auto_mark_read=true skips --confirm (no prompt), plain todo check never auto-marks","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1168,"uniquenessScore":47,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-11T19:16:37.343Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-11T19:16:37.343Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-12T05:16:25.516Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}