Skip to Content

24c - 自定义集成教程

本文是《AI Agent 实战手册》第 24 章第 3 节。 上一节:24b-自定义Agent开发教程 | 下一节:24d-Agent间通信

概述

OpenClaw 的核心价值之一在于它能与你日常使用的外部服务深度集成——Gmail 收发邮件、Notion 管理知识库、GitHub 协作开发、Slack 团队沟通。本节提供四大服务的完整集成教程,覆盖从 API 凭证获取、Skill 编写到实际交互的全流程。每个集成都包含两种实现路径:Skill 模式(Markdown 驱动,适合大多数场景)和 Plugin 模式(TypeScript 代码,适合需要持久连接或复杂认证流的场景),帮助你根据实际需求选择最合适的方案。


1. Gmail 集成

Gmail 是 OpenClaw 最常用的集成之一。通过 Google Workspace CLI 工具 gog,Agent 可以搜索邮件、发送回复、管理日历和 Drive 文件。

工具推荐

工具用途价格适用场景
gog CLIGoogle Workspace 统一 CLI(Gmail/Calendar/Drive/Contacts/Sheets/Docs)免费(开源)推荐方案,OpenClaw 官方 Skill 依赖
Google Cloud ConsoleOAuth 凭证管理和 API 启用免费(API 调用有配额)必需,用于创建 OAuth 客户端
ClawEmail一键 Google Workspace 邮箱配置$5/月/邮箱快速配置,跳过 OAuth 手动流程
Resend替代发件方案(API 发送邮件)免费(100 封/天)/ $20/月起仅需发送邮件、不需读取收件箱时

前置条件

  1. Google 账号
  2. Google Cloud Console 项目(免费创建)
  3. Node.js 18+ 和 Homebrew(macOS)或等效包管理器

配置步骤

步骤 1:创建 Google Cloud OAuth 凭证

# 1. 访问 Google Cloud Console # https://console.cloud.google.com # 2. 创建新项目(或选择已有项目) # → APIs & Services → Library → 搜索并启用以下 API: # - Gmail API # - Google Calendar API # - Google Drive API # - Google Sheets API # - Google Docs API # - People API(联系人) # 3. 配置 OAuth 同意屏幕 # → APIs & Services → OAuth consent screen # → 选择 "External" → 填写应用名称 # → 添加 Scopes: # - https://www.googleapis.com/auth/gmail.modify # - https://www.googleapis.com/auth/calendar # - https://www.googleapis.com/auth/drive # - https://www.googleapis.com/auth/contacts.readonly # - https://www.googleapis.com/auth/spreadsheets # - https://www.googleapis.com/auth/documents # → 点击 "Publish App"(获取永久 token,否则 7 天过期) # 4. 创建 OAuth 客户端 ID # → APIs & Services → Credentials → Create Credentials → OAuth client ID # → Application type: Desktop app # → 下载 JSON 文件(client_secret_xxx.json)

⚠️ 重要:如果不点击 “Publish App”,OAuth token 将在 7 天后过期,需要重新授权。发布后 token 永久有效。Google 可能显示”未验证应用”警告,但因为这是你自己的应用,可以安全忽略。

步骤 2:安装 gog CLI 并授权

# macOS 安装 brew install steipete/tap/gogcli # 验证安装 gog --version # 导入 OAuth 凭证 gog auth credentials /path/to/client_secret_xxx.json # 添加 Google 账号并授权所有服务 gog auth add your-email@gmail.com \ --services gmail,calendar,drive,contacts,docs,sheets # 浏览器会自动打开,完成 Google 授权流程 # 授权成功后验证 gog auth list # 预期输出:your-email@gmail.com gmail,calendar,drive,contacts,docs,sheets

步骤 3:安装 OpenClaw Gmail Skill

# 安装官方 gog Skill npx playbooks add skill openclaw/openclaw --skill gog # 或手动创建 Skill 文件 mkdir -p ~/.openclaw/skills

完整 Skill 示例

方式 A:使用官方 gog Skill(推荐)

官方 Skill 已经封装了所有 gog CLI 命令。安装后 Agent 自动获得 Gmail/Calendar/Drive 等能力。

方式 B:自定义 Gmail Skill

如果你只需要邮件功能,或想定制 Agent 的邮件处理行为,可以编写自定义 Skill:

<!-- ~/.openclaw/skills/gmail-assistant/SKILL.md --> # Gmail Assistant 管理 Gmail 收件箱:搜索、阅读、回复和发送邮件。 ## 前置条件 - gog CLI 已安装并授权(`gog auth list` 确认) - 默认账号已设置(`GOG_DEFAULT_ACCOUNT=your@gmail.com` ## 使用场景 - 用户说"检查邮件"或"有什么新邮件" - 用户说"回复 xxx 的邮件" - 用户说"发邮件给 xxx" - 用户说"搜索关于 xxx 的邮件" ## 操作指南 ### 搜索邮件 ```bash # 搜索最近 7 天的邮件 gog gmail search 'newer_than:7d' --max 10 --json # 搜索特定发件人 gog gmail messages search "from:boss@company.com in:inbox" \ --max 20 --json # 搜索未读邮件 gog gmail search 'is:unread in:inbox' --max 10 --json

发送邮件

# 纯文本邮件 gog gmail send \ --to recipient@example.com \ --subject "会议纪要" \ --body-file - <<'EOF' 你好, 以下是今天会议的要点: - 第一项决议 - 第二项决议 此致 EOF # HTML 格式邮件 gog gmail send \ --to recipient@example.com \ --subject "周报" \ --body-html "<h2>本周总结</h2><ul><li>完成功能 A</li><li>修复 Bug B</li></ul>"

回复邮件

# 回复特定邮件(需要原始 messageId) gog gmail send \ --to sender@example.com \ --subject "Re: 原始主题" \ --body "收到,我会处理。" \ --reply-to-message-id <原始messageId>

创建草稿

gog gmail drafts create \ --to recipient@example.com \ --subject "待审核邮件" \ --body-file ./draft-content.txt

安全规则

  • 发送邮件前必须向用户确认收件人和内容
  • 不要自动回复未经用户审核的邮件
  • 搜索结果中的敏感信息(密码、token)不要在通道中显示
