跳转到内容

聊天账号、路由与身份

本页是企业微信、个人微信、飞书、Lark、钉钉的公共契约,请与平台页一起读,传输和发送能力不可互换。账号持有凭据/连接,触发器筛选消息并绑定已存在的脚本/工作流,不支持 endpoint 目标。配置和目标草稿实时生效,无发布步骤;不提供原生 HomeAssistant、MQTT、Slurm 连接器。

所有 API 示例均源码推导/未执行;真实扫码、发送和供应商调用需明确授权。账号/触发器 CRUD 及账号 QR API 当前要求项目成员;创建/撤销自助链接需内容编辑权,查看链接/使用记录需成员资格;创建/删除发送者身份绑定需 Owner/Admin/Developer 项目权,查看需成员资格。管理凭据与消息解析出的最终用户身份不同。

POST /projects/<slug>/im/accounts 创建,GET …/im/accounts 列表,PUT|DELETE …/im/accounts/<name> 更新/删除。

字段创建/更新
name必填非空白;PUT 可重命名由旧路径名指定的账号。
platform必填 `wecom
enabled默认 true,包括 PUT;编辑已暂停账号须显式保留 false。
resource凭据资源名,与 credentials 互斥。
credentials内联 JSON,加密存储,从不回传;创建时与 resource 必须恰有一个,微信扫码前用 {}。更新时两者省略/null 保留当前来源,发送一个则切换来源,无明文读取 API。
meta创建默认 {},更新省略/null 保持,{} 清空;随 message.account.meta 传入,bot_open_id/robot_code 会由对应连接器读取。
groups默认 [],更新省略/null 保持,[] 清空;这是账号 population,不是供应商群聊。
peer_identity可选平台侧人的身份,同平台全实例唯一;更新省略/null 保持,不清除;冲突返回 409,避免静默抢占绑定。

静态平台凭据使用 app_id、app_secret,可选 base_url 受平台 host allowlist 约束。凭据放资源 secret 部分或加密内联字段,不放 meta/仓库说明。AccountPublic 完整字段为 id,name,platform,enabled,resource,has_inline_credentials,meta,groups,peer_identity,status,status_message,created_at,updated_at;resource/peer/status_message 可为 null。status 是 ok|degraded|needs_reauth,不证明消息送达。GET …/<name>/health 查连接/lease,GET …/<name>/messages 查处理账本。默认上限每项目 500、总计 2000、每实例服务 200 账号,由运维配置;超限不能靠反复创建解决。

POST /projects/<slug>/im/triggers、GET …/im/triggers、PUT|DELETE …/im/triggers/<name>;创建和更新共用整体表单。

字段默认/条件
name、platform、target必填名称、平台枚举、已存在 `{kind:script
enabledtrue。
account省略/null 汇入项目中所有该平台启用账号;名称固定一个账号,平台必须一致。
account_groups[] 无 population 限制,非空按账号所属 population 缩小范围。
chat_type默认 any,可 direct/group;过滤器不能创造平台不支持的流量。
keyword省略/null 匹配全部,否则对规范化 text 做不区分大小写的子串匹配;纯媒体 text 可能空。
require_mentionfalse;仅飞书/Lark 有意义;企业微信/钉钉群消息本来就因 @ 才到达,原生微信群聊支持尚未确立。
inputs默认 null;执行只合并对象常量,建议 {}。
params默认 {},参数名 → {from:<点分路径>,required:false,default?:<JSON>},如 message.text/message.raw.priority。缺失/null 叶子有 default 则使用,无默认的 required 失败,可选缺失保持不存在;这里没有 HTTP 类型转换声明。
params_strictfalse 同时传 message 和声明参数;true 不传 message,常量仍合并;声明值覆盖同名常量。
max_concurrent_runs4,连接建立时读取,按账号约束,修改在重连生效;不是全局按触发器顺序保证。
identity_modeoff,也支持 optional/required,下述。
identity_key省略/null 使用 message.sender_id;点分 envelope 路径。
identity_rule可选模板,必须含 {key}。
identity_prompt可选未绑定回复,必须含 {url}。mode off 时所有 identity 字段必须空白/省略。

触发器 PUT 是整体替换,省略可选字段恢复默认;按字段用 []、{}、null、显式 false,不是 merge patch。账号更新保留语义是上节单独的例外。一条消息可匹配多个触发器,各运行一次,不是首个匹配即停止。

TriggerPublic 返回 account_id(可空 UUID),不是提交的账号名;返回 target_kind,target_name,不是嵌套 target。其余字段为 id,name,enabled,platform,account_groups,chat_type,keyword,require_mention,inputs,params,params_strict,max_concurrent_runs,identity_mode,identity_key,identity_rule,identity_prompt,created_at,updated_at。

默认参数是常量加 message:{platform,account:{id,name,meta},chat_type,chat_id,sender_id,kind,text,mentioned,raw,reply_handle}。kind 为 text/image/voice/file/video/event;raw/媒体字段因平台而异,不代表所有连接器自动下载或转录。reply_handle 是敏感路由能力,不是永久通用目标。identity_mode off 不添加 message.identity,以 system:im 运行;optional 添加绑定用户对象或 null;required 阻止未绑定发送者。绑定后沿用 active 项目用户的 principal 做授权/资源身份转发,绑定本身不授予权限。

共享绑定键为 (project,platform,sender_key),不依赖某个机器人账号。先查已有绑定,再尝试 identity_rule;模板匹配已存在 active 项目用户 identity,成功持久化绑定。只有供应商 key 与 IdP subject 确实一致才可用 identity_rule:"jwt:corp:{key}";它不创建用户,也不证明任意 meta。手动绑定:

POST /projects/demo/im/identities
Content-Type: application/json
{"platform":"feishu","sender_key":"synthetic-open-id","user":"alice"}

响应含 id,platform,sender_key,project_user_id,identity,display_name,bound_via:"admin",bound_at;可按 ?user=alice&platform=feishu 查看,DELETE …/im/identities/<id> 移除。required 下未绑定私聊用户可收到签名的 15 分钟项目登录链接;/p/<slug>/_chat/bind 页面要求项目凭据并执行 login pre-hook。群聊只提示转私聊,不向所有成员泄露发送者绑定链接。已停用用户不视为有效绑定。账号 population、peer_identity、邀请 meta、项目用户身份绑定是不同机制,互不自动赋予信任。

返回平台适用文本/JSON 自动回复,null 不自动发送。企业微信沿连接回复,其他平台有各自发送约束。脚本成功或供应商 API 接受都不证明人已阅读。

每账号一个持 lease 实例持有连接。先 claim 去重账本,再 ack/持久化 cursor;每个匹配触发器有独立消息身份。可抑制通常重复投递,但不保证外部副作用 exactly-once。供应商 ack 可以发生在持久 claim 之后、脚本成功之前,与 RabbitMQ success→ack 不同。不保证每次脚本失败自动重试,也无通用重放 API。遗留旧工作按一小时阈值标记失败,账本不是完整 payload 重放档案;关键业务需独立持久 outbox/工作表和业务 id。

瞬态断线 2–60 秒退避,健康会话重置;撤销/配置错误停在 needs_reauth,需要人修复,更新凭据可清此状态,仅切 enabled 不是凭据修复。并发按账号,没有全局严格顺序;脚本编辑 live,连接级设置重连生效。timeout 不回滚写入或已发送消息;禁用/删除需等对账停止新路由/连接,不原子取消在途副作用。检查 health、账本、执行日志,修复目标/凭据,再确认既有副作用后重试业务。

原生微信 QR 建立机器人账号,与发送者→项目用户绑定不同。editor 可 POST …/im/bind-links,字段 {platform:"weixin",label?,ttl_hours:24,max_uses:50,groups:[],meta?:{…}}。TTL 必须为正,受部署最大默认 168h 限制;max_uses 夹到 1–配置上限(默认 500);meta 必须对象且编码 <=4096 字节。响应 {id,url,expires_at,max_uses} 只展示一次 URL。需要 VIAT_IM_BIND_BASE_URL;页面前缀默认 /ui,代理 IP 限流需要可信代理配置。URL 是未认证创建账号能力;逐人身份断言必须由已认证人的业务系统发短期单次链接,邀请 meta 优先于自己填的 display_name。清理前查看/撤销链接及 uses;撤销仅挡未来使用,不停止已创建账号。

仓库支持 chat,没有独立聊天 CLI 命令:im/accounts/<name>.yaml、im/triggers/<name>.yaml,viat pull/push --only chat。资源凭据账号与触发器正常镜像;内联/扫码账号 pull 跳过,push 不因缺失而删除。因此引用这些账号的触发器并非完全可移植。本地缺失资源账号/触发器会删除远端,先 diff。禁用测试触发器/账号、撤销邀请、移除测试身份绑定后再删除隔离配置;本地文件删除不撤销供应商凭据或外部消息。

依据:crates/viat-api/src/handlers/{im,im_bind,im_identities}.rs、im/{mod,dispatch}.rs、im/connector/*;crates/viat-core/src/types/im.rs;crates/viat-cli/src/repo/im.rs。本任务未验证真实供应商行为。