关于 google-chat-messages
google-chat-messages 是通过 Google Chat 入站 Webhook 发送消息的技能,适用于通知和结构化更新。它支持文本、cardsV2 卡片和带线程的回复,并提供 TypeScript 构建与发送工具。
主要功能
- 01发送格式化文本消息
- 02构建 cardsV2 富卡片
- 03发送线程回复
- 04使用 TypeScript Webhook 工具
适用场景
- 01发布部署状态通知
- 02发送每周摘要卡片
- 03在同一线程中更新告警
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jezweb/ claude-skills --skill google-chat-messages安装后在 Claude Code 中输入/google-chat-messages确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/card-builder.tstypes.tswebhook-sender.ts详细介绍
Google Chat 消息
通过传入 Webhook 向 Google Chat 空间发送消息。可生成文本消息、富卡片(cardsV2)和线程回复。
可生成内容
- 带有 Google Chat 格式的文本消息
- 富卡片消息(cardsV2),包含标题、分区和组件
- 线程对话
- 可复用的 Webhook 发送工具
工作流
步骤 1:获取 Webhook URL
在 Google Chat 中:
- 打开一个空间 > 点击空间名称 > 管理 Webhook
- 创建 Webhook(为其命名,可选添加头像 URL)
- 复制 Webhook URL
将 URL 存储为环境变量或保存在密钥管理器中,切勿硬编码。
步骤 2:选择消息类型
| 需求 | 类型 | 复杂度 |
|---|---|---|
| 简单通知 | 文本消息 | 低 |
| 结构化信息(状态、摘要) | 卡片消息(cardsV2) | 中 |
| 持续更新 | 线程回复 | 中 |
| 操作按钮(打开 URL) | 带 buttonList 的卡片 | 中 |
步骤 3:发送消息
使用 assets/webhook-sender.ts 作为发送工具。使用 assets/card-builder.ts 构建结构化卡片。
文本格式
Google Chat 不使用标准 Markdown。
| 格式 | 语法 | 示例 |
|---|---|---|
| 粗体 | *text* | *important* |
| 斜体 | _text_ | _emphasis_ |
| 删除线 | ~text~ | ~removed~ |
| 等宽文本 | `text` | `code` |
| 代码块 | ```text``` | 多行代码 |
| 链接 | <url|text> | <https://example.com|Click here> |
| 提及用户 | <users/USER_ID> | <users/123456> |
| 提及所有人 | <users/all> | <users/all> |
不支持:**双星号**、标题(###)、引用块、表格、内嵌图片。
文本消息示例
await sendText(webhookUrl, '*Build Complete*\n\nBranch: `main`\nStatus: Passed\n<https://ci.example.com/123|View Build>');
cardsV2 结构
卡片使用 cardsV2 格式(推荐使用,而非旧版卡片)。
const message = {
cardsV2: [{
cardId: 'unique-id',
card: {
header: {
title: 'Card Title',
subtitle: 'Optional subtitle',
imageUrl: 'https://example.com/icon.png',
imageType: 'CIRCLE' // or 'SQUARE'
},
sections: [{
header: 'Section Title', // optional
widgets: [
// widgets go here
]
}]
}
}]
};
组件参考
cardsV2 分区中提供的所有组件类型。
textParagraph
格式化文本块。支持 Google Chat 格式(*粗体*、_斜体_、<url|text>)。
{
textParagraph: {
text: '*Status*: All systems operational\n_Last checked_: 5 minutes ago'
}
}
decoratedText
带可选图标的标签值。用于键值数据的最通用组件。
基本用法:
{
decoratedText: {
topLabel: 'Environment',
text: 'Production',
bottomLabel: 'Last deployed 2h ago'
}
}
带起始图标:
{
decoratedText: {
topLabel: 'Status',
text: 'Healthy',
startIcon: { knownIcon: 'STAR' }
}
}
带自定义图标 URL:
{
decoratedText: {
topLabel: 'GitHub',
text: 'PR #142 merged',
startIcon: {
iconUrl: 'https://github.githubassets.com/favicons/favicon.svg',
altText: 'GitHub'
}
}
}
带按钮:
{
decoratedText: {
topLabel: 'Alert',
text: 'CPU at 95%',
button: {
text: 'View',
onClick: { openLink: { url: 'https://monitoring.example.com' } }
}
}
}
可点击(整个组件):
{
decoratedText: {
text: 'View full report',
wrapText: true,
onClick: { openLink: { url: 'https://reports.example.com' } }
}
}
带换行文本:
{
decoratedText: {
topLabel: 'Description',
text: 'This is a longer description that should wrap to multiple lines instead of being truncated',
wrapText: true
}
}
buttonList
一个或多个操作按钮。按钮可打开 URL 或触发操作。
单个按钮:
{
buttonList: {
buttons: [{
text: 'Open Dashboard',
onClick: { openLink: { url: 'https://dashboard.example.com' } }
}]
}
}
多个按钮:
{
buttonList: {
buttons: [
{
text: 'Approve',
onClick: { openLink: { url: 'https://app.example.com/approve/123' } },
color: { red: 0, green: 0.5, blue: 0, alpha: 1 }
},
{
text: 'Reject',
onClick: { openLink: { url: 'https://app.example.com/reject/123' } }
}
]
}
}
带图标的按钮:
{
buttonList: {
buttons: [{
text: 'View on GitHub',
icon: { knownIcon: 'BOOKMARK' },
onClick: { openLink: { url: 'https://github.com/org/repo/pull/42' } }
}]
}
}
image
独立的图片组件。
{
image: {
imageUrl: 'https://example.com/chart.png',
altText: 'Monthly usage chart'
}
}
divider
用于分隔组件的水平线。
{ divider: {} }
可折叠分区
可将分区设为可折叠,仅显示前 N 个组件:
{
header: 'Details',
collapsible: true,
uncollapsibleWidgetsCount: 2, // Show first 2, collapse rest
widgets: [
{ decoratedText: { topLabel: 'Status', text: 'Active' } },
{ decoratedText: { topLabel: 'Region', text: 'AU' } },
// These start collapsed
{ decoratedText: { topLabel: 'Instance', text: 'prod-01' } },
{ decoratedText: { topLabel: 'Memory', text: '2.1 GB' } },
{ decoratedText: { topLabel: 'CPU', text: '45%' } }
]
}
已知图标
decoratedText 和按钮组件可通过 knownIcon 使用的图标。
{ startIcon: { knownIcon: 'STAR' } }
// or
{ icon: { knownIcon: 'EMAIL' } }
| 图标名称 | 用途 |
|---|---|
AIRPLANE | 旅行、航班 |
BOOKMARK | 保存、参考、链接 |
BUS | 交通、公共交通 |
CAR | 驾驶、交通 |
CLOCK | 时间、时长、日程 |
CONFIRMATION_NUMBER_ICON | 门票、预订 |
DESCRIPTION | 文档、文件 |
DOLLAR | 金钱、定价、费用 |
EMAIL | 电子邮件、消息 |
INVITE | 邀请 |
MAP_PIN | 位置、地址 |
MEMBERSHIP | 成员、用户 |
MULTIPLE_PEOPLE | 团队、群组 |
OFFER | 优惠、促销 |
PERSON | 个人用户 |
PHONE | 电话号码、通话 |
SHOPPING_CART | 商业、购物 |
STAR | 评分、收藏、重要事项 |
STORE | 商店、零售 |
TICKET | 门票、活动 |
VIDEO_CAMERA | 视频、会议 |
对于列表中没有的图标,可将 iconUrl 与任何可公开访问的图片搭配使用(正方形,建议尺寸为 24x24 或 48x48 像素)。
线程
使用 threadKey 将消息串联成线程:
// First message — creates thread
const response = await sendCard(webhookUrl, card, {
threadKey: 'deploy-2026-02-16'
});
// Reply to thread — append &messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD
const threadUrl = `${webhookUrl}&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD`;
await sendCard(threadUrl, replyCard, {
threadKey: 'deploy-2026-02-16'
});
threadKey 是由客户端分配的字符串。相关消息应使用一致的键(例如 deploy-{date}、alert-{id})。
常见模式
通知卡片
import { buildCard, sendCard } from './assets/card-builder';
import { sendWebhook } from './assets/webhook-sender';
const card = buildCard({
cardId: 'deploy-notification',
title: 'Deployment Complete',
subtitle: 'production - v2.1.0',
imageUrl: 'https://example.com/your-icon.png',
sections: [{
widgets: [
{ decoratedText: { topLabel: 'Environment', text: 'Production' } },
{ decoratedText: { topLabel: 'Version', text: 'v2.1.0' } },
{ decoratedText: { topLabel: 'Status', text: '*Healthy*', startIcon: { knownIcon: 'STAR' } } },
{ buttonList: { buttons: [{ text: 'View Deployment', onClick: { openLink: { url: 'https://dash.example.com' } } }] } }
]
}]
});
摘要卡片(每周总结)
const digest = buildCard({
cardId: 'weekly-digest',
title: 'Weekly Summary',
subtitle: `${count} updates this week`,
sections: [
{
header: 'Highlights',
widgets: items.map(item => ({
decoratedText: { text: item.title, bottomLabel: item.date }
}))
},
{
widgets: [{
buttonList: {
buttons: [{ text: 'View All', onClick: { openLink: { url: dashboardUrl } } }]
}
}]
}
]
});
错误预防
| 错误 | 修复方法 |
|---|---|
文本中使用 **bold** | 使用 *bold*(单个星号) |
使用 [text](url) 链接 | 使用 <url|text> 格式 |
缺少 cardsV2 封装 | 将卡片封装在 { cardsV2: [{ cardId, card }] } 中 |
| 线程回复未进入线程 | 将 &messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD 附加到 webhook URL |
| Webhook 返回 400 | 检查 JSON 结构——常见问题是顶层缺少 text 或 cardsV2 |
| 卡片未显示 | 确保 sections 至少包含一个 widget |
资源文件
| 文件 | 用途 |
|---|---|
assets/types.ts | cardsV2 的 TypeScript 类型定义 |
assets/card-builder.ts | 用于构建卡片消息的实用工具 |
assets/webhook-sender.ts | 向 webhook 发送 POST 请求并处理错误 |