**方式 C:Plugin 模式(高级——实时邮件推送)** 当你需要实时收到新邮件通知(而非轮询),可以使用 Gmail Pub/Sub + Plugin 的方式: ```typescript // plugins/gmail-realtime/src/index.ts import type { PluginApi } from '@openclaw/types'; import { google } from 'googleapis'; export function register(api: PluginApi) { // 注册 Gmail 实时监听工具 api.registerTool({ name: 'gmail-watch', description: '启动 Gmail 实时监听,新邮件到达时通过 Pub/Sub 推送通知', parameters: { labelIds: { type: 'array', items: { type: 'string' }, description: '要监听的标签 ID 列表,默认 ["INBOX"]', optional: true } }, execute: async ({ labelIds = ['INBOX'] }) => { const auth = new google.auth.GoogleAuth({ keyFile: process.env.GOOGLE_SERVICE_ACCOUNT_KEY, scopes: ['https://www.googleapis.com/auth/gmail.readonly'] }); const gmail = google.gmail({ version: 'v1', auth }); const res = await gmail.users.watch({ userId: 'me', requestBody: { topicName: process.env.GMAIL_PUBSUB_TOPIC, // 例如 'projects/my-project/topics/gmail-notifications' labelIds } }); return { historyId: res.data.historyId, expiration: res.data.expiration, message: `Gmail 监听已启动,将在 ${new Date( Number(res.data.expiration) ).toISOString()} 过期` }; } }); // 注册 Webhook 处理 Pub/Sub 推送 api.registerWebhook({ path: '/webhook/gmail-pubsub', handler: async (req) => { const message = JSON.parse( Buffer.from(req.body.message.data, 'base64').toString() ); const { emailAddress, historyId } = message; // 获取新邮件详情 const auth = new google.auth.GoogleAuth({ keyFile: process.env.GOOGLE_SERVICE_ACCOUNT_KEY, scopes: ['https://www.googleapis.com/auth/gmail.readonly'] }); const gmail = google.gmail({ version: 'v1', auth }); const history = await gmail.users.history.list({ userId: 'me', startHistoryId: historyId, historyTypes: ['messageAdded'] }); return { newMessages: history.data.history?.length || 0, emailAddress, summary: '新邮件已到达,请检查收件箱' }; } }); }

使用场景

场景实现方式示例指令
早间邮件简报Heartbeat + gog Skill”检查未读邮件,生成摘要”
自动分类邮件Cron + 自定义 Skill”每 30 分钟扫描收件箱,标记 VIP 邮件”
实时新邮件通知Plugin + Pub/Sub新邮件到达时自动推送到 Telegram
邮件草稿审核gog Skill + 人工确认”帮我起草回复,我确认后发送”
日历事件提醒Heartbeat + gog Skill”15 分钟内有会议就提醒我”

安全注意事项

  1. OAuth Scope 最小化:如果只需读取邮件,使用 gmail.readonly 而非 gmail.modify
  2. Token 存储安全gog 将 token 存储在 ~/.config/gog/,确保该目录权限为 700
  3. 发送确认:在 Skill 中明确要求 Agent 发送前必须获得用户确认
  4. 敏感信息过滤:Agent 处理邮件时不应在公共通道显示密码、token 等敏感内容

2. Notion 集成

Notion 集成让 Agent 能够管理你的知识库——创建页面、查询数据库、更新任务状态。OpenClaw 通过 Notion API 直接操作,无需额外 CLI 工具。

工具推荐

工具用途价格适用场景
Notion Internal Integration官方 API 集成免费(API 调用无额外费用)推荐方案,所有 Notion 操作
Notion APIRESTful API(v2025-09-03)免费(速率限制 ~3 req/s)Skill 和 Plugin 的底层接口
openclaw/notion Skill官方 Notion Skill免费(开源)快速安装,覆盖常见操作

前置条件

  1. Notion 账号(免费版即可)
  2. Notion Integration(在 notion.so/my-integrations 创建)
  3. curl 或 httpie(用于 API 调用)

配置步骤

步骤 1:创建 Notion Integration

# 1. 访问 https://notion.so/my-integrations # 2. 点击 "New integration" # 3. 填写名称(如 "OpenClaw Agent") # 4. 选择关联的 Workspace # 5. 设置 Capabilities: # - Content Capabilities: Read content, Update content, Insert content # - Comment Capabilities: Read comments(可选) # - User Capabilities: Read user information(可选) # 6. 复制 API Key(以 ntn_ 或 secret_ 开头)

步骤 2:存储 API Key

# 创建配置目录 mkdir -p ~/.config/notion # 安全存储 API Key echo "ntn_your_api_key_here" > ~/.config/notion/api_key chmod 600 ~/.config/notion/api_key # 或设置环境变量(在 .env 或 openclaw.json 中) export NOTION_API_KEY="ntn_your_api_key_here"

步骤 3:授权页面访问

# 在 Notion 中,对每个需要 Agent 访问的页面/数据库: # → 点击右上角 "..." → "Connect to" → 选择你的 Integration 名称 # # ⚠️ 这一步容易遗忘!Integration 只能访问显式授权的页面。 # 子页面会自动继承父页面的授权。

步骤 4:安装 Notion Skill

# 安装官方 Notion Skill npx playbooks add skill openclaw/openclaw --skill notion

完整 Skill 示例

自定义 Notion 项目管理 Skill

<!-- ~/.openclaw/skills/notion-pm/SKILL.md --> # Notion 项目管理 通过 Notion API 管理项目任务、会议记录和知识库。 ## 前置条件 - NOTION_API_KEY 已配置(`~/.config/notion/api_key` 或环境变量) - 目标页面/数据库已授权给 Integration ## 使用场景 - 用户说"创建任务"或"添加待办" - 用户说"查看项目进度"或"今天有什么任务" - 用户说"记录会议纪要" - 用户说"搜索知识库" ## API 配置 所有请求需要以下 Header: ```bash NOTION_KEY=$(cat ~/.config/notion/api_key) # 基础请求模板 curl -X [METHOD] "https://api.notion.com/v1/[endpoint]" \ -H "Authorization: Bearer $NOTION_KEY" \ -H "Notion-Version: 2025-09-03" \ -H "Content-Type: application/json"

