feat(outlook): Microsoft Graph SDK integration — auto-detect interview invites from Outlook + Teams#666
feat(outlook): Microsoft Graph SDK integration — auto-detect interview invites from Outlook + Teams#666Schlaflied wants to merge 2 commits into
Conversation
…w invites from Outlook + Teams Closes santifer#664 - scan-outlook.mjs: zero-LLM interview invite scanner via Microsoft Graph REST API - Covers personal outlook.com and enterprise Azure AD tenants (Fortune 500) - Zero extra npm dependencies — Node.js 18+ native fetch only - OAuth2 flow on port 3001 (no conflict with Google Calendar on 3000) - Extracts: company, role, date/time, Teams/Zoom meeting link from email - Auto-updates applications.md status to Interview on detection - Auto-creates interview prep file skeleton in interview-prep/ - Dedup by company+role — safe to run multiple times - --dry-run, --days N, --auth flags consistent with scan.mjs conventions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughNew CLI script ChangesOutlook Interview Detection
sequenceDiagram
participant User as CLI User
participant Script as scan-outlook.mjs
participant AAD as Azure AD
participant Graph as Microsoft Graph API
participant Files as File System (tracker, prep, history)
User->>Script: node scan-outlook.mjs --auth
Script->>AAD: Start local OAuth callback on :3001
AAD-->>User: Redirect to Azure AD login
User->>AAD: Authenticate with credentials
AAD-->>Script: Authorization code to :3001
Script->>AAD: Exchange code for access token
AAD-->>Script: access_token + refresh_token
Script->>Files: Persist token to calendar/ms-token.json
User->>Script: node scan-outlook.mjs --days 7
Script->>Script: Load token from cache
Script->>Graph: GET /me/messages?filter=receivedDateTime gt now-7d AND subject contains interview keywords
Graph-->>Script: Message list (sender, subject, body preview)
Script->>Script: Extract metadata (company, role, datetime, link)
Script->>Script: Dedup against applications.md
Script->>Files: Update data/applications.md status
Script->>Files: Create interview-prep/{company}-{role}.md
Script->>Files: Append to data/scan-history.tsv
Script-->>User: Print summary (found, duplicates, scanned)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scan-outlook.mjs`:
- Around line 49-50: The DAYS parsing using daysArg/process.argv and parseInt
can produce NaN for invalid input; update the logic around daysArg and DAYS so
that after parsing parseInt(process.argv[daysArg + 1]) you validate the result
(Number.isFinite or Number.isNaN check and ensure an integer > 0) and if invalid
fall back to the default 7 (and optionally emit a console.warn explaining the
bad value). Make the change where DAYS is defined so DAYS is always a valid
positive integer before any date arithmetic.
- Line 118: The execSync call using interpolated authUrl creates a
command-injection risk; replace the execSync(`start "" "${authUrl}"`) pattern
(symbol: execSync and variable authUrl) with a safe launcher that avoids shell
interpolation—use child_process.spawn (or cross-platform open method) with
shell:false and pass the URL as an argument (or use platform-specific APIs like
open/xdg-open/Start-Process equivalents) so the URL is not interpreted by a
shell; ensure you handle Windows "start" separately by invoking the executable
with args instead of constructing a shell string and catch/log errors.
- Around line 412-416: The code writes to SCAN_HISTORY_PATH without ensuring its
parent directory exists; before calling writeFileSync/appendFileSync in the
block guarded by DRY_RUN and historyRows.length, create the directory for
SCAN_HISTORY_PATH (use path.dirname(SCAN_HISTORY_PATH)) and call
fs.mkdirSync(..., { recursive: true }) if it does not exist so writeFileSync and
appendFileSync won't throw ENOENT; update the block around
existsSync/appendFileSync to first ensure the directory exists, then proceed
with writing the header and appending historyRows.
- Around line 280-291: The YAML frontmatter template in the content string uses
unquoted values for company and role which can break when those variables
contain YAML-special characters; update the template in scan-outlook.mjs (the
content constant construction) to properly escape or wrap ${company} and ${role}
in safe YAML strings (for example, always quote them or run them through an
escaping function) so the title, company and role fields produce valid YAML
regardless of characters; modify the code that builds content (the content
constant) to apply the escaping/quoting function to company and role before
interpolation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 651e1526-08d3-4f6a-ae8f-9b494f9c963f
📒 Files selected for processing (1)
scan-outlook.mjs
- Validate --days argument: NaN/invalid values fall back to default 7 - Replace execSync shell interpolation with spawn shell:false (command injection fix) - Create parent directory before writing scan-history.tsv (ENOENT fix) - Quote company/role values in YAML frontmatter (special character safety) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Closes #664
Summary
Add Microsoft Graph API integration so career-ops can automatically detect interview invitations from Outlook email (personal and enterprise), extract structured data, update the tracker, create interview prep files, and add calendar events — without manual intervention.
Parallel to #660 (Gmail/Google SDK), but targeting Outlook/Exchange users. Most Fortune 500 recruiters (Equinix, EY, Deloitte, etc.) send interview invites via Outlook — this closes the gap for enterprise job seekers.
What this adds
`scan-outlook.mjs` — zero-LLM interview invite scanner via Microsoft Graph REST API
```
node scan-outlook.mjs # scan last 7 days
node scan-outlook.mjs --dry-run # preview without writing files
node scan-outlook.mjs --days 14 # scan last N days
node scan-outlook.mjs --auth # re-run OAuth2 flow
```
Key differentiators vs #660 (Gmail)
What it does
Config (`config/profile.yml`)
```yaml
microsoft:
client_id: '' # from portal.azure.com app registration
tenant: common # 'common' for personal, tenant ID for enterprise
token_path: calendar/ms-token.json
outlook_scan_days: 7
```
Acceptance Criteria
Related
Summary by CodeRabbit