夜雨聆风学习资料网

ARTICLE · 1278725

ESP-Claw源码剖析-09_IM通信集成

ESP-Claw源码剖析-09_IM通信集成

IM 通道是人与 ESP-Claw 设备对话的主入口:Telegram、QQ、飞书、微信四个远端平台,外加一个进程内的 Web 通道,全部由设备直接连接各平台的开放接口收发消息,中间没有自建消息服务器。本章剖析 cap_im_platform 与 cap_im_local 两个组件如何把这些异构平台折叠成统一的能力组与事件流:入站消息如何变成事件并流入 Agent,Agent 的回复如何经出站绑定表找到正确的通道与聊天 ID 回发,媒体附件如何落盘与事件化。视角依旧是"为什么这样设计"。

目录

  1. IM 通道的定位与组件版图1.1 设备直连:为什么 IM 是主入口1.2 两个组件的分工

  2. IM 抽象:能力组、网关与发送器2.1 gateway 与 send_*:一组能力两种角色2.2 新平台如何接入

  3. 各平台的接入差异3.1 传输方式对比:长轮询与长连接3.2 凭证管理链路3.3 微信特例:扫码登录与进度限流

  4. 入站流:一条消息如何进入 Agent

  5. 出站流:出站绑定与 send_message 动作5.1 out_message 事件与默认绑定解析5.2 注册制而非 switch-case5.3 多通道并发会话的隔离

  6. cap_im_local:Web IM 的进程内桥

  7. 媒体消息:附件落盘与事件化

  8. 设计动机总评参考文献

1. IM 通道的定位与组件版图

1.1 设备直连:为什么 IM 是主入口

第一性问题是:一个没有屏幕(或只有小屏幕)的嵌入式 Agent,用户用什么客户端跟它说话?自研 App 意味着开发、分发、升级三重成本;而 IM 机器人什么都不需要——用户的微信、QQ、Telegram、飞书早已装好,加个好友即可对话。ESP-Claw 选择"设备直连平台开放接口"而非"自建服务器中转",出于三层考虑:

  • 免运维:设备主动外连平台(长轮询或 WebSocket),不需要公网 IP、域名、证书与常驻服务器,家庭 NAT 环境即插即用。

  • 隐私:消息只经过 IM 平台与 LLM 服务两方,不落在开发者自建的中转机上。

  • 可裁剪:四个平台各自受 Kconfig 开关(CONFIG_APP_CLAW_CAP_IM_TG/QQ/FEISHU/WECHAT)控制,按目标市场只编入需要的通道。

单台设备可同时挂多个通道:每个通道一个独立的收发任务,但共享同一个事件路由器与同一个 Agent。用户在哪个通道说话,回复就从哪个通道回去。

1.2 两个组件的分工

IM 相关代码分两个能力组件(第5章《能力系统》的能力组机制):

  • components/claw_capabilities/cap_im_platform/:四个远端平台(cap_im_tg.c、cap_im_qq.c、cap_im_feishu.c、cap_im_wechat.c)加共享附件工具 cap_im_attachment.c,每个平台约 1500~3400 行。

  • components/claw_capabilities/cap_im_local/:进程内本地通道 cap_im_local.c(约 500 行),为设备自带 Web 聊天页充当 IM 网关。