操作指南

搜索页面和数据库

curl -X POST "https://api.notion.com/v1/search" \ -H "Authorization: Bearer $NOTION_KEY" \ -H "Notion-Version: 2025-09-03" \ -H "Content-Type: application/json" \ -d '{"query": "项目计划", "filter": {"property": "object", "value": "page"}}'

查询任务数据库

# 查询所有"进行中"的任务 curl -X POST "https://api.notion.com/v1/data_sources/[data_source_id]/query" \ -H "Authorization: Bearer $NOTION_KEY" \ -H "Notion-Version: 2025-09-03" \ -H "Content-Type: application/json" \ -d '{ "filter": { "property": "Status", "select": {"equals": "进行中"} }, "sorts": [{"property": "Priority", "direction": "ascending"}] }'

创建新任务

curl -X POST "https://api.notion.com/v1/pages" \ -H "Authorization: Bearer $NOTION_KEY" \ -H "Notion-Version: 2025-09-03" \ -H "Content-Type: application/json" \ -d '{ "parent": {"database_id": "[你的数据库ID]"}, "properties": { "Name": {"title": [{"text": {"content": "新任务标题"}}]}, "Status": {"select": {"name": "待办"}}, "Priority": {"select": {"name": "高"}}, "Due Date": {"date": {"start": "2026-03-20"}}, "Assignee": {"people": [{"id": "user_id"}]} } }'

更新任务状态

curl -X PATCH "https://api.notion.com/v1/pages/[page_id]" \ -H "Authorization: Bearer $NOTION_KEY" \ -H "Notion-Version: 2025-09-03" \ -H "Content-Type: application/json" \ -d '{"properties": {"Status": {"select": {"name": "已完成"}}}}'

创建会议纪要页面

curl -X POST "https://api.notion.com/v1/pages" \ -H "Authorization: Bearer $NOTION_KEY" \ -H "Notion-Version: 2025-09-03" \ -H "Content-Type: application/json" \ -d '{ "parent": {"page_id": "[会议记录父页面ID]"}, "properties": { "title": {"title": [{"text": {"content": "2026-03-15 产品评审会议"}}]} }, "children": [ { "object": "block", "type": "heading_2", "heading_2": {"rich_text": [{"text": {"content": "参会人员"}}]} }, { "object": "block", "type": "bulleted_list_item", "bulleted_list_item": {"rich_text": [{"text": {"content": "张三、李四、王五"}}]} }, { "object": "block", "type": "heading_2", "heading_2": {"rich_text": [{"text": {"content": "决议事项"}}]} }, { "object": "block", "type": "to_do", "to_do": { "rich_text": [{"text": {"content": "完成 v2.0 需求文档"}}], "checked": false } } ] }'

Notion API 2025-09-03 版本注意事项

  • 数据库在 API 中称为 “data sources”,使用 /data_sources/ 端点查询
  • 每个数据库有两个 ID:database_id(创建页面时用)和 data_source_id(查询时用)
  • 搜索结果中数据库返回为 "object": "data_source"
  • 速率限制约 3 请求/秒

安全规则

  • 不要在消息中暴露完整的 API Key
  • 创建/修改操作前向用户确认
  • 只访问已授权的页面,不要尝试访问未授权资源
