Google Workspace 技能
Hermes 的 Gmail、日历、云端硬盘、联系人、表格和文档集成。使用 OAuth2 并自动刷新令牌。优先使用 Google Workspace CLI (gws)(如果可用),以获得更广泛的功能覆盖;否则回退到 Google 的 Python 客户端库。
技能路径: skills/productivity/google-workspace/
设置
设置完全由代理驱动 —— 要求 Hermes 设置 Google Workspace,它将引导您完成每一步。流程如下:
- 创建 Google Cloud 项目并启用所需的 API(Gmail、日历、云端硬盘、表格、文档、联系人)
- 创建 OAuth 2.0 凭据(桌面应用类型)并下载客户端密钥 JSON 文件
- 授权 —— Hermes 生成一个授权 URL,您在浏览器中批准,然后将重定向 URL 粘贴回来
- 完成 —— 从此时起令牌将自动刷新
如果您只需要电子邮件(不需要日历/云端硬盘/表格),请使用 himalaya 技能 —— 它使用 Gmail 应用专用密码,只需 2 分钟即可完成。无需创建 Google Cloud 项目。
Gmail
搜索
$GAPI gmail search "is:unread" --max 10
$GAPI gmail search "from:boss@company.com newer_than:1d"
$GAPI gmail search "has:attachment filename:pdf newer_than:7d"
返回每条消息的 JSON 数据,包含 id、from、subject、date、snippet 和 labels。
阅读
$GAPI gmail get MESSAGE_ID
以纯文本形式返回完整的消息正文(优先使用纯文本,否则回退到 HTML)。
发送
# 基本发送
$GAPI gmail send --to user@example.com --subject "Hello" --body "Message text"
# HTML 邮件
$GAPI gmail send --to user@example.com --subject "Report" \
--body "<h1>Q4 Results</h1><p>Details here</p>" --html
# 自定义发件人头部(显示名称 + 邮箱)
$GAPI gmail send --to user@example.com --subject "Hello" \
--from '"Research Agent" <user@example.com>' --body "Message text"
# 带抄送
$GAPI gmail send --to user@example.com --cc "team@example.com" \
--subject "Update" --body "FYI"
自定义发件人头部
--from 标志允许您自定义外发电子邮件的发件人显示名称。当多个代理共享同一个 Gmail 账户,但您希望收件人看到不同的名称时,这非常有用:
# 代理 1
$GAPI gmail send --to client@co.com --subject "Research Summary" \
--from '"Research Agent" <shared@company.com>' --body "..."
# 代理 2
$GAPI gmail send --to client@co.com --subject "Code Review" \
--from '"Code Assistant" <shared@company.com>' --body "..."
工作原理: --from 值被设置为 MIME 消息的 RFC 5322 From 头部。Gmail 允许您在自己的已认证邮箱地址上自定义显示名称,而无需任何额外配置。收件人看到的是自定义显示名称(例如“Research Agent”),而邮箱地址保持不变。
重要提示: 如果您在 --from 中使用不同的邮箱地址(非已认证账户),Gmail 要求该地址必须在 Gmail 设置 → 账户 → 以该地址发送邮件中配置为“以该地址发送”别名。
--from 标志同时适用于 send 和 reply 命令:
$GAPI gmail reply MESSAGE_ID \
--from '"Support Bot" <shared@company.com>' --body "We're on it"
回复
$GAPI gmail reply MESSAGE_ID --body "Thanks, that works for me."
自动将回复归入线程(设置 In-Reply-To 和 References 头部),并使用原始消息的线程 ID。
标签
# 列出所有标签
$GAPI gmail labels
# 添加/移除标签
$GAPI gmail modify MESSAGE_ID --add-labels LABEL_ID
$GAPI gmail modify MESSAGE_ID --remove-labels UNREAD
日历
# 列出事件(默认为接下来 7 天)
$GAPI calendar list
$GAPI calendar list --start 2026-03-01T00:00:00Z --end 2026-03-07T23:59:59Z
# 创建事件(必须指定时区)
$GAPI calendar create --summary "Team Standup" \
--start 2026-03-01T10:00:00-07:00 --end 2026-03-01T10:30:00-07:00
# 带地点和参与者
$GAPI calendar create --summary "Lunch" \
--start 2026-03-01T12:00:00Z --end 2026-03-01T13:00:00Z \
--location "Cafe" --attendees "alice@co.com,bob@co.com"
# 删除事件
$GAPI calendar delete EVENT_ID
日历时间必须包含时区偏移(例如 -07:00)或使用 UTC(Z)。像 2026-03-01T10:00:00 这样的裸时间戳是模糊的,将被视为 UTC。
云端硬盘
$GAPI drive search "quarterly report" --max 10
$GAPI drive search "mimeType='application/pdf'" --raw-query --max 5
表格
# 读取一个范围
$GAPI sheets get SHEET_ID "Sheet1!A1:D10"
# 写入一个范围
$GAPI sheets update SHEET_ID "Sheet1!A1:B2" --values '[["Name","Score"],["Alice","95"]]'
# 追加行
$GAPI sheets append SHEET_ID "Sheet1!A:C" --values '[["new","row","data"]]'
文档
$GAPI docs get DOC_ID
返回文档标题和完整文本内容。
联系人
$GAPI contacts list --max 20
输出格式
所有命令均返回 JSON。每个服务的关键字段如下:
| 命令 | 字段 |
|---|---|
gmail search | id、threadId、from、to、subject、date、snippet、labels |
gmail get | id、threadId、from、to、subject、date、labels、body |
gmail send/reply | status、id、threadId |
calendar list | id、summary、start、end、location、description、htmlLink |
calendar create | status、id、summary、htmlLink |
drive search | id、name、mimeType、modifiedTime、webViewLink |
contacts list | name、emails、phones |
sheets get | 单元格值的二维数组 |
故障排除
| 问题 | 解决方法 |
|---|---|
NOT_AUTHENTICATED | 运行设置(要求 Hermes 设置 Google Workspace) |
REFRESH_FAILED | 令牌已被撤销 —— 重新运行授权步骤 |
HttpError 403: Insufficient Permission | 缺少权限范围 —— 撤销并重新授权正确的服务 |
HttpError 403: Access Not Configured | Google Cloud 控制台中未启用 API |
ModuleNotFoundError | 使用 --install-deps 运行设置脚本 |