Teams Automation Skill
Unified Microsoft Graph API client for Microsoft 365.
Supports the full office automation lifecycle: Mail, Calendar, Teams Chat/Channel
(read & send), Users, Presence, Files, SharePoint Sites, and Tasks.
When to Use
Trigger this skill when the user:
- Wants to read/search/send emails via Graph API
- Needs to query calendar events
- Asks about Teams messages (chats or channel) — read or send
- Wants to search/lookup users in the organization
- Checks someone's online/availability status
- Needs to access SharePoint sites or OneDrive files
- Wants to read Planner/To Do tasks
- Mentions "Graph API", "Teams", "teams-automation", "邮件", "日历", "Teams消息", "发送消息", etc.
Quick Start
Prerequisites
- Register an Azure AD application:
- Go to Azure Portal → App registrations → New registration
- Set redirect URI tohttps://login.microsoftonline.com/common/oauth2/nativeclient
- Under API Permissions, add the required Microsoft Graph delegated permissions (see Permissions)
- Under Authentication, enable "Allow public client flows" (for device-code flow)
- 复制config.example.yaml为config.yaml,填入你的client_id和tenant
Authentication Flow
CRITICAL — AI caller must follow this procedure exactly:
Authentication is a two-step, user-driven process — never auto-poll:
Step 1: Check cache
Read ~/.workbuddy/.msal_token.json
If valid token exists → proceed to API call immediately (no user interaction)
If expired → try refresh, then proceed
If missing → go to Step 2
Step 2: Start device-code flow (ONLY when no cached token)
1. Run: python scripts/graph_client.py --start-auth
2. **ALWAYS output the device code to the user immediately:**
"请在浏览器打开 https://login.microsoft.com/device 并输入代码: XXXXXXXX"
3. **STOP HERE and WAIT** — do NOT poll, do NOT continue
4. Tell the user: "完成后告诉我继续"
Step 3: Complete auth (ONLY after user confirms they finished in browser)
1. Run: python scripts/graph_client.py --complete-auth
2. Token is cached at ~/.workbuddy/.msal_token.json
3. Now proceed to the actual API call (email, calendar, etc.)
Mandatory rules:
- If cached token is valid → skip auth entirely, no output needed
- If auth required → MUST use --start-auth first, display the device code, and WAIT
- NEVER auto-poll — the user must explicitly tell you to continue before running --complete-auth
- Token cache path is fixed: ~/.workbuddy/.msal_token.json
The token is shared across all modules — authenticate once, use everywhere.
Basic Usage
# Authentication (two-step)
python scripts/graph_client.py --start-auth # Step 1: display device code, wait for user
python scripts/graph_client.py --complete-auth # Step 2: poll for token after user authenticates
# Send an email
python scripts/graph_client.py --send-email --to "user@example.com" --subject "Test" --body "Hello"
# Read emails
python scripts/graph_client.py --email-list 15
# Read calendar events
python scripts/graph_client.py --calendar --days 7
# List Teams chats
python scripts/graph_client.py --chats
# Read chat messages with a person
python scripts/graph_client.py --chat-messages --person "UserName"
# Send a message to a person by name (auto finds user, finds/creates chat, sends)
python scripts/graph_client.py --send-to-person "TargetUser" --message "Hi, this is a test"
# Send a message to yourself
python scripts/graph_client.py --send-to-self "Reminder for later"
# Send a message to an existing chat by ID
python scripts/graph_client.py --send-chat-message "Hello" --chat-id "19:xxx@thread.v2"
# Search users
python scripts/graph_client.py --user-search "UserName"
# Check user presence (by email/UPN)
python scripts/graph_client.py --presence "user@example.com"
# Quick presence check by name (search + status)
python scripts/graph_client.py --status "UserName"
# Get my profile
python scripts/graph_client.py --me
# List SharePoint sites
python scripts/graph_client.py --sites
# List document libraries in a SharePoint site
python scripts/graph_client.py --site-drives "SiteName"
# List folder contents (OneDrive root by default)
python scripts/graph_client.py --ls
# List specific folder
python scripts/graph_client.py --ls --folder "MyFolder"
# List SharePoint folder
python scripts/graph_client.py --ls --drive "DRIVE_ID" --folder "SubFolder"
# Download a file
python scripts/graph_client.py --download "ITEM_ID" --save "./file.xlsx"
From Python Code
from graph_client import GraphClient
client = GraphClient()
client.ensure_token() # Auto auth
# Email
emails = client.list_emails(count=15)
unread = client.list_emails(count=10, filter_unread=True)
client.send_email("Subject", "Body text", "user@example.com")
# Calendar
events = client.list_events(days_back=7)
# Teams
chats = client.list_chats()
msgs = client.get_chat_messages(person_name="UserName")
channels = client.list_channels(team_name="TeamName")
# Teams — Send messages
result = client.send_message_to_person("TargetUser", "Hello!")
client.send_chat_message(chat_id="19:xxx@thread.v2", content="Hi")
client.ensure_self_chat() # get or create self-chat
client.find_or_create_chat(user_id="xxx") # find/create 1:1 chat
# Users
user = client.get_me()
results = client.search_users("UserName")
presence = client.get_presence("user@example.com")
status_results = client.quick_presence("UserName")
manager = client.get_manager()
# Files & Sites
files = client.list_recent_files()
sites = client.list_sites()
site = client.get_site("SiteName") # find site by name
drives = client.list_site_drives("SiteName") # list doc libraries
items = client.list_folder_items("me", "MyFolder") # OneDrive folder
items = client.list_folder_items(drive_id, "SubFolder") # SharePoint folder
path = client.download_file("me", item_id, "./file.txt") # download
# Tasks
tasks = client.list_tasks()
Permissions
| Scope | API |
|---|---|
Mail.Read |
GET /me/messages |
Mail.Send |
POST /me/sendMail |
Calendars.Read |
GET /me/events |
Chat.Read |
GET /me/chats, GET /chats/{id}/messages |
Chat.ReadWrite |
POST /chats, POST /chats/{id}/messages |
Team.ReadBasic.All |
GET /teams |
Channel.ReadBasic.All |
GET /teams/{id}/channels |
ChannelMessage.Read.All |
GET /teams/{id}/channels/{id}/messages |
User.Read |
GET /me |
User.Read.All |
GET /users |
Presence.Read.All |
POST /communications/getPresencesByUserId |
Files.Read.All |
GET /me/drive, GET /drives/{id} |
Sites.Read.All |
GET /sites |
Tasks.Read |
GET /me/planner/tasks |
Files
teams-automation/
├── .gitignore # Prevents config.yaml from leaking
├── SKILL.md # Skill documentation
├── config.example.yaml # Configuration template
├── config.yaml # Your local config (gitignored)
└── scripts/
└── graph_client.py # Unified Graph API client
Configuration
config.yaml structure:
| Field | Required | Description |
|---|---|---|
client_id |
Yes | Azure AD application (client) ID |
tenant |
Yes | Azure AD tenant domain (e.g. yourcompany.com) |
scopes |
No | Custom permission scopes (defaults to read + write scopes) |
Token Cache
Token is stored at ~/.workbuddy/.msal_token.json — a persistent cache shared by all Graph API calls in this skill. Once authenticated, all subsequent calls reuse the cached token automatically; re-authentication only triggers when the token expires.
Path resolution:
| OS | Token cache file |
|---|---|
| Linux / macOS | ~/.workbuddy/.msal_token.json |
| Windows | %USERPROFILE%\.workbuddy\.msal_token.json |
Important for AI callers: Before making Graph API calls, check if ~/.workbuddy/.msal_token.json exists and its token is still valid. If expired, run --auth to refresh. Do NOT re-authenticate on every call — read the cache first.
{
"access_token": "eyJ...",
"refresh_token": "0.A..."
}