**Plugin 模式:Notion 双向同步** 当你需要 Notion 变更实时触发 Agent 行为时,可以使用 Plugin + Webhook 模式: ```typescript // plugins/notion-sync/src/index.ts import type { PluginApi } from '@openclaw/types'; const NOTION_API = 'https://api.notion.com/v1'; const NOTION_VERSION = '2025-09-03'; async function notionRequest( method: string, endpoint: string, body?: object ) { const apiKey = process.env.NOTION_API_KEY; const res = await fetch(`${NOTION_API}${endpoint}`, { method, headers: { 'Authorization': `Bearer ${apiKey}`, 'Notion-Version': NOTION_VERSION, 'Content-Type': 'application/json' }, body: body ? JSON.stringify(body) : undefined }); return res.json(); } export function register(api: PluginApi) { // 批量创建任务工具 api.registerTool({ name: 'notion-batch-create', description: '批量创建 Notion 任务(从会议纪要或需求文档中提取)', parameters: { databaseId: { type: 'string', description: '目标数据库 ID' }, tasks: { type: 'array', items: { type: 'object', properties: { title: { type: 'string' }, status: { type: 'string', optional: true }, priority: { type: 'string', optional: true }, dueDate: { type: 'string', optional: true } } }, description: '任务列表' } }, execute: async ({ databaseId, tasks }) => { const results = []; for (const task of tasks) { const properties: Record<string, unknown> = { Name: { title: [{ text: { content: task.title } }] } }; if (task.status) { properties.Status = { select: { name: task.status } }; } if (task.priority) { properties.Priority = { select: { name: task.priority } }; } if (task.dueDate) { properties['Due Date'] = { date: { start: task.dueDate } }; } const result = await notionRequest('POST', '/pages', { parent: { database_id: databaseId }, properties }); results.push({ id: result.id, title: task.title, url: result.url }); // 遵守速率限制 await new Promise(r => setTimeout(r, 350)); } return { created: results.length, tasks: results }; } }); }

使用场景

场景实现方式示例指令
每日任务回顾Heartbeat + Notion Skill”查看今天到期的任务”
会议纪要归档自定义 Skill”把这次会议的要点记录到 Notion”
批量创建任务Plugin”从这份需求文档中提取任务,批量创建到项目看板”
知识库搜索Notion Skill”搜索关于部署流程的文档”
周报自动生成Cron + Notion Skill”每周五汇总本周完成的任务,生成周报”

3. GitHub 集成

GitHub 集成让 Agent 能够管理代码仓库——查看 PR、创建 Issue、检查 CI 状态。OpenClaw 提供两种 GitHub 认证方式:Personal Access Token(PAT)和 GitHub App。

工具推荐

工具用途价格适用场景
GitHub PAT(Fine-grained)细粒度个人访问令牌免费推荐方案,最小权限控制
GitHub PAT(Classic)经典个人访问令牌免费需要跨组织访问时
GitHub App组织级应用集成免费团队/企业场景,更安全的权限模型
GitHub CLI(gh)官方命令行工具免费(开源)Skill 中调用 GitHub 操作
openclaw/github-pat Skill官方 GitHub PAT Skill免费(开源)快速安装,覆盖常见 Git 操作

前置条件

  1. GitHub 账号
  2. GitHub CLI(gh)已安装
  3. Personal Access Token 或 GitHub App 凭证

配置步骤

步骤 1:创建 Fine-grained PAT

# 1. 访问 https://github.com/settings/tokens?type=beta # 2. 点击 "Generate new token" # 3. 设置 Token 名称(如 "OpenClaw Agent") # 4. 设置过期时间(建议 90 天,定期轮换) # 5. 选择 Repository access: # - "Only select repositories" → 选择需要的仓库 # 6. 设置 Permissions(最小权限原则): # - Repository permissions: # - Contents: Read and write(克隆、推送) # - Issues: Read and write(创建/管理 Issue) # - Pull requests: Read and write(创建/审查 PR) # - Actions: Read-only(查看 CI 状态) # - Metadata: Read-only(必需) # 7. 点击 "Generate token" 并复制

步骤 2:安全存储 Token

# 方式 A:使用 GitHub CLI 认证(推荐) gh auth login --with-token <<< "ghp_your_token_here" # 方式 B:存储到文件 mkdir -p ~/.config/github echo "ghp_your_token_here" > ~/.config/github/pat chmod 600 ~/.config/github/pat # 方式 C:环境变量 export GITHUB_TOKEN="ghp_your_token_here"

步骤 3:安装 GitHub Skill

# 安装官方 GitHub PAT Skill npx playbooks add skill openclaw/skills --skill github-pat # 验证 GitHub CLI 认证 gh auth status

完整 Skill 示例

自定义 GitHub DevOps Skill

<!-- ~/.openclaw/skills/github-devops/SKILL.md --> # GitHub DevOps 管理 GitHub 仓库:PR 审查、Issue 管理、CI/CD 状态监控、代码搜索。 ## 前置条件 - GitHub CLI(gh)已安装并认证(`gh auth status` 确认) - 或 GITHUB_TOKEN 环境变量已设置 ## 使用场景 - 用户说"检查 PR"或"有什么待审查的" - 用户说"创建 Issue"或"报告 Bug" - 用户说"CI 状态"或"部署情况" - 用户说"搜索代码"或"找到 xxx 的实现" ## 操作指南 ### PR 管理 ```bash # 列出待审查的 PR gh pr list --repo owner/repo --state open --json number,title,author,reviewDecision # 查看 PR 详情和 diff gh pr view 42 --repo owner/repo gh pr diff 42 --repo owner/repo # 审查 PR(添加评论) gh pr review 42 --repo owner/repo --comment --body "LGTM,但建议优化第 35 行的错误处理" # 合并 PR gh pr merge 42 --repo owner/repo --squash --delete-branch # 创建 PR gh pr create --repo owner/repo \ --title "feat: 添加用户认证模块" \ --body "## 变更说明\n- 实现 JWT 认证\n- 添加登录/注册 API\n\n## 测试\n- [x] 单元测试通过\n- [x] E2E 测试通过" \ --base main --head feature/auth

Issue 管理

# 列出 Issue gh issue list --repo owner/repo --state open --label "bug" --json number,title,labels # 创建 Issue gh issue create --repo owner/repo \ --title "Bug: 登录页面在 Safari 上白屏" \ --body "## 复现步骤\n1. 打开 Safari\n2. 访问 /login\n3. 页面白屏\n\n## 预期行为\n显示登录表单\n\n## 环境\n- Safari 18.2\n- macOS 15.3" \ --label "bug,priority:high" # 关闭 Issue gh issue close 15 --repo owner/repo --comment "已在 PR #42 中修复"

CI/CD 状态

# 查看最近的 workflow 运行 gh run list --repo owner/repo --limit 5 --json databaseId,displayTitle,status,conclusion # 查看特定运行的详情 gh run view 12345 --repo owner/repo # 查看失败的日志 gh run view 12345 --repo owner/repo --log-failed # 重新运行失败的 workflow gh run rerun 12345 --repo owner/repo --failed

代码搜索

# 在仓库中搜索代码 gh search code "authentication middleware" --repo owner/repo --json path,textMatches # 搜索文件 gh api "/search/code?q=filename:Dockerfile+repo:owner/repo" | jq '.items[].path'

Release 管理

# 查看最新 Release gh release list --repo owner/repo --limit 3 # 创建 Release gh release create v1.2.0 --repo owner/repo \ --title "v1.2.0 - 用户认证" \ --notes "## 新功能\n- JWT 认证\n- OAuth 2.0 支持\n\n## Bug 修复\n- 修复 Safari 白屏问题" \ --latest

安全规则

  • 不要在消息中显示完整的 PAT token
  • 合并 PR 前必须获得用户确认
  • 不要自动推送到 main/master 分支
  • 删除分支操作需要用户确认
