🔗 https://github.com/eggyrooch-blip/office365-tools
本 skill 依赖上面这个 Python CLI。Agent 执行前 先确认 repo 已 clone 到本地;遇到任何不确定的实现细节(CLI 子命令签名、 .env 变量名、API 返回字段) 优先去仓库查 README.md / CLAUDE.md / docs/,不要凭本 skill 描述臆断。仓库是 single source of truth。
git clone https://github.com/eggyrooch-blip/office365-tools && cd office365-tools && pip install -r requirements.txt.env,按下方模板填写contact:contact:readonly,发布版本User.ReadWrite.All / LicenseAssignment.ReadWrite.All 管理员同意.env 模板(复制到 office-usertools/.env 后按实际填写)#env
# --------- Office 365(世纪互联) ---------
CLIENT_ID=your-entra-app-client-id
TENANT_ID=your-entra-tenant-id
CLIENT_SECRET=your-entra-app-secret
DEFAULT_PASSWORD=ChangeMe@2025
DEFAULT_DOMAIN=yourcorp.partner.onmschina.cn
FORCE_CHANGE_PASSWORD=true
# 通知邮件
NOTIFICATION_ENABLED=true
NOTIFICATION_FROM_EMAIL=it-tools@yourcorp.com
NOTIFICATION_BCC_EMAILS=it@yourcorp.com
NOTIFICATION_EMAIL_DOMAIN=yourcorp.com
# SMTP
SMTP_HOST=smtp.feishu.cn
SMTP_PORT=465
SMTP_USERNAME=it-tools@yourcorp.com
SMTP_PASSWORD=your-smtp-password
SMTP_USE_SSL=true
# --------- Adobe UMAPI ---------
ADOBE_CLIENT_ID=your-adobe-client-id
ADOBE_CLIENT_SECRET=your-adobe-client-secret
ADOBE_ORG_ID=xxxxxxxxxxxxxxxxxxxxxxxx@AdobeOrg
ADOBE_API_BASE_URL=https://usermanagement.adobe.io/v2/usermanagement
ADOBE_DEFAULT_DOMAIN=yourcorp.com
# --------- 飞书开放平台 ---------
FEISHU_APP_ID=cli_xxxxxxxxxxxxxxx
FEISHU_APP_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
FEISHU_API_BASE=https://open.feishu.cn
# 员工在飞书登记的工作邮箱域(把 O365 UPN 的 local-part 拼这个域去查)
FEISHU_EMAIL_DOMAIN=yourcorp.com
用户说动作"离职检查" / "查离职" / "筛查离职"执行完整流程"清理离职账号" / "清户"完整流程,最后一步交互确认删除"Office 离职" / "Adobe 离职"只跑指定平台"resignation check"完整流程
参数说明默认provideroffice365 / adobe / both``bothcsv_pathAdobe 走 CSV 模式时传入 Admin Console 导出的 users.csv 路径无(优先走 API)delete交互确认后是否删除true
/Users/kite/Documents/office-usertools(含 office365 / adobe CLI).env 必备: FEISHU_APP_ID + FEISHU_APP_SECRET( 不依赖 lark-cli)contact:contact:readonly(或 contact:user.employee_id:readonly)并发布版本步骤 1 · 获取 tenant_access_token(2h 有效)
python
import requests, os
from dotenv import load_dotenv; load_dotenv()
BASE = os.getenv('FEISHU_API_BASE', 'https://open.feishu.cn')
r = requests.post(f'{BASE}/open-apis/auth/v3/tenant_access_token/internal',
json={'app_id': os.environ['FEISHU_APP_ID'],
'app_secret': os.environ['FEISHU_APP_SECRET']}, timeout=10)
token = r.json()['tenant_access_token']
H = {'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'}
步骤 2 · 批量用 email 查 user_id(单次最多 50 个)
python
# POST /open-apis/contact/v3/users/batch_get_id?user_id_type=user_id
def lookup(emails):
out = {}
for i in range(0, len(emails), 50):
batch = emails[i:i+50]
r = requests.post(f'{BASE}/open-apis/contact/v3/users/batch_get_id',
headers=H, params={'user_id_type':'user_id'},
json={'emails': batch}, timeout=15)
for item in r.json().get('data', {}).get('user_list', []):
out[item['email']] = item.get('user_id') # None = 离职/未入职
return out
user_id is None 即飞书通讯录里查不到 → 离职候选。命中 user_id 即在职。
相比 lark-cli 的优势:
auth login,可在 CI / ClawHub 环境运行Office 365
python
from app.services.provider_factory import get_provider
p = get_provider('office365')
users = p.graph_client.get_users(select='userPrincipalName,displayName,accountEnabled')
emails_o365 = [u['userPrincipalName'] for u in users] # 例 zhangsan@corp.partner.onmschina.cn
但要注意:世纪互联的 UPN 域( partner.onmschina.cn)和飞书注册的工作邮箱域(通常是 corp.com) 不一样。需要把 UPN 的 local-part 拼上飞书员工邮箱域:
python
FEISHU_EMAIL_DOMAIN = os.getenv('FEISHU_EMAIL_DOMAIN') or os.getenv('ADOBE_DEFAULT_DOMAIN') # 例 yourcorp.com
o365_probe = [upn.split('@')[0] + '@' + FEISHU_EMAIL_DOMAIN for upn in emails_o365]
(如果两边域一致就直接用原 email)
Adobe
优先:CSV 模式——Admin Console → Users → Export python
import csv
with open(csv_path, encoding='utf-8-sig') as f:
rows = list(csv.DictReader(f))
emails_adobe = [r['电子邮件'] for r in rows if r.get('电子邮件')]
备选:API 模式 p.client.get_all_users()(分页易触发 429,Retry-After 可能很长)
python
probe_emails = list(set(o365_probe + emails_adobe))
id_map = lookup(probe_emails)
active_set = {e for e, uid in id_map.items() if uid}
miss_set = {e for e, uid in id_map.items() if not uid}
Office 365:
admin, admin-it, 含 testuser / svc- 前缀等 → skip_systemAdobe:
keepadobe* / testaccount* → shared_pool(人工判断)needs_manual_reviewtext
## 离职筛查结果(<provider>)
- 总账号:N | 在职命中:X | 疑似离职:Y
### 真名离职候选 (Z 人)
| email | displayName | 平台 | 状态 | license |
...
### 需人工判断
- 共享池账号:...
- 非员工邮箱:...
用 AskUserQuestion 给三选项:
必须:
deleted: True;Adobe: completed:1, result:success)/tmp/<provider>_delete.logpython main.py office365 delete <ldap> —— 自动发通知邮件python main.py adobe delete <email> —— 已修复 array payload + removeFromOrgRetry-After 头,或用 ScheduleWakeup 稍后重试控制台报告 + /tmp/resignation_report_<ts>.md,包含:
症状原因处理飞书全部 MISSemail 域不对 / app 权限未发布先单独查一个已知在职邮箱,确认能命中code:99991668token 未授权该 scope去应用后台权限管理,申请 contact:contact:readonly 并 发布版本code:99991672app 未发版 / 租户未安装发布应用版本 → 管理员后台启用O365 UPN 域与飞书邮箱域不一致世纪互联是 partner.onmschina.cn,飞书是企业邮箱域用 FEISHU_EMAIL_DOMAIN 覆盖;local-part 重新拼域Adobe 429 Retry-After >30minget_all_users 分页打满配额切 CSV 模式删除返回 notCompleted=1用户已不在 org / 邮箱错先 inspect 核实
AskUserQuestion 展示完整名单拿确认FEISHU_APP_SECRET 不能外泄/写进日志