基于飞书机器人框架实现GitLab MR自动化通知的实战指南
1. 项目概述:一个为飞书群聊注入智能的“机器人管家”
如果你在团队协作中使用飞书,大概率遇到过这样的场景:一个重要的项目文档更新了,你需要手动@所有人;每天早晨,你需要把当天的待办事项整理成消息发到群里;或者,每当代码仓库有新的提交、服务器出现告警时,你希望这些信息能第一时间自动同步到对应的飞书群,而不是依赖人工转发。这些重复、琐碎但又至关重要的信息同步工作,正是rawchen/feishu-bot这个项目所要解决的痛点。
简单来说,feishu-bot是一个开源的自定义机器人框架,它允许开发者通过编写简单的代码,将外部系统(如GitHub、Jenkins、监控平台、内部业务系统)的事件或数据,自动转化为飞书群聊中的富文本消息。它不是一个现成的、功能固定的机器人产品,而是一套“乐高积木”式的工具包。你基于它提供的核心能力——主要是与飞书开放平台机器人API的通信封装——来搭建符合自己团队独特需求的自动化工作流。我最初接触它,是因为团队需要将GitLab的代码合并请求(Merge Request)通知到飞书,经过一番调研和折腾,发现这个项目结构清晰、文档友好,虽然需要一定的开发基础,但实现起来远比从零调用飞书API要高效和稳定。
它的核心价值在于“连接”与“自动化”。通过这个机器人,你可以让飞书群聊成为团队信息的聚合中枢,将散落在各处的状态更新、事件提醒集中呈现,减少上下文切换,提升协同效率。无论是研发、运维、产品还是运营团队,都能找到它的用武之地。接下来,我将结合自己从零搭建一个MR通知机器人的全过程,拆解这个项目的设计思路、核心细节、实操步骤以及那些只有踩过坑才知道的注意事项。
2. 项目核心架构与设计思路拆解
在动手写代码之前,理解feishu-bot的架构设计至关重要。这能帮助你在后续开发中做出正确的技术选型,并在遇到问题时快速定位。整个项目的设计可以概括为“一个核心,两种模式,三层结构”。
2.1 核心定位:飞书开放平台的“友好封装层”
飞书开放平台为机器人提供了完善的Webhook和API能力,但直接使用原生的HTTP接口进行开发,开发者需要处理诸多细节:身份认证(获取tenant_access_token)、请求签名、消息格式组装(飞书支持复杂的卡片消息)、错误重试、速率限制等。feishu-bot项目最根本的价值,就是将这些底层、重复的复杂性封装起来,暴露给开发者一个简洁、易用的编程接口(通常是函数或类方法)。
例如,原生API发送一条文本消息可能需要你自己构建JSON体、计算签名、管理token生命周期。而使用feishu-bot,你可能只需要一行代码:bot.send_text(content=“Hello World”)。这种封装极大地降低了开发门槛和出错概率。项目源码中,你会看到对飞书API文档中各种消息类型(文本、富文本、卡片、图片等)的模型定义和客户端实现,这是其核心所在。
2.2 两种运行模式:Webhook与消息监听
根据机器人的能力配置,feishu-bot支持两种主要的交互模式,这也是飞书机器人本身的两种类型:
2.2.1 Outgoing Webhook模式(发送为主)这是最常用、也是最简单的模式。机器人被添加到群聊后,主要功能是“向外发送消息”。它监听外部事件(如GitHub的Webhook、定时任务触发),然后主动调用飞书API将消息推送到指定的群或用户。这种模式下,机器人通常不需要接收和处理群内的消息。feishu-bot的绝大部分功能都围绕此模式展开,提供了强大的消息发送和构建能力。我们构建的CI/CD通知、监控报警机器人,都属于这一类。
2.2.2 事件订阅模式(可接收与响应)这种模式更为强大,机器人不仅能够发送消息,还能“接收并处理”群内@它的消息,或是其他特定事件(如用户进入群聊)。这需要你在飞书开发者后台配置“事件订阅”URL,并在你的服务器上运行一个HTTP服务来接收飞书推送的事件。feishu-bot也提供了相应的框架来解析这些事件,帮助你构建交互式机器人,例如一个可以查询数据的问答机器人。不过,这种模式的部署和调试复杂度更高,需要公网可访问的服务器。
注意:对于初学者或大多数自动化场景,建议从Outgoing Webhook模式开始。它功能强大且实现简单,足以覆盖80%的需求。除非你明确需要机器人与用户进行复杂对话,否则不必一开始就挑战事件订阅模式。
2.3 三层结构:从配置到消息的清晰路径
在实际开发中,使用feishu-bot构建一个可用的机器人服务,其代码结构通常会自然形成三个层次:
- 配置与初始化层:这一层负责读取机器人的关键凭证(App ID, App Secret),并初始化
feishu-bot的客户端。这些凭证是机器人在飞书开放平台的“身份证”,必须妥善保管,通常通过环境变量或配置文件读取,绝不能硬编码在代码中。 - 业务逻辑与消息构建层:这是你的核心代码所在。你在这里编写逻辑,处理外部输入(例如解析GitLab Webhook的JSON数据),并利用
feishu-bot提供的消息构建器,创建出内容丰富、格式美观的飞书消息。这一层决定了机器人发送什么、以及以何种形式发送。 - 触发与执行层:这一层决定了机器人“何时”以及“如何”被触发。常见的方式有:
- HTTP Webhook端点:你的服务暴露一个API端点,接收来自GitLab、Jenkins等系统的Webhook调用。
- 定时任务(Cron Job):使用
crontab或Celery等工具定时执行你的脚本,用于发送日报、周报等。 - 消息队列消费者:从Kafka、RabbitMQ等队列中消费事件,再触发消息发送,适用于高并发或解耦场景。
这样的分层设计使得代码职责清晰,易于维护和扩展。当你需要增加一个新的通知类型时,通常只需要在第二层添加新的消息构建函数,并在第三层注册一个新的触发入口即可。
3. 从零开始:构建一个GitLab Merge Request通知机器人
理论讲得再多,不如动手实践。下面我将详细演示如何利用feishu-bot,构建一个实用的GitLab Merge Request通知机器人。当有新的MR被创建、更新、合并或关闭时,机器人会在指定的飞书群中发送一条格式化的通知卡片。
3.1 环境准备与飞书应用创建
3.1.1 飞书侧配置这是第一步,也是最容易出错的一步。请严格按照以下流程操作:
- 登录飞书开发者后台:访问飞书开放平台,使用有权限创建应用的管理员账号登录。
- 创建企业自建应用:在“应用开发”下选择“企业自建应用”,点击创建。应用名称可以定为“GitLab通知机器人”,描述按需填写。
- 获取凭证:创建成功后,在“凭证与基础信息”页面,你会看到
App ID和App Secret。请立即将它们记录下来,后续代码中会用到。这是机器人的核心密钥。 - 添加机器人能力:在“功能”菜单下,找到“机器人”,点击“启用”。
- 配置权限:在“权限管理”页面,为机器人添加必要的权限。对于发送群消息,至少需要添加
im:message权限下的“发送消息”、“发送群聊消息”等。请仔细阅读权限说明,按需添加。添加后,记得点击“申请线上发布”或“版本管理与发布”来创建新版本并申请审核(企业自用通常管理员自审即可)。 - 将机器人添加到群聊:在飞书客户端,进入你希望接收通知的群聊。点击群设置 -> 群机器人 -> 添加机器人 -> 找到你刚创建的应用并添加。添加成功后,飞书会提供一个Webhook地址。这个地址是“群机器人”特有的,格式如
https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxx。这个地址可以直接用于发送消息到该群,且无需App Secret,但功能可能受限。我们更推荐使用基于App ID/Secret的API方式,因为它更灵活(可发往任何有权限的群)且功能更全。不过,这个Webhook地址可以用于快速测试。
3.1.2 开发环境准备假设我们使用Python进行开发,这是feishu-bot的主要语言环境。
# 1. 创建项目目录 mkdir feishu-gitlab-bot && cd feishu-gitlab-bot # 2. 创建虚拟环境(推荐) python -m venv venv source venv/bin/activate # Linux/Mac # venv\Scripts\activate # Windows # 3. 安装 feishu-bot 核心库 # 注意:项目 rawchen/feishu-bot 可能是一个仓库,其PyPI包名可能不同。 # 通常可以通过pip直接从GitHub安装,或者查找正确的包名。 # 假设包名为 `feishu-bot`(这里仅为示例,请以官方文档为准) pip install feishu-bot # 4. 安装其他依赖:用于处理Webhook的Web框架,如 FastAPI pip install fastapi uvicorn httpx3.2 核心代码实现与消息构建
接下来,我们编写核心的业务逻辑。我们将创建一个FastAPI应用,提供一个接收GitLab Webhook的端点,并利用feishu-bot构建和发送消息。
3.2.1 配置文件与环境变量首先,将敏感信息配置化。创建.env文件(需加入.gitignore):
# .env FEISHU_APP_ID=cli_xxxxxx FEISHU_APP_SECRET=xxxxxx # 可选:如果使用群Webhook快速测试,可以配置这个 FEISHU_WEBHOOK_URL=https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxx # 目标群聊的chat_id,可以通过API查询获取,后续会讲 TARGET_CHAT_ID=oc_xxxxxx3.2.2 初始化飞书客户端创建一个bot_client.py文件:
# bot_client.py import os from feishu_bot import FeishuBotClient # 假设类名如此,请根据实际库调整 from dotenv import load_dotenv load_dotenv() # 加载 .env 文件中的环境变量 class FeishuBot: def __init__(self): self.app_id = os.getenv('FEISHU_APP_ID') self.app_secret = os.getenv('FEISHU_APP_SECRET') self.chat_id = os.getenv('TARGET_CHAT_ID') if not all([self.app_id, self.app_secret]): raise ValueError("FEISHU_APP_ID 和 FEISHU_APP_SECRET 环境变量必须配置") # 初始化客户端,库内部会处理token的获取和刷新 self.client = FeishuBotClient(self.app_id, self.app_secret) def send_mr_card(self, mr_data): """ 根据GitLab MR数据构建并发送飞书卡片消息 mr_data: 字典,包含从GitLab Webhook解析出的MR信息 """ # 1. 构建卡片内容 # 飞书卡片是一种强大的富消息格式,这里构建一个简单的 card_content = { "config": {"wide_screen_mode": True}, "header": { "title": { "tag": "plain_text", "content": f"🔄 Merge Request: {mr_data.get('title', 'No Title')}" }, "template": "blue" # 蓝色标题,表示更新/进行中 }, "elements": [ { "tag": "div", "text": { "tag": "lark_md", # 支持Markdown "content": f"**仓库:** {mr_data.get('project', {}).get('name', 'N/A')}\n" f"**源分支:** `{mr_data.get('source_branch', 'N/A')}` → **目标分支:** `{mr_data.get('target_branch', 'N/A')}`\n" f"**提交者:** {mr_data.get('user', {}).get('name', 'N/A')}\n" f"**状态:** **{mr_data.get('state', 'opened').upper()}**\n" f"**链接:** [点击查看MR]({mr_data.get('url', '#')})" } }, { "tag": "action", "actions": [ { "tag": "button", "text": {"tag": "plain_text", "content": "查看详情"}, "type": "primary", "url": mr_data.get('url', '') } ] } ] } # 2. 发送消息到群聊 # 注意:需要群的chat_id。如何获取chat_id见下文“注意事项” resp = self.client.message.send_card( receive_id=self.chat_id, content=card_content ) return resp3.2.3 创建Webhook处理器创建main.py文件作为应用入口:
# main.py from fastapi import FastAPI, Request, HTTPException, Header import httpx import hashlib import hmac import json from bot_client import FeishuBot app = FastAPI(title="GitLab Feishu Bot") bot = FeishuBot() # 初始化我们的机器人 # 可选的GitLab Webhook Secret验证,增强安全性 GITLAB_WEBHOOK_SECRET = os.getenv('GITLAB_WEBHOOK_SECRET', '') def verify_gitlab_signature(payload_body, secret_token, signature_header): """验证GitLab Webhook签名""" if not secret_token: return True # 未配置secret则跳过验证 if not signature_header: return False # GitLab使用SHA256 digest = hmac.new(secret_token.encode('utf-8'), msg=payload_body, digestmod=hashlib.sha256).hexdigest() return hmac.compare_digest(f"sha256={digest}", signature_header) @app.post("/webhook/gitlab/mr") async def handle_gitlab_mr( request: Request, x_gitlab_token: str = Header(None), x_gitlab_event: str = Header(None) ): """ 处理GitLab Merge Request事件的Webhook端点 """ # 1. 读取原始body用于签名验证 raw_body = await request.body() # 2. 验证签名(如果配置了secret) signature = request.headers.get('X-Gitlab-Token') or x_gitlab_token if not verify_gitlab_signature(raw_body, GITLAB_WEBHOOK_SECRET, signature): raise HTTPException(status_code=403, detail="Invalid signature") # 3. 解析JSON数据 try: payload = await request.json() except json.JSONDecodeError: raise HTTPException(status_code=400, detail="Invalid JSON") # 4. 判断事件类型,我们只处理Merge Request事件 event_type = payload.get('object_kind') if event_type != 'merge_request': return {"status": "ignored", "reason": f"Event type '{event_type}' not handled"} # 5. 提取MR核心数据 mr_attributes = payload.get('object_attributes', {}) # 可以根据 action (open, update, merge, close) 进行更精细的处理 action = mr_attributes.get('action') if action not in ['open', 'reopen', 'update', 'merge', 'close']: return {"status": "ignored", "reason": f"Action '{action}' not notified"} # 6. 准备数据并调用机器人发送消息 mr_data = { "id": mr_attributes.get('iid'), "title": mr_attributes.get('title'), "state": mr_attributes.get('state'), # opened, merged, closed "url": mr_attributes.get('url'), "source_branch": mr_attributes.get('source_branch'), "target_branch": mr_attributes.get('target_branch'), "project": payload.get('project', {}), "user": payload.get('user', {}), "action": action } # 7. 根据action微调消息,例如合并成功时标题用绿色 # 这里简化处理,实际可以根据action修改card_content的header颜色等 try: resp = bot.send_mr_card(mr_data) return {"status": "success", "feishu_msg_id": resp.get('data', {}).get('message_id')} except Exception as e: # 记录日志 print(f"Failed to send Feishu message: {e}") raise HTTPException(status_code=500, detail="Failed to send notification") if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)3.3 部署、配置与联调测试
代码写好了,如何让它跑起来并真正工作?
3.3.1 获取目标群聊的Chat ID飞书API发送消息到群聊需要chat_id,这不是群名称。获取方法有两种:
- 通过API查询:调用
https://open.feishu.cn/open-apis/im/v1/chats?page_size=20接口(需要chat:read权限),从返回的列表中找到你的群。 - 通过机器人Webhook反推:如果你使用了群机器人的Webhook地址,地址末尾的
xxxxxx部分并不是chat_id。更可靠的方法是,先用这个Webhook发一条消息,然后在飞书开放平台的“事件订阅”中,订阅“接收消息”事件。当机器人在群里发言后,你会收到一个事件,其中就包含了该群的chat_id。记录下这个ID,填入环境变量。
3.3.2 配置GitLab Webhook
- 进入你的GitLab项目 -> Settings -> Webhooks。
- URL填写你部署好的服务地址,例如
https://your-server.com/webhook/gitlab/mr。 - Secret Token:生成一个随机字符串,填入,并在你的服务环境变量
GITLAB_WEBHOOK_SECRET中配置同样的值,以启用签名验证,防止伪造请求。 - 触发事件(Trigger):至少勾选 “Merge request events”。你也可以根据需要勾选其他事件。
- 点击“Add webhook”。添加后,可以点击“Test”按钮发送一个测试事件,检查你的服务日志和飞书群是否收到消息。
3.3.3 服务器部署你可以将代码部署到任何有公网IP的服务器或云函数上。
- 传统服务器:使用
nohup或systemd运行uvicorn main:app --host 0.0.0.0 --port 8000。更推荐使用Gunicorn作为生产级WSGI服务器:gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app。 - 容器化部署:编写Dockerfile,构建镜像后部署到Kubernetes或云厂商的容器服务。
- Serverless:非常适合这种Webhook处理器。你可以将代码部署到阿里云函数计算、腾讯云SCF或AWS Lambda,通常有免费的额度。
3.3.4 测试与调试
- 本地测试:运行
python main.py,使用ngrok或localtunnel将本地http://localhost:8000暴露为一个公网临时地址,用这个地址去配置GitLab Webhook进行测试。 - 查看日志:密切关注服务日志,查看GitLab的Webhook请求是否收到,飞书API的响应是什么。
- 飞书API调试工具:飞书开放平台提供了API调试工具,你可以手动构造请求,测试发送消息是否成功,这比通过代码调试更直接。
4. 进阶技巧与深度优化
一个能跑通的机器人只是开始,要让它稳定、可靠、易维护,还需要考虑更多。
4.1 消息模板化与个性化
直接在代码里拼接卡片JSON会很快变得难以维护。最佳实践是将消息模板化。
4.1.1 使用Jinja2模板将卡片结构写入一个单独的JSON或Jinja2模板文件。
# templates/mr_card.json.j2 { "config": {"wide_screen_mode": true}, "header": { "title": { "tag": "plain_text", "content": "{{ header_emoji }} Merge Request: {{ mr.title }}" }, "template": "{{ header_color }}" }, "elements": [ { "tag": "div", "text": { "tag": "lark_md", "content": "**仓库:** {{ mr.project.name }}\n**源分支:** `{{ mr.source_branch }}` → **目标分支:** `{{ mr.target_branch }}`\n**提交者:** {{ mr.user.name }}\n**状态:** **{{ mr.state.upper() }}**\n**链接:** [点击查看MR]({{ mr.url }})" } } {% if mr.assignees %} { "tag": "note", "elements": [{ "tag": "plain_text", "content": "👤 负责人: {{ mr.assignees | join(', ', attribute='name') }}" }] } {% endif %} ] }然后在代码中渲染:
from jinja2 import Environment, FileSystemLoader env = Environment(loader=FileSystemLoader('templates')) template = env.get_template('mr_card.json.j2') card_content = json.loads(template.render( mr=mr_data, header_emoji=get_emoji_by_action(mr_data['action']), header_color=get_color_by_state(mr_data['state']) ))4.1.2 动态颜色与图标根据MR状态(open, merged, closed)或动作(open, merge)动态改变卡片标题的颜色和图标,使消息一目了然。
def get_emoji_by_action(action): emoji_map = { 'open': '🆕', 'reopen': '🔄', 'update': '📝', 'merge': '✅', 'close': '❌' } return emoji_map.get(action, '📌') def get_color_by_state(state): color_map = { 'opened': 'blue', 'merged': 'green', 'closed': 'grey' } return color_map.get(state, 'blue')4.2 异步发送与错误处理
飞书API调用是网络I/O操作,应该使用异步方式,避免阻塞主线程,尤其是在处理多个并发Webhook时。
4.2.1 使用异步HTTP客户端在FeishuBot类中,使用httpx.AsyncClient或aiohttp.ClientSession。
import httpx from feishu_bot import AsyncFeishuBotClient # 假设有异步客户端 class AsyncFeishuBot: def __init__(self): self.client = AsyncFeishuBotClient(app_id, app_secret) self.http_client = httpx.AsyncClient(timeout=10.0) # 设置超时 async def send_message(self, content): # 使用异步客户端发送 async with self.http_client as client: # ... 调用异步版本的SDK方法 pass4.2.2 实现重试与降级机制网络请求可能失败,必须实现重试逻辑。可以使用tenacity库。
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type import httpx @retry( stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10), retry=retry_if_exception_type((httpx.RequestError, httpx.HTTPStatusError)) ) async def send_with_retry(self, content): resp = await self.client.send_card(receive_id=self.chat_id, content=content) # 检查响应码,飞书业务错误也可能需要重试 if resp.get('code') != 0: raise httpx.HTTPStatusError(f"Feishu API error: {resp}", request=None, response=None) return resp如果重试后仍然失败,可以考虑将消息存入一个本地队列(如Redis)或数据库,由后台任务稍后重试,或者发送一条简化的备用通知(如纯文本消息)。
4.3 安全与权限管理
- Webhook验证:如前所述,务必验证GitLab Webhook的签名(
X-Gitlab-Token),这是防止恶意请求的第一道防线。 - 飞书权限控制:在飞书开放后台,遵循最小权限原则,只授予机器人必要的权限。定期审计权限列表。
- 环境隔离:为开发、测试、生产环境配置不同的飞书应用和群聊,避免测试消息干扰生产群。
- Token管理:
feishu-bot客户端内部通常会缓存并自动刷新tenant_access_token。你需要确保你的服务实例是长期运行的,或者实现一个分布式的Token管理方案(如果有多实例部署),避免每个实例都去频繁申请新Token,触发频率限制。
5. 常见问题排查与实战心得
在实际部署和运维过程中,我遇到了不少坑,这里总结一下最常见的几个问题及其解决方案。
5.1 消息发送失败:错误码大全与排查
飞书API返回的错误码是定位问题的关键。以下是一些常见错误及解决方法:
| 错误码 (code) | 含义 | 可能原因与解决方案 |
|---|---|---|
| 99991663 | 租户令牌失效 | tenant_access_token过期。确保你的客户端实现了Token的自动刷新逻辑。检查服务器时间是否准确。 |
| 99991668 | 请求被限流 | 发送频率过高。飞书机器人有频率限制(约5条/秒)。需要实现消息队列进行缓冲,或降低发送频率。 |
| 99991401 | 参数错误 | 请求体格式不正确,特别是卡片消息的JSON结构有误。使用飞书开放平台的“消息卡片搭建工具”在线调试你的卡片JSON。 |
| 99991403 | 无权限 | 1. 机器人未添加到目标群。2. 机器人没有im:message相关权限。去开发者后台检查权限并发布新版本。3. 使用的chat_id不对。 |
| 99991664 | 内部错误 | 飞书服务端临时问题。通常重试即可。建议所有发送操作都包裹在重试逻辑中。 |
排查步骤:
- 检查日志:首先查看你的应用日志,确认是否收到了GitLab的Webhook,以及请求体是否解析正确。
- 隔离测试:写一个简单的测试脚本,直接调用
bot.send_text发送一条消息,看是否能成功。这可以排除Webhook处理逻辑的问题。 - 验证Token:手动调用飞书获取Token的接口,看凭证(App ID/Secret)是否有效。
- 检查网络:确保你的服务器可以正常访问
open.feishu.cn。
5.2 如何获取正确的chat_id?
这是新手最常问的问题。除了前面提到的通过事件订阅获取,还有一个更编程式的方法:
- 确保机器人有
chat:read权限。 - 调用
获取群列表API (/open-apis/im/v1/chats)。 - 在返回的列表里,根据群的
name或description找到你的目标群。注意,返回的可能是分页数据。 - 记录下该群的
chat_id。
你可以写一个一次性脚本,运行后打印出所有你有权限的群列表,方便查找。
5.3 消息格式与飞书卡片调试
飞书卡片功能强大但格式严格。一个多余的逗号、一个错误的字段类型都可能导致发送失败。
调试技巧:
- 使用官方工具:飞书开放平台的“消息卡片搭建工具”是你的最佳伙伴。你可以在这里可视化搭建卡片,并直接生成对应的JSON。将你代码中的JSON复制过去验证,或者用工具生成的JSON替换你的。
- 简化消息:当发送复杂卡片失败时,先尝试发送最简单的文本消息
bot.send_text(“test”)。如果成功,再逐步增加卡片复杂度,定位问题字段。 - 关注字段类型:特别注意
content字段,在发送卡片时,它必须是一个字符串,这个字符串是卡片的JSON序列化。很多错误是因为直接将Python字典传给了content,而SDK期望的是一个JSON字符串。查看feishu-bot库的源码或文档,确认send_card方法的参数格式。
5.4 性能与稳定性考量
- 异步化:如前所述,将HTTP请求异步化,并使用连接池,可以显著提升高并发下的性能。
- 消息队列解耦:不要直接在Webhook处理线程中同步调用飞书API。应该将消息发送任务推送到Redis、RabbitMQ等消息队列中,由独立的消费者进程异步处理。这样即使飞书API暂时不可用,也不会阻塞或丢失GitLab的Webhook。
- 监控与告警:为你的机器人服务添加监控。监控指标包括:Webhook接收数量、消息发送成功/失败率、API调用延迟。当失败率超过阈值时,通过另一个可靠的通道(如短信、另一个飞书机器人)发送告警。
- 日志记录:详细记录每个环节的日志,包括接收的Webhook数据、构造的消息体、飞书API的请求和响应。这些日志是排查问题的黄金资料。建议使用结构化的日志格式(如JSON),方便后续检索和分析。
5.5 一个容易被忽略的细节:@特定人员
在群通知中,经常需要@相关责任人。飞书卡片中@人需要使用open_id。
- 你需要先通过用户的邮箱或手机号,调用飞书API查询到其
open_id。 - 在卡片文本中使用
<at open_id=”ou_xxxxxx”></at>的格式。 - 注意,仅仅在文本中插入
<at>标签是不够的,必须在卡片的elements数组的顶层,额外添加一个”at”类型的元素,列出所有需要@的open_id,否则@不会生效。
{ "elements": [ { "tag": "div", "text": { "tag": "lark_md", "content": "请处理此MR:<at open_id=\"ou_123456\"></at>" } }, { "tag": "at", "user_id": "ou_123456" } ] }这个过程稍微繁琐,建议封装一个工具函数,输入邮箱列表,输出处理好的文本和at元素。
构建一个稳定、好用的飞书机器人,就像打磨一个产品。从满足核心需求开始,逐步迭代,加入错误处理、性能优化、监控告警。rawchen/feishu-bot这个项目提供了一个坚实的地基,让你可以专注于业务逻辑和用户体验,而不是陷在与API通信的泥潭里。当你看到团队因为自动化的通知而减少沟通成本、提升响应速度时,这一切的投入都是值得的。