**Plugin 模式:GitHub Webhook 自动化** 当你需要 GitHub 事件(PR 创建、CI 失败、Issue 评论)自动触发 Agent 行为时: ```typescript // plugins/github-automation/src/index.ts import type { PluginApi } from '@openclaw/types'; import { createHmac } from 'crypto'; function verifyGitHubSignature( payload: string, signature: string, secret: string ): boolean { const expected = 'sha256=' + createHmac('sha256', secret).update(payload).digest('hex'); return signature === expected; } export function register(api: PluginApi) { // PR 审查助手工具 api.registerTool({ name: 'github-pr-analyze', description: '分析 GitHub PR 的变更,生成审查摘要', parameters: { owner: { type: 'string', description: '仓库所有者' }, repo: { type: 'string', description: '仓库名称' }, prNumber: { type: 'number', description: 'PR 编号' } }, execute: async ({ owner, repo, prNumber }) => { const token = process.env.GITHUB_TOKEN; const headers = { 'Authorization': `Bearer ${token}`, 'Accept': 'application/vnd.github.v3+json', 'User-Agent': 'OpenClaw-Agent' }; // 获取 PR 信息 const prRes = await fetch( `https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}`, { headers } ); const pr = await prRes.json(); // 获取变更文件列表 const filesRes = await fetch( `https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}/files`, { headers } ); const files = await filesRes.json(); // 获取 CI 状态 const checksRes = await fetch( `https://api.github.com/repos/${owner}/${repo}/commits/${pr.head.sha}/check-runs`, { headers } ); const checks = await checksRes.json(); return { title: pr.title, author: pr.user.login, state: pr.state, additions: pr.additions, deletions: pr.deletions, changedFiles: files.length, files: files.map((f: { filename: string; status: string; changes: number }) => ({ path: f.filename, status: f.status, changes: f.changes })), ciStatus: checks.check_runs?.map( (c: { name: string; conclusion: string }) => ({ name: c.name, conclusion: c.conclusion }) ) || [], mergeable: pr.mergeable, reviewDecision: pr.review_decision }; } }); // GitHub Webhook 处理 api.registerWebhook({ path: '/webhook/github', handler: async (req) => { // 验证签名 const signature = req.headers['x-hub-signature-256']; const secret = process.env.GITHUB_WEBHOOK_SECRET || ''; if (!verifyGitHubSignature( JSON.stringify(req.body), signature, secret )) { return { error: 'Invalid signature', status: 401 }; } const event = req.headers['x-github-event']; const payload = req.body; switch (event) { case 'pull_request': if (payload.action === 'opened' || payload.action === 'synchronize') { return { action: 'analyze_pr', pr: { number: payload.pull_request.number, title: payload.pull_request.title, author: payload.pull_request.user.login, repo: payload.repository.full_name }, message: `新 PR #${payload.pull_request.number}: ${payload.pull_request.title}` }; } break; case 'workflow_run': if (payload.workflow_run.conclusion === 'failure') { return { action: 'ci_failure', workflow: payload.workflow_run.name, repo: payload.repository.full_name, branch: payload.workflow_run.head_branch, url: payload.workflow_run.html_url, message: `⚠️ CI 失败: ${payload.workflow_run.name} on ${payload.workflow_run.head_branch}` }; } break; case 'issues': if (payload.action === 'opened') { return { action: 'new_issue', issue: { number: payload.issue.number, title: payload.issue.title, labels: payload.issue.labels.map( (l: { name: string }) => l.name ) }, message: `新 Issue #${payload.issue.number}: ${payload.issue.title}` }; } break; } return { action: 'ignored', event }; } }); }

使用场景

场景实现方式示例指令
PR 审查摘要gh CLI Skill”分析 PR #42 的变更”
CI 失败通知Webhook PluginCI 失败时自动推送到 Slack
自动创建 Issuegh CLI Skill”为这个 Bug 创建一个 Issue”
代码搜索gh CLI Skill”在仓库中搜索认证相关的代码”
Release 管理gh CLI Skill”创建 v1.2.0 Release”
PR 自动分析Webhook + Plugin新 PR 创建时自动生成审查建议

4. Slack 集成

Slack 集成有两个维度:一是将 Slack 作为 OpenClaw 的消息通道(Agent 通过 Slack 与用户交互),二是让 Agent 操作 Slack(发消息、管理频道、添加反应)。本节覆盖两种场景。

工具推荐

工具用途价格适用场景
Slack Bot TokenBot 用户身份操作 Slack免费(Slack Free 计划)/ $8.75/人/月起(Pro)推荐方案,所有 Slack 操作
Slack App自定义 Slack 应用免费创建必需,用于获取 Bot Token
openclaw/slack Skill官方 Slack Skill免费(开源)快速安装,覆盖消息/反应/Pin 操作
Slack Webhook入站 Webhook(仅发送)免费仅需单向通知时的简化方案

前置条件

  1. Slack 工作区(Free 计划即可)
  2. Slack 工作区管理员权限(用于安装 App)

配置步骤

步骤 1:创建 Slack App

# 1. 访问 https://api.slack.com/apps # 2. 点击 "Create New App" → "From scratch" # 3. 填写 App 名称(如 "OpenClaw Agent") # 4. 选择目标 Workspace

步骤 2:配置 Bot Token Scopes

# 在 Slack App 设置页面: # → OAuth & Permissions → Scopes → Bot Token Scopes # 添加以下最小权限集: # 必需 Scopes: # - chat:write → 发送消息(核心功能) # - channels:history → 读取公共频道消息历史 # - groups:history → 读取私有频道消息历史 # - im:history → 读取私聊消息 # - mpim:history → 读取群组私聊消息 # - users:read → 解析 @mentions # - app_mentions:read → 检测 @bot 提及 # 可选 Scopes(按需添加): # - reactions:write → 添加 emoji 反应 # - reactions:read → 读取反应 # - pins:write → 置顶消息 # - pins:read → 读取置顶 # - files:write → 上传文件 # - channels:read → 列出频道

步骤 3:安装 App 到 Workspace

# 在 Slack App 设置页面: # → OAuth & Permissions → Install to Workspace # → 授权后复制 Bot User OAuth Token(以 xoxb- 开头)

步骤 4:配置 OpenClaw Slack 通道

// openclaw.json { "channels": { "slack": { "enabled": true, "token": "${SLACK_BOT_TOKEN}", "appToken": "${SLACK_APP_TOKEN}", "channels": ["#general", "#engineering", "#alerts"], "allowedUsers": ["U01234ABCDE", "U05678FGHIJ"] } } }
# 设置环境变量 export SLACK_BOT_TOKEN="xoxb-your-bot-token" export SLACK_APP_TOKEN="xapp-your-app-token" # Socket Mode 需要 # 启用 Socket Mode(推荐,无需公网 URL): # → Slack App 设置 → Socket Mode → Enable # → 生成 App-Level Token(xapp- 开头)

步骤 5:安装 Slack Skill

# 安装官方 Slack Skill npx playbooks add skill openclaw/openclaw --skill slack

完整 Skill 示例

自定义 Slack 团队助手 Skill