远端平台的聚合入口只有 39 行(src/cap_im_platform.c),做的事仅仅是在 Kconfig 开关下依次调用各平台的注册函数:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
esp_err_tcap_im_platform_register_groups(void){#if CONFIG_APP_CLAW_CAP_IM_FEISHU    ESP_RETURN_ON_ERROR(cap_im_feishu_register_group(), TAG, "register Feishu IM group failed");#endif#if CONFIG_APP_CLAW_CAP_IM_QQ    ESP_RETURN_ON_ERROR(cap_im_qq_register_group(), TAG, "register QQ IM group failed");#endif#if CONFIG_APP_CLAW_CAP_IM_TG    ESP_RETURN_ON_ERROR(cap_im_tg_register_group(), TAG, "register Telegram IM group failed");#endif// 微信分支同构,略return ESP_OK;}

真正的接线发生在应用层:components/common/app_claw/app_capabilities.c 的能力组表为每个平台准备 prepare/register 两个回调,register 回调里完成"注册能力组 + 注入凭证 + 注入附件配置"三件事;app_claw.c 随后为每个通道注册出站绑定(第 5 节详述)。组件本身不知道应用何时、以何种顺序装配它——这正是能力系统"注册制"的体现。

2. IM 抽象:能力组、网关与发送器

2.1 gateway 与 send_*:一组能力两种角色

每个平台注册一个 claw_cap_group,组内描述符固定为两种角色(以 Telegram 为例,摘自 cap_im_tg.c):

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
staticconstclaw_cap_descriptor_t s_tg_descriptors[] = {    {        .id = "tg_gateway",              // 事件源:收消息        .kind = CLAW_CAP_KIND_EVENT_SOURCE,        .cap_flags = CLAW_CAP_FLAG_EMITS_EVENTS |                     CLAW_CAP_FLAG_SUPPORTS_LIFECYCLE,        .init = cap_im_tg_gateway_init,        .start = cap_im_tg_gateway_start,   // 启动轮询任务        .stop = cap_im_tg_gateway_stop,    },    {        .id = "tg_send_message",         // 可调用:发消息        .kind = CLAW_CAP_KIND_CALLABLE,        .cap_flags = CLAW_CAP_FLAG_CALLABLE_BY_LLM,        .execute = cap_im_tg_send_message_execute,// input_schema_json 声明 chat_id / message 两个入参,略    },// tg_send_image / tg_send_file 同构,略};

值得注意的是:这里没有 C 层的 ops 虚函数表,抽象完全落在两条约定上。其一,gateway 是一个带生命周期的事件源能力,start 内部自建 FreeRTOS 任务或 WebSocket 客户端,框架不 prescribe 线程模型——Telegram 在 cap_im_tg_gateway_start 里创建轮询任务,QQ/飞书创建的是 esp_websocket_client 会话。其二,send_* 是可被 LLM 调用的工具,且 chat_id 允许缺省:cap_im_tg_send_message_execute 先从输入 JSON 取 chat_id,取不到就回退到调用上下文 ctx->chat_id。这意味着 Agent 在会话中途调工具回话时,不必(也无法)知道用户在哪个平台、哪个聊天里——位置感由框架携带,模型只管说内容。

四个平台的头文件契约完全同构:cap_im_<x>_register_group / set_token(或 set_credentials)/ set_attachment_config / start / stop / send_text / send_image / send_file。QQ 额外有 cap_im_qq_set_msg_type(选择回发消息格式),微信额外有一组扫码登录接口(见 3.3 节)。

2.2 新平台如何接入

接入一个新平台(例如钉钉)的改动清单:

  • 新建 cap_im_dingtalk.c/.h,实现凭证 setter、<x>_gateway 生命周期(自建收发任务)、<x>_send_message 的 execute 与 HTTP 发送;

  • 在 cap_im_platform.c 与 Kconfig 中加入开关 CONFIG_APP_CLAW_CAP_IM_DINGTALK;

  • 在 app_capabilities.c 能力组表中加一行 prepare/register,注入凭证;

  • 在 app_claw.c 中注册出站绑定:claw_event_router_register_outbound_binding("dingtalk", "dingtalk_send_message")。

关键在于:事件路由器、claw_core、其他平台、会话系统零改动。新平台只需遵守"入站发布 message 事件、出站注册 <x>_send_message 能力"两条约定,其余基础设施自动生效——路由规则里的 {{event.source_channel}} 模板会把回复送回新通道,会话系统自动按新通道的 chat_id 建会话。

3. 各平台的接入差异

3.1 传输方式对比:长轮询与长连接

四个平台的传输方式以源码为准汇总如下(通道名即出站绑定表的键):

平台
通道名
传输方式
凭证
Telegram
telegram
HTTP 长轮询 getUpdates(timeout 20 s,offset 游标推进)
Bot Token
QQ
qq
WebSocket 长连接(/gateway 取 wss 地址后 identify 鉴权)
app_id + app_secret
飞书
feishu
WebSocket 长连接(长连接事件订阅)
app_id + app_secret
微信
wechat
HTTP 长轮询(默认网关 ilinkai.weixin.qq.com,35 s 超时 + sync 游标)
扫码登录态 + token
Web
web
进程内直调
无

为什么全部是"设备主动外连"而没有 webhook 回调?因为 webhook 要求设备有公网可达的 HTTPS 端点,这在家庭 NAT 与移动网络下不现实,也违背免运维目标。Telegram 的 cap_im_tg_poll_once 构造 {"timeout":20,"offset":next_update_id} 调 getUpdates,失败后退避 3 秒重试;QQ 先用 app_id/secret 换 access_token(提前 300 秒刷新、遇 401 失效重取),再请求 https://api.sgroup.qq.com/gateway 拿到 wss 地址,建连后发送 op=2 identify(鉴权头 QQBot <access_token>);飞书则请求 https://open.feishu.cn/callback/ws/endpoint 获取 WebSocket 配置(URL 与 PingInterval,默认 120 秒心跳),连上后只关注 im.message.receive_v1 事件,鉴权用 /auth/v3/tenant_access_token/internal 换取的 tenant_access_token。

QQ 与飞书同为 WebSocket,代码却各自实现一遍分片重组(cap_im_qq_ws_assembly_t 与飞书的 cap_im_feishu_ws_frame_t)——两家的帧格式、心跳、重连策略差异足够大,强行抽象出"通用 IM WebSocket 层"只会制造一个两头不靠的接口,这是"不做投机抽象"的取舍。

3.2 凭证管理链路

凭证不走第 8 章那样的独立配置组件,而是复用应用的统一配置体系:

  • 定义:application/edge_agent/components/app_config/app_config.c 用 APP_CONFIG_FIELD(tg_bot_token, "tg_bot_token", APP_DEFAULT_TG_BOT_TOKEN) 声明字段,默认值来自 Kconfig,覆盖值持久化在 settings 存储;

  • 注入:app_cap_register_im_tg() 等函数把 app_claw_config_t 里的凭证经 cap_im_tg_set_token()、cap_im_qq_set_credentials()、cap_im_feishu_set_credentials()、cap_im_wechat_set_client_config() 塞进组件静态状态;

  • 运行期修改:Web 配置页把 IM 凭证归入 "im" 组(http_server_config_api.c 的 CONFIG_FIELD("im", tg_bot_token) 等),改完即可热生效;串口侧另有 cmd_cap_im_tg.c 等 CLI 命令做同样的事。

也就是说,IM 组件对凭证来源零假设:它只暴露 setter,谁来调、值存哪里是应用装配层的决策。这让 cap_im_platform 可以脱离 app_config 在别的应用里复用。

3.3 微信特例:扫码登录与进度限流

个人微信没有官方机器人 API,固件默认对接 https://ilinkai.weixin.qq.com 网关(wechat_base_url 可配置),配合扫码登录绑定个人账号:cap_im_wechat_qr_login_start() 启动流程,cap_im_wechat_qr_login_get_status() 轮询出的 cap_im_wechat_qr_login_status_t 里带有 qr_data_url(渲染成二维码供扫码)与 active/completed/persisted 等状态位,登录成功后 token 持久化(cap_im_wechat_qr_login_mark_persisted()),此后用 35 秒长轮询加 sync_buf 游标增量拉取新消息。

微信还有一个独有的细节——进度消息限流。Agent 推理过程中的 agent_stage 阶段事件("正在查天气……")会经由路由规则回发到 IM;微信对话流密集,连续刷屏体验极差。cap_im_wechat_stage_send_mode_for_event() 为每个 chat_id 维护连续 stage 计数:前 CAP_IM_WECHAT_STAGE_LIMIT(5)条照发,第 6 条改发一条"进度过多"提示,之后全部静默跳过,直到出现非 stage 消息清零计数。这是"平台适配差异下沉到平台文件"的范例:路由器与 claw_core 对此一无所知。

4. 入站流:一条消息如何进入 Agent

流程执行说明:

  • 收取阶段(步骤 1-3):平台侧消息到达 Telegram 云端后,由 cap_im_tg_poll_task 的 cap_im_tg_poll_once() 经 20 秒长轮询取回,解析出 chat_id、sender_id、message_id 与文本,调用 cap_im_tg_publish_inbound_text(),后者只是 claw_event_router_publish_message("tg_gateway", "telegram", chat_id, content, sender_id, message_id) 的薄封装。该函数(第6章《事件路由系统》)把参数折叠成 claw_event_t:event_type="message"、content_type="text"、session_policy=CLAW_SESSION_POLICY_CHAT、correlation_id=入站 message_id——这条 correlation 线后面要用来串回复。

  • 规则短路阶段(步骤 4-5):路由器按 router_rules.json 顺序匹配。im_any_message_working_reply(consume_on_match=false,不消费事件)先回一句"🦞 ESP-Claw is snapping on it..."稳住用户;/session、/llm 前缀分别被 im_session_command、im_llm_command 规则用 call_cap 动作短路给 session_command、llm_config_command 能力处理,毫秒级应答,不消耗一次 LLM 调用。

这里的消费语义是精心排布的:工作回复规则排在前面但不消费事件,前缀命令规则消费事件后即终止匹配,普通文本规则最后消费并交给 Agent。于是同一条消息既得到即时安抚,又不会重复触发多条链路——"应答"与"处理"是两条规则而非一个分支,各自可独立启停。

  • 进入 Agent 阶段(步骤 6-8):普通文本命中 im_any_message_agent(consume_on_match=true),动作 run_agent 的模板 target_channel={{event.source_channel}} 把回程地址写进请求。claw_event_router_execute_agent_action() 组装 claw_agent_mgr_root_input_t——source_channel/source_chat_id/source_message_id 全部随行,且 flags 带上 CLAW_CORE_REQUEST_FLAG_PUBLISH_OUT_MESSAGE(这是出站事件的关键开关)——然后 claw_agent_mgr_submit_root() 提交。此后是第4章《Agent核心设计》的推理循环。

入站流的设计要点:四个平台的 cap_im_<x>_publish_inbound_text 长得一模一样(只换 source_cap 与通道名字符串)。平台适配层把一切差异(轮询/长连接、JSON 方言、鉴权)消化在自己文件里,交给路由器的已经是同一种 message 事件——异构性在进入系统之前就被吸收了。

5. 出站流:出站绑定与 send_message 动作

5.1 out_message 事件与默认绑定解析

Agent 完成回复后,claw_core_publish_out_message_if_requested() 检查请求 flags 里的 CLAW_CORE_REQUEST_FLAG_PUBLISH_OUT_MESSAGE(由路由器的 run_agent 动作设置),调用 build_agent_out_message_event() 构造事件:source_cap="claw_core"、event_type="out_message"、通道取 target_channel(无则回落 source_channel)、correlation_id 复制入站消息的 message_id,随后 claw_event_router_publish()。规则 agent_out_message_send_message 匹配到它,执行 send_message 动作:

  • claw_event_router_execute_send_message_action() 用模板渲染出 channel/chat_id/message(message 缺省回落 last.output);

  • claw_event_router_resolve_outbound_cap() 把通道名翻译成能力 id:先问应用注入的自定义 resolver,未命中(返回 ESP_ERR_NOT_FOUND)再用默认实现——遍历绑定表按通道名字符串匹配:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
claw_event_router_lock();                       // 默认解析器:加锁遍历绑定表for (i = 0; i < s_runtime->binding_count; i++) {if (strcmp(s_runtime->bindings[i].channel,               target_channel ? target_channel : "") == 0) {        strlcpy(cap_name, s_runtime->bindings[i].cap_name, cap_name_size);        claw_event_router_unlock();return ESP_OK;                          // 命中:返回能力 id    }}claw_event_router_unlock();return ESP_ERR_NOT_FOUND;                       // 通道未注册绑定
  • claw_cap_call("tg_send_message", {chat_id, message}, caller=CLAW_CAP_CALLER_SYSTEM) 调起平台发送能力,最终落到 cap_im_tg_send_text() 的 HTTP sendMessage。

绑定表由 app_claw.c 在启动时注册,每个通道一行:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
#if CONFIG_APP_CLAW_CAP_IM_TG    ESP_RETURN_ON_ERROR(claw_event_router_register_outbound_binding("telegram", "tg_send_message"),                        TAG, "Failed to bind Telegram outbound");#endif// qq→qq_send_message、feishu→feishu_send_message、// wechat→wechat_send_message、web→local_send_message 同构,略

此外还有一条并行的出站流:claw_core_publish_stage_text() 发布的 agent_stage 进度事件,被规则 agent_stage_im_notify 同样送回原聊天,让用户在长任务中看到中间进展(微信侧受 3.3 节的限流保护)。

5.2 注册制而非 switch-case

出站分发最容易写成 if (strcmp(channel,"telegram")==0) cap_im_tg_send_text(...) else if ... 的硬编码。ESP-Claw 没有这么做,收益有三:

  • 路由器零依赖:claw_event_router.c 不 include 任何 cap_im_*.h,通道到能力的映射是运行期字符串绑定,路由器组件可以脱离全部 IM 平台单独编译测试;

  • 新平台零侵入:如 2.2 节所述,新通道只需注册能力加一行绑定,不改路由器一行代码;

  • 策略可覆盖:outbound_resolver 钩子允许应用实现比"按通道"更细的策略(例如把某类通知强制改投另一通道),默认实现只兜底。

本质上,通道名与能力 id 都是字符串协议,绑定表是唯一将二者焊接在一起的地方——这和第5章"能力按 id 字符串调用"的设计一脉相承。

5.3 多通道并发会话的隔离

一个用户同时在 Telegram 和微信里跟同一台设备说话,两段对话必须互不串味。隔离靠会话 ID 的构造:claw_session_mgr_build_session_id() 以 agent_id + source_channel + chat_id 生成会话 ID,同一个人在两个平台天然得到两个会话,各自维护独立的上下文与记忆(第7章《记忆系统》);同一平台内,不同群聊/单聊的 chat_id 也互不干扰。会话策略由入站事件携带——IM 消息统一标记 CLAW_SESSION_POLICY_CHAT(按聊天归档),而定时器、开机这类触发事件走 trigger/global 策略,不混入对话历史;NOSAVE 策略则直接返回空会话 ID,用于一次性查询。若会话管理器尚未就绪,构造函数会退回默认会话 ID(claw_session_mgr_write_default_session_id),保证启动早期消息不丢。回程方向,target_channel/target_chat_id 由 run_agent 动作从入站事件拷贝而来,回复永远走"来时的路",除非规则显式改写 target。correlation_id 则把出站 out_message 与入站 message 串起来,供日志与上层审计。

值得强调的是,这套隔离对 Agent 完全透明:claw_core 收到的请求里只有 source_channel/source_chat_id 与文本,它不需要任何"多租户"意识。并发正确性由两端的约定保证——入站时路由器把通道与聊天写进请求,出站时绑定表按同一组字符串找回出口,中间没有任何共享可变状态。

6. cap_im_local:Web IM 的进程内桥

cap_im_local 是五个通道里最便宜的一个,却最能检验抽象的完备性。设备自带的 Web 聊天页(application/edge_agent/components/http_server/http_server_webim_api.c,WebSocket 长连接)需要一个 IM 入口,做法不是给 Web 开特权通道,而是把它做成"第五个 IM 平台":

  • 入站:浏览器 WS 消息 → cap_im_local_emit_user_message("web", chat_id, ...) → 与 Telegram 完全相同的 claw_event_router_publish_message("local_gateway", ...),走同一条规则链、进同一个 Agent、建同构的会话;

  • 出站:绑定表里 ("web", "local_send_message"),路由器照常 claw_cap_call("local_send_message", ...) → cap_im_local_send_text_impl() → 交给注册的出站回调 webim_outbound_cb(过滤 channel=="web")→ WS 广播回浏览器。

组件自身注册的仍是标准两件套:local_gateway(EVENT_SOURCE,cap_im_local_start/stop 只翻转一个 started 标志位)与 local_send_message(CALLABLE_BY_LLM,入参 schema 与各平台一致)。消息未显式给出 channel 时回落默认通道 "local",发送者回落 "local_user";入站消息若带附件链接,cap_im_local_emit_user_message() 会把链接列表拼成 Markdown 的 [Links] 尾部随正文一起进事件,保持与远端平台一致的"文本+附件"呈现。

回调注入是这里的关键设计:cap_im_local_set_outbound_callback() 让 cap_im_local 组件不依赖任何 HTTP/WebSocket 头文件——发送方向被反转成了接口。cap_im_local_message_t 还带 link_url/link_label 字段,Agent 回复附带的文件链接以结构化方式呈现给页面。日志里那句 "Web IM outbound -> WebSocket (no server-side history)" 顺带说明了取舍:Web 通道不落服务端历史,历史由第7章的会话记忆体系统一持有。

cap_im_local 同时保留了默认通道 "local":任何进程内代码(CLI、测试)都可以用它模拟一个 IM 用户,为第6章的规则调试提供无网络环境。

7. 媒体消息:附件落盘与事件化

图片、语音、文件的处理横跨四个平台,共享 cap_im_attachment.c 一套工具:cap_im_attachment_build_saved_paths() 生成确定性存储路径(<root>/<platform>/<chat_id>/<message_id>/...,extension 由 MIME/URL/原始文件名三重猜测)、cap_im_attachment_download_url_to_file() 带大小上限下载、cap_im_attachment_build_payload_json() 构造统一负载。应用侧统一配置:存储根目录取 paths->im_attachment_root,入站附件上限 APP_IM_ATTACHMENT_MAX_BYTES(2 MB),默认开启。

以 Telegram 入站图片为例(cap_im_tg_save_attachment 流程):轮询任务发现 photo/file_id 后不阻塞主循环,而是把任务投给独立队列,由 tg_attach 任务(8 KB 栈、优先 PSRAM)执行 getFile 换取 file_path,拼出 https://api.telegram.org/file/bot<token>/<path> 下载落盘,然后构造负载并发布事件:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
payload_json = cap_im_attachment_build_payload_json(    &(cap_im_attachment_payload_config_t) {        .platform = "telegram",        .attachment_kind = attachment_kind,   // photo / voice / document        .saved_path = saved_path,             // 落盘绝对路径        .mime = mime,        .caption = caption,                   // 用户附言        .source_key = "platform_file_id",     // 平台原始 ID 透传        .size_bytes = bytes,    });

该事件以 event_type="attachment_saved"、content_type 标注媒体类型进入路由器,规则 im_attachment_saved_reply 立即回一句 "File received from telegram"。真正的消费在下游:负载 JSON 里的 saved_path 让视觉技能(第8章《技能系统》)或 Agent 的多模态工具可以直接读取本地文件——平台差异再次在附件工具层被抹平,事件里只剩统一的"文件已就位"语义。

出站方向,四个平台都提供 send_image/send_file 能力(Telegram 走 sendPhoto/sendDocument 的 multipart 上传,cap_im_tg_send_multipart_file),LLM 可显式调用它们把生成或截取的文件发回聊天。

8. 设计动机总评

回看本章的设计,可以提炼成三条主线:

  • 异构性就地消化:每个平台文件是一堵墙,把轮询/长连接、鉴权、JSON 方言、消息格式差异全部关在墙内;对外只暴露两种同构能力(gateway 事件源、send_* 工具)与两条字符串约定(通道名、能力 id)。新平台接入成本被压到"写一个文件 + 一行绑定"。

  • 位置感由框架携带:chat_id 回退到 ctx->chat_id、run_agent 携带 source/target 通道、会话按 channel+chat 隔离、correlation_id 串联往返——模型和业务规则永远不需要理解"用户在哪",多通道并发自然正确。

  • 字符串协议替代编译期耦合:出站绑定表让路由器与 IM 实现彻底解耦,cap_im_local 用同一套抽象接住了 Web 页面,证明这套接口对"远端云平台"与"进程内页面"两类极端都足够通用。

与第10章《MCP通信体系》对照会更清晰:IM 解决"人与设备"的对话入口,MCP 解决"设备与外部工具服务"的能力扩展;两者一个向内收敛为 message 事件,一个向外发散为工具调用,共同围出第4章 Agent 的输入输出边界。而这一切的前提是第3章《启动流程与应用外壳》完成的装配——Kconfig 裁剪、能力组注册、绑定表写入,让同一份固件在"只有 Telegram"与"全家桶"之间自由伸缩。

参考文献

[1] Telegram Bot API:https://core\.telegram\.org/bots/api

[2] QQ 机器人开放平台文档:https://bot\.q\.qq\.com/wiki/

[3] 飞书开放平台事件订阅文档:https://open\.feishu\.cn/document/

[4] 微信开放文档:https://developers\.weixin\.qq\.com/doc/

[5] Espressif esp-websocket-client 组件:https://components\.espressif\.com/components/espressif/esp\_websocket\_client

回首页,搜索你想要的文档

在首页搜索框输入关键词,快速定位资料

文库首页搜索指引 立即搜索

相关学习资料