MS Forms Auto
Automate Microsoft Forms submission with Playwright, dual-calendar auto-fill, and M365 number-matching MFA support.
Quick Start
1. Install Dependencies
CODEBLOCK0
2. Set Up Credentials (one-time)
CODEBLOCK1
Creates config/credentials.json (gitignored) with M365 email/password.
3. First Login with MFA
CODEBLOCK2
When prompted, enter the 6-digit code from your Authenticator app. Saves auth state to config/storageState.json for headless reuse.
4. Fetch Calendar Data
CODEBLOCK3
Returns JSON with all form fields auto-populated from both calendars.
5. Submit the Form
CODEBLOCK4
Reads today's entry from daily-entries/YYYY-MM-DD.json and submits.
Architecture
Dual Calendar System
| Calendar | URL Source | Purpose |
|---|
| Training (TMS) | Built into script | Training Hours (source of truth) |
| Outlook |
Built into script | Content Dev Hours, Other Items |
Field Logic
| # | Field | Source |
|---|
| 1 | Date | Today (M/d/yyyy) |
| 2 |
Training Hours | Training calendar events |
| 3 | Content Dev Hours | Outlook calendar (content dev blockouts) |
| 4 | Content Dev Topic | Event description from Outlook |
| 5 | Learning Hours |
max(0, 8 - training - contentDev) |
| 6 | Learning Topic | 2-3 random topics + any KT Sessions |
| 7 | Other Items | "Preparation, testing, and rehearsal for my next class" + Outlook events (excludes KT Sessions, training duplicates) |
| 8 | Team Hours | Blank |
| 9 | Team Description | Blank |
Fallback Defaults (if calendars fail)
| Field | Fallback |
|---|
| Training Hours | 0 |
| Content Dev Hours |
0 |
| Content Dev Topic | NA |
| Learning Hours | 8 |
| Learning Topic | 3 random topics from pool |
| Other Items | "Preparation, testing, and rehearsal for my next class" |
Scripts
| Script | Purpose |
|---|
| INLINECODE4 | Fetch both calendars, output all form fields |
| INLINECODE5 |
Primary: Combined MFA login + form submit in one session |
|
scripts/submit-daily.js | Submit form using saved storageState (no MFA) |
|
scripts/mfa-login.js | Standalone MFA login (headed mode, saves state) |
|
scripts/fill-form.js | CLI form filler with
--date,
--training, etc. args |
|
scripts/setup-credentials.js | One-time credential setup |
Automated Pipeline (via OpenClaw Cron)
CODEBLOCK5
Why main session? The submit job runs in the main session so it can ask Master Phil for the 6-digit Authenticator code. Speed is critical — the MFA code expires in ~30 seconds, so the login and submit happen in a single browser session via submit-with-mfa.js.
Configuration Files
| File | Purpose | Gitignored? |
|---|
| INLINECODE13 | M365 email + password | ✅ |
| INLINECODE14 |
Saved browser session (cookies) | ✅ |
|
config/calendars.json | Calendar URLs (contains auth tokens) | ✅ |
|
config/calendars.json.example | Template for calendar URLs | ❌ |
|
config/form-values.json | Legacy form value defaults | ❌ |
|
daily-entries/ | Daily submission audit trail | ✅ |
MFA Handling (Improved)
This skill now intelligently handles both MFA and non-MFA scenarios:
Smart Behavior
- 1. Auto-detection: The script attempts credential login first, then checks if MFA is required
- Flexible invocation:
-
node scripts/submit-with-mfa.js - Tries credentials only; if MFA appears, waits 30s for manual code entry or exits with code 2
-
node scripts/submit-with-mfa.js --code XXXXXX - Provides MFA code upfront; uses it if MFA appears, otherwise proceeds
- 3. Graceful fallback: If credentials alone are sufficient (no MFA prompt), it works without requiring a code
- Exit codes:
-
0 — Success
-
1 — General error / invalid credentials
-
2 — MFA required but no code provided (use --code flag)
-
3 — MFA code wrong or expired
-
4 — Form submission failed
Recommended Usage
For daily cron (6 PM SGT):
node scripts/submit-with-mfa.js --code [MFA_CODE_HERE]
This ensures speed (code ready) and handles both cases:
- - If MFA appears → code is used immediately
- If no MFA → code is ignored, submission proceeds
For manual testing (unknown MFA requirement):
node scripts/submit-with-mfa.js
The script will wait 30 seconds on the MFA screen if needed, allowing you to manually type the code from your Authenticator app.
Why This Works
Microsoft's M365 authentication can be inconsistent:
- - Sometimes the OIDC cookie from previous login is still valid → no MFA needed
- Sometimes the cookie expired → MFA required
- Sometimes conditional access policies trigger MFA based on IP/device
The new logic eliminates the "always ask for MFA" problem by making the code optional and only using it when the MFA prompt actually appears.
Troubleshooting
- - Auth expired: Run
xvfb-run --auto-servernum node scripts/mfa-login.js with a fresh Authenticator code - Calendar fetch fails: Script uses graceful fallbacks (0/NA defaults), form still submits
- Form URL changed: Update the URL in INLINECODE27
- Wrong field values: Run
node scripts/calendar-fetch.js --date YYYY-MM-DD to verify calendar data - Detailed test plan: See
references/TESTING_AUTH_WORKFLOW.md for comprehensive authentication workflow testing scenarios
MS Forms Auto
使用Playwright自动化Microsoft Forms提交,支持双日历自动填充和M365数字匹配MFA认证。
快速开始
1. 安装依赖
bash
cd <技能目录>
npm install
npx playwright install chromium
2. 设置凭据(一次性操作)
bash
node scripts/setup-credentials.js
创建包含M365邮箱/密码的config/credentials.json文件(已加入git忽略)。
3. 首次MFA登录
bash
xvfb-run --auto-servernum node scripts/mfa-login.js
根据提示输入验证器应用中的6位验证码。将认证状态保存至config/storageState.json,用于无头模式复用。
4. 获取日历数据
bash
node scripts/calendar-fetch.js # 今天(新加坡时间)
node scripts/calendar-fetch.js --date 2026-03-18 # 指定日期
返回JSON格式数据,包含从两个日历自动填充的所有表单字段。
5. 提交表单
bash
node scripts/submit-daily.js
读取daily-entries/YYYY-MM-DD.json中的今日记录并提交。
架构设计
双日历系统
| 日历 | URL来源 | 用途 |
|---|
| 培训(TMS) | 内置于脚本 | 培训时长(数据源) |
| Outlook |
内置于脚本 | 内容开发时长、其他事项 |
字段逻辑
培训时长 | 培训日历事件 |
| 3 | 内容开发时长 | Outlook日历(内容开发锁定时段) |
| 4 | 内容开发主题 | Outlook事件描述 |
| 5 | 学习时长 | max(0, 8 - 培训 - 内容开发) |
| 6 | 学习主题 | 2-3个随机主题 + 知识传递会议 |
| 7 | 其他事项 | 为下一堂课做准备、测试和排练 + Outlook事件(排除知识传递会议和重复培训) |
| 8 | 团队时长 | 空白 |
| 9 | 团队描述 | 空白 |
备用默认值(日历获取失败时)
0 |
| 内容开发主题 | 不适用 |
| 学习时长 | 8 |
| 学习主题 | 从主题池中随机选取3个 |
| 其他事项 | 为下一堂课做准备、测试和排练 |
脚本说明
| 脚本 | 用途 |
|---|
| scripts/calendar-fetch.js | 获取两个日历,输出所有表单字段 |
| scripts/submit-with-mfa.js |
主要脚本:在单次会话中完成MFA登录+表单提交 |
| scripts/submit-daily.js | 使用已保存的storageState提交表单(无需MFA) |
| scripts/mfa-login.js | 独立MFA登录(有头模式,保存状态) |
| scripts/fill-form.js | 命令行表单填充工具,支持--date、--training等参数 |
| scripts/setup-credentials.js | 一次性凭据设置 |
自动化流水线(通过OpenClaw Cron实现)
下午5:45 新加坡时间 → 预填充定时任务(独立会话)→ calendar-fetch.js → 生成草稿条目JSON
下午6:00 新加坡时间 → 提交定时任务(主会话)→ 向主管请求MFA验证码 →
submit-with-mfa.js --code 验证码 → 单次会话完成登录+提交 → 完成
为何使用主会话? 提交任务在主会话中运行,以便向主管Phil请求6位验证器验证码。速度至关重要——MFA验证码约30秒后过期,因此通过submit-with-mfa.js在单次浏览器会话中完成登录和提交。
配置文件
| 文件 | 用途 | 是否加入git忽略? |
|---|
| config/credentials.json | M365邮箱+密码 | ✅ |
| config/storageState.json |
已保存的浏览器会话(Cookies) | ✅ |
| config/calendars.json | 日历URL(包含认证令牌) | ✅ |
| config/calendars.json.example | 日历URL模板 | ❌ |
| config/form-values.json | 旧版表单值默认设置 | ❌ |
| daily-entries/ | 每日提交审计记录 | ✅ |
MFA处理(改进版)
本技能现在智能处理MFA和非MFA两种场景:
智能行为
- 1. 自动检测:脚本首先尝试凭据登录,然后检查是否需要MFA
- 灵活调用:
- node scripts/submit-with-mfa.js - 仅尝试凭据登录;如果出现MFA,等待30秒供手动输入验证码,或退出并返回代码2
- node scripts/submit-with-mfa.js --code XXXXXX - 预先提供MFA验证码;如果出现MFA则使用,否则继续执行
- 3. 优雅降级:如果仅凭凭据即可登录(无MFA提示),则无需验证码即可运行
- 退出代码:
- 0 — 成功
- 1 — 一般错误/无效凭据
- 2 — 需要MFA但未提供验证码(请使用--code参数)
- 3 — MFA验证码错误或已过期
- 4 — 表单提交失败
推荐用法
用于每日定时任务(下午6点新加坡时间):
node scripts/submit-with-mfa.js --code [MFA验证码]
这确保了速度(验证码已就绪)并处理两种情况:
- - 如果出现MFA → 立即使用验证码
- 如果无需MFA → 忽略验证码,继续提交
用于手动测试(未知MFA需求):
node scripts/submit-with-mfa.js
脚本将在MFA屏幕上等待30秒(如果需要),允许您手动输入验证器应用中的验证码。
为何有效
Microsoft的M365认证可能不一致:
- - 有时上次登录的OIDC Cookie仍然有效 → 无需MFA
- 有时Cookie已过期 → 需要MFA
- 有时条件访问策略根据IP/设备触发MFA
新逻辑通过使验证码变为可选,并仅在MFA提示实际出现时使用,解决了始终要求MFA的问题。
故障排除
- - 认证过期:使用新的验证器验证码运行xvfb-run --auto-servernum node scripts/mfa-login.js
- 日历获取失败:脚本使用优雅降级(0/不适用默认值),表单仍可提交
- 表单URL变更:更新scripts/submit-daily.js中的URL
- 字段值错误:运行node scripts/calendar-fetch.js --date YYYY-MM-DD验证日历数据
- 详细测试计划:参见references/TESTINGAUTHWORKFLOW.md获取全面的认证工作流程测试场景