<!-- ~/.openclaw/skills/slack-team/SKILL.md --> # Slack 团队助手 通过 Slack 管理团队沟通:发送通知、管理频道消息、添加反应、置顶重要信息。 ## 前置条件 - OpenClaw Slack 通道已配置并连接 - Bot 已被邀请到目标频道(在频道中 /invite @OpenClaw) ## 使用场景 - 用户说"发消息到 #engineering" - 用户说"总结 #general 的讨论" - 用户说"置顶这条消息" - 用户说"给那条消息加个 ✅" ## 操作指南 ### 发送消息 使用 OpenClaw 内置的 slack 工具: - 发送到频道:`sendMessage` action,target 为 `channel:<channelId>` - 发送私聊:`sendMessage` action,target 为 `user:<userId>` - 回复线程:包含 `threadTs` 参数 ### 读取消息历史 - 使用 `readMessages` action 获取频道最近消息 - 指定 `channelId` 和消息数量 - 用于在回复前了解上下文 ### 添加反应 - 使用 `react` action - 需要 `channelId``messageId`(Slack 时间戳格式如 1712023032.1234) - 支持 Unicode emoji 或 `:name:` 格式 - 适合低噪音的确认(比发送文字回复更安静) ### 管理置顶 - `pinMessage`:置顶重要消息(决策、规范、周报) - `unpinMessage`:取消置顶 - `listPins`:查看频道所有置顶消息 ### 查询成员信息 - `memberInfo`:获取用户详情(名称、头像、状态) - 用于在消息中正确 @mention 用户 ## 消息格式注意事项 - Slack 使用 mrkdwn 格式(不是标准 Markdown) - 粗体:`*text*`(不是 **text** - 斜体:`_text_` - 代码:`` `code` `````code block``` - 链接:`<https://url|显示文本>` - @mention:`<@userId>` - 频道引用:`<#channelId>` - 消息长度限制:40,000 字符 ## 安全规则 - 不要在公共频道发送包含密钥、密码的消息 - 批量发送消息时注意速率限制,避免刷屏 - 删除消息操作需要用户确认

Plugin 模式:Slack 事件驱动自动化

当你需要 Slack 事件(特定关键词、特定频道消息)触发 Agent 复杂行为时:

// plugins/slack-automation/src/index.ts import type { PluginApi } from '@openclaw/types'; export function register(api: PluginApi) { // Slack 频道摘要工具 api.registerTool({ name: 'slack-channel-digest', description: '生成 Slack 频道的消息摘要(最近 N 小时)', parameters: { channelId: { type: 'string', description: 'Slack 频道 ID' }, hours: { type: 'number', description: '回溯小时数', optional: true } }, execute: async ({ channelId, hours = 24 }) => { const token = process.env.SLACK_BOT_TOKEN; const oldest = Math.floor( (Date.now() - hours * 3600 * 1000) / 1000 ).toString(); // 获取消息历史 const res = await fetch( 'https://slack.com/api/conversations.history?' + new URLSearchParams({ channel: channelId, oldest, limit: '200' }), { headers: { 'Authorization': `Bearer ${token}` } } ); const data = await res.json(); if (!data.ok) { return { error: data.error, hint: '确认 Bot 已被邀请到该频道' }; } // 获取用户信息用于显示名称 const userIds = [...new Set( data.messages.map((m: { user: string }) => m.user) )]; const users: Record<string, string> = {}; for (const uid of userIds) { const userRes = await fetch( `https://slack.com/api/users.info?user=${uid}`, { headers: { 'Authorization': `Bearer ${token}` } } ); const userData = await userRes.json(); if (userData.ok) { users[uid as string] = userData.user.real_name || userData.user.name; } } const messages = data.messages .reverse() .map((m: { user: string; text: string; ts: string }) => ({ author: users[m.user] || m.user, text: m.text, time: new Date(parseFloat(m.ts) * 1000).toISOString() })); return { channel: channelId, period: `最近 ${hours} 小时`, messageCount: messages.length, messages, summary: `共 ${messages.length} 条消息,来自 ${Object.keys(users).length} 位成员` }; } }); // Slack 通知路由工具 api.registerTool({ name: 'slack-smart-notify', description: '根据紧急程度智能路由 Slack 通知', parameters: { message: { type: 'string', description: '通知内容' }, severity: { type: 'string', description: '紧急程度', enum: ['info', 'warning', 'critical'] }, team: { type: 'string', description: '目标团队', enum: ['engineering', 'product', 'ops'] } }, execute: async ({ message, severity, team }) => { const token = process.env.SLACK_BOT_TOKEN; // 路由规则 const routing: Record<string, Record<string, string>> = { engineering: { info: '#eng-general', warning: '#eng-alerts', critical: '#eng-incidents' }, product: { info: '#product', warning: '#product-alerts', critical: '#product-urgent' }, ops: { info: '#ops-log', warning: '#ops-alerts', critical: '#ops-incidents' } }; const channel = routing[team]?.[severity] || '#general'; const emoji = { info: 'ℹ️', warning: '⚠️', critical: '🚨' }[severity]; const formattedMessage = `${emoji} *[${severity.toUpperCase()}]* ${message}`; const res = await fetch('https://slack.com/api/chat.postMessage', { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ channel, text: formattedMessage, unfurl_links: false }) }); const data = await res.json(); return { sent: data.ok, channel, severity, timestamp: data.ts }; } }); }

使用场景

场景实现方式示例指令
频道消息摘要Plugin + Cron”每天 18:00 总结 #engineering 的讨论”
智能通知路由Plugin”发送 critical 告警到 ops 团队”
消息确认反应Slack Skill”给那条部署消息加个 ✅“
置顶周报Slack Skill”把周报置顶到 #engineering”
跨频道广播Plugin”把这个公告发到所有团队频道”
站会提醒Cron + Slack 通道”每天 9:55 提醒 #team 准备站会”

Slack 集成安全注意事项

  1. 最小 Scope 原则:只申请实际需要的 Bot Token Scopes,避免过度授权
  2. Socket Mode 优先:使用 Socket Mode 而非 Event Subscriptions,无需暴露公网 URL
  3. 频道白名单:在 openclaw.json 中限制 Bot 可操作的频道列表
  4. 用户白名单:限制哪些用户可以通过 Slack 与 Agent 交互
  5. 公共频道安全:理解 OpenClaw 7 层策略栈中 Group 层的作用——公共频道自动剥离高风险工具
  6. 线程回复:优先在线程中回复,避免刷屏主频道

5. 集成开发通用模式

工具推荐表

工具/服务认证方式Skill 复杂度Plugin 必要性价格
GmailOAuth 2.0中(需 gog CLI)仅实时推送需要免费(API 配额内)
NotionAPI Key低(curl 即可)仅批量操作需要免费
GitHubPAT / GitHub App低(gh CLI)仅 Webhook 需要免费
SlackBot Token低(内置工具)仅复杂自动化需要免费 / $8.75/人/月起
Google CalendarOAuth 2.0(同 Gmail)低(gog CLI)免费
Google DriveOAuth 2.0(同 Gmail)低(gog CLI)免费
TodoistAPI Token免费 / $5/月
LinearAPI Key免费 / $10/人/月
JiraAPI Token免费(10 人以下)/ $8.15/人/月
AirtablePAT免费 / $20/人/月

认证模式对比

认证方式安全性配置复杂度Token 有效期适用场景
API Key⭐⭐⭐永久(手动撤销)Notion、Todoist、Linear
PAT(Personal Access Token)⭐⭐⭐⭐可设过期时间GitHub、Jira、Airtable
OAuth 2.0⭐⭐⭐⭐⭐刷新 token 自动续期Gmail、Google Workspace
Bot Token⭐⭐⭐⭐永久(App 级别)Slack、Discord、Telegram
Service Account⭐⭐⭐⭐⭐自动管理Google Cloud 服务间调用
Webhook Secret⭐⭐⭐永久入站 Webhook 签名验证

认证方式选择决策

需要用户级别的操作? ├── 是 → 用户数据敏感? │ ├── 是 → OAuth 2.0(Gmail、Google Workspace) │ └── 否 → PAT 或 API Key(GitHub、Notion) └── 否 → 应用级别操作? ├── 是 → Bot Token(Slack、Discord) └── 否 → Service Account(服务间调用)

错误处理模式

所有集成都应遵循统一的错误处理模式:

// 通用错误处理模板 async function safeApiCall<T>( name: string, fn: () => Promise<T>, retries = 3 ): Promise<T | { error: string; retryable: boolean }> { for (let attempt = 1; attempt <= retries; attempt++) { try { return await fn(); } catch (err: unknown) { const error = err as { status?: number; message?: string }; // 速率限制 → 指数退避重试 if (error.status === 429) { const delay = Math.pow(2, attempt) * 1000; console.log( `[${name}] 速率限制,${delay}ms 后重试(${attempt}/${retries})` ); await new Promise(r => setTimeout(r, delay)); continue; } // 认证失败 → 不重试 if (error.status === 401 || error.status === 403) { return { error: `认证失败:${error.message}。请检查 API Key/Token 是否有效。`, retryable: false }; } // 资源不存在 → 不重试 if (error.status === 404) { return { error: `资源不存在:${error.message}。请检查 ID 是否正确。`, retryable: false }; } // 服务器错误 → 重试 if (error.status && error.status >= 500) { if (attempt === retries) { return { error: `服务暂时不可用(${error.status}),已重试 ${retries} 次。`, retryable: true }; } await new Promise(r => setTimeout(r, Math.pow(2, attempt) * 1000) ); continue; } // 其他错误 return { error: `未知错误:${error.message || String(err)}`, retryable: false }; } } return { error: '重试次数耗尽', retryable: false }; }

各服务速率限制参考

服务速率限制建议间隔重试策略
Gmail API250 配额单位/秒无需特别处理429 时指数退避
Notion API~3 请求/秒350ms429 时等待 1 秒
GitHub API5000 请求/小时(认证)无需特别处理检查 X-RateLimit-Remaining
Slack APITier 1-4 不等1 秒/请求(保守)429 时读取 Retry-After

实战案例:构建多服务集成的自动化工作流

以下案例展示如何将 Gmail、Notion、GitHub 和 Slack 四个服务串联,构建一个完整的”客户反馈处理”自动化工作流。

场景描述

当客户通过邮件发送反馈时,Agent 自动:

  1. 从 Gmail 提取反馈内容
  2. 在 Notion 的反馈数据库中创建记录
  3. 如果是 Bug 报告,在 GitHub 创建 Issue
  4. 在 Slack #product 频道通知团队

工作流配置

HEARTBEAT.md 配置

# 客户反馈处理 ## 每 30 分钟 - 搜索 Gmail 中标签为 "customer-feedback" 的未处理邮件 - 对每封反馈邮件: 1. 分析邮件内容,判断类型(Bug / 功能请求 / 一般反馈) 2. 在 Notion "客户反馈" 数据库中创建记录 3. 如果是 Bug:在 GitHub 创建 Issue,标签为 "customer-reported" 4. 在 Slack #product 频道发送摘要通知 5. 给邮件添加 "processed" 标签 - 如果没有新反馈,回复 HEARTBEAT_OK

Skill 组合配置

// openclaw.json { "agents": { "list": [ { "id": "feedback-bot", "agentDir": "~/.openclaw/agents/feedback", "model": "anthropic/claude-sonnet-4-5", "tools": { "profile": "coding", "allow": [ "group:web", "group:fs", "slack", "notion-batch-create", "github-pr-analyze" ] }, "heartbeat": { "every": "30m", "model": "anthropic/claude-haiku-4", "target": "slack", "to": "#product", "activeHours": { "start": "08:00", "end": "20:00" } } } ] } }

Agent IDENTITY.md

# Identity ## Definition 我是客户反馈处理助手,负责从 Gmail 收集客户反馈, 分类后同步到 Notion 和 GitHub,并通知产品团队。 ## Rules | # | Rule | |---|------| | 1 | 每封反馈邮件只处理一次(检查 "processed" 标签) | | 2 | Bug 报告必须同时创建 GitHub Issue | | 3 | 所有反馈都记录到 Notion | | 4 | 紧急 Bug(影响核心功能)立即通知,其他批量通知 | | 5 | 不自动回复客户邮件,只做内部处理 | ## Decision Table | 反馈类型 | Notion 状态 | GitHub | Slack 通知 | |---------|------------|--------|-----------| | Bug(紧急) | 🔴 紧急 | Issue + label:critical | 🚨 立即通知 | | Bug(一般) | 🟡 待处理 | Issue + label:bug | 批量摘要 | | 功能请求 | 🔵 评估中 | 不创建 | 批量摘要 | | 一般反馈 | ⚪ 已记录 | 不创建 | 仅日报 |

案例分析

这个多服务集成案例展示了几个关键设计模式:

  1. Skill 优先,Plugin 补充:四个服务中,Gmail(gog Skill)、GitHub(gh CLI Skill)、Slack(内置工具)都通过 Skill 实现,只有 Notion 批量操作使用了 Plugin
  2. Heartbeat 驱动:使用 30 分钟心跳轮询新邮件,而非实时推送——对于反馈处理场景,30 分钟延迟完全可接受,且大幅降低了架构复杂度
  3. 双模型策略:心跳检查用 Haiku(便宜),实际处理反馈时切换到 Sonnet(更强推理)
  4. 幂等处理:通过 Gmail 标签(“processed”)确保每封邮件只处理一次,避免重复创建 Notion 记录和 GitHub Issue
  5. 分级通知:紧急 Bug 立即通知,一般反馈批量汇总,避免通知疲劳

避坑指南

❌ 常见错误

  1. OAuth Token 7 天过期

    • 问题:在 Google Cloud Console 中创建 OAuth 应用后没有点击 “Publish App”,导致 token 在 7 天后过期,Agent 突然无法访问 Gmail
    • 正确做法:创建 OAuth 应用后务必点击 “Publish App”。虽然 Google 会显示”未验证应用”警告,但这是你自己的应用,发布后 token 永久有效
  2. Notion Integration 未授权页面

    • 问题:创建了 Notion Integration 并配置了 API Key,但忘记在目标页面/数据库中点击 “Connect to” 授权,导致 API 返回 404
    • 正确做法:对每个需要 Agent 访问的页面,手动点击 ”…” → “Connect to” → 选择 Integration。子页面会自动继承父页面的授权
  3. GitHub PAT 权限过大

    • 问题:使用 Classic PAT 并勾选了所有权限,相当于给了 Agent 完整的 GitHub 账号访问权限
    • 正确做法:使用 Fine-grained PAT,只选择需要的仓库和最小权限集。定期轮换 Token(建议 90 天)
  4. Slack Bot 未被邀请到频道

    • 问题:配置了 Slack 集成,但 Bot 没有被邀请到目标频道,导致发送消息失败(not_in_channel 错误)
    • 正确做法:在每个目标频道中执行 /invite @YourBotName。对于私有频道,必须由频道成员手动邀请
  5. 在公共频道暴露敏感信息

    • 问题:Agent 在 Slack 公共频道中显示了邮件内容、API Key 或客户个人信息
    • 正确做法:在 Skill 的安全规则中明确禁止在公共通道显示敏感信息;利用 OpenClaw 的 Group 层策略自动限制公共频道的工具权限
  6. 忽略 API 速率限制导致请求被拒

    • 问题:批量操作时(如从 Notion 查询大量数据)没有添加请求间隔,触发速率限制后所有请求失败
    • 正确做法:Notion API 限制约 3 请求/秒,批量操作时每个请求间隔至少 350ms;实现指数退避重试逻辑

✅ 最佳实践

  1. Skill 优先原则:大多数集成用 Skill(Markdown)即可实现,只有需要持久连接、复杂认证流或实时事件处理时才使用 Plugin
  2. 凭证分离存储:API Key 和 Token 存储在 ~/.config/ 或环境变量中,不要硬编码在 Skill 文件里,更不要提交到 Git
  3. 最小权限:每个服务只申请实际需要的权限。Gmail 只读用 gmail.readonly,GitHub 只需 PR 审查就不要给 write 权限
  4. 幂等设计:所有自动化操作都应该是幂等的——重复执行不会产生重复数据。使用标签、状态字段或去重逻辑
  5. 错误降级:当某个服务不可用时,不应阻塞整个工作流。例如 Notion 挂了,仍然可以创建 GitHub Issue 和发送 Slack 通知
  6. 日志和审计:在 Agent 的 MEMORY.md 中记录每次集成操作的结果,便于排查问题和审计

相关资源与延伸阅读

资源类型说明
OpenClaw Setup Guide: 25 Tools + 53 Skills Explained 综合教程OpenClaw 工具和 Skill 体系全面解析,覆盖 gog、notion、github、slack 等核心 Skill
Automate Your Gmail Inbox with OpenClaw in 25 Minutes Gmail 教程Gmail OAuth 配置和自动化的快速入门指南
OpenClaw Gmail Pub/Sub Integration for Real-Time Inbox Automation 高级教程Gmail 实时推送集成,使用 Google Pub/Sub 实现新邮件即时通知
Sync OpenClaw with Notion & Obsidian in 20 Minutes Notion 教程Notion 和 Obsidian 双向同步配置指南
OpenClaw GitHub PR Review Automation Guide GitHub 教程GitHub PR 自动审查配置,含 diff 分析和审查摘要生成
Configure OpenClaw with Slack in 20 Minutes Slack 教程Slack 集成快速配置指南,覆盖 Bot Token 和 Socket Mode
OpenClaw Slack Setup: Threads, Permissions, and Production-Safe Defaults Slack 深度指南Slack 权限最小化、线程行为和生产环境安全配置
OpenClaw Custom Skill Creation Guide Skill 开发自定义 Skill 编写完整指南,含模板和最佳实践
Exploring the OpenClaw Extension Ecosystem: 50+ Official Integrations 生态概览OpenClaw 50+ 官方集成的完整目录和分类介绍
ClawHub Skills Marketplace Developer Guide 市场指南ClawHub Skill 市场使用和发布指南,3000+ 社区 Skill

参考来源


📖 返回 总览与导航 | 上一节:24b-自定义Agent开发教程 | 下一节:24d-Agent间通信

Last updated on