@imweapp/openclaw-imwe 2026.4.12-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +172 -0
  3. package/index.ts +16 -0
  4. package/openclaw.plugin.json +58 -0
  5. package/package.json +73 -0
  6. package/proto/PbBoxPullProto.proto +43 -0
  7. package/proto/PbChatAudioContent.proto +23 -0
  8. package/proto/PbChatDeliverMsg.proto +38 -0
  9. package/proto/PbChatFileMeta.proto +34 -0
  10. package/proto/PbChatMsg.proto +93 -0
  11. package/proto/PbChatRichMediaContent.proto +31 -0
  12. package/proto/PbChatTextContent.proto +38 -0
  13. package/proto/PbMarkdownContent.proto +18 -0
  14. package/proto/PbMsgReadStampContent.proto +11 -0
  15. package/proto/PbPacket.proto +61 -0
  16. package/proto/PbSingleChatMsg.proto +60 -0
  17. package/setup-entry.ts +17 -0
  18. package/src/accounts.ts +109 -0
  19. package/src/api-client.ts +740 -0
  20. package/src/bot-info-cache.ts +49 -0
  21. package/src/channel.runtime.ts +29 -0
  22. package/src/channel.ts +456 -0
  23. package/src/config-schema.ts +26 -0
  24. package/src/e2ee/api.ts +261 -0
  25. package/src/e2ee/canonical.ts +59 -0
  26. package/src/e2ee/errors.ts +103 -0
  27. package/src/e2ee/index.ts +8 -0
  28. package/src/e2ee/proper-lockfile.d.ts +61 -0
  29. package/src/e2ee/service.ts +1273 -0
  30. package/src/e2ee/store.ts +174 -0
  31. package/src/e2ee/types.ts +113 -0
  32. package/src/e2ee/vodozemac.ts +373 -0
  33. package/src/file-transfer/api.ts +364 -0
  34. package/src/file-transfer/concurrency.ts +77 -0
  35. package/src/file-transfer/download.ts +261 -0
  36. package/src/file-transfer/file-crypto.ts +93 -0
  37. package/src/file-transfer/index.ts +18 -0
  38. package/src/file-transfer/scheduler.ts +185 -0
  39. package/src/file-transfer/types.ts +195 -0
  40. package/src/file-transfer/upload.ts +656 -0
  41. package/src/markdown-detect.ts +119 -0
  42. package/src/media-upload.ts +338 -0
  43. package/src/media-utils.ts +110 -0
  44. package/src/monitor.ts +838 -0
  45. package/src/proto/codec.ts +54 -0
  46. package/src/proto/inbound.codec.ts +624 -0
  47. package/src/proto/proto-types.ts +291 -0
  48. package/src/proto/registry.ts +226 -0
  49. package/src/proto/send.codec.ts +535 -0
  50. package/src/recent-message-cache.ts +350 -0
  51. package/src/send.ts +792 -0
  52. package/src/setup-core.ts +62 -0
  53. package/src/types.ts +153 -0
  54. package/src/vodozemackit/index.ts +297 -0
  55. package/src/vodozemackit/pkg/vodozemackit_wasm.d.ts +138 -0
  56. package/src/vodozemackit/pkg/vodozemackit_wasm.js +24 -0
  57. package/src/vodozemackit/pkg/vodozemackit_wasm_bg.js +1172 -0
  58. package/src/vodozemackit/pkg/vodozemackit_wasm_bg.wasm +0 -0
  59. package/src/vodozemackit/pkg/vodozemackit_wasm_bg.wasm.d.ts +109 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lark Technologies Pte. Ltd.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,172 @@
1
+ # @openclaw/imwe
2
+
3
+ imwe 即时通讯渠道插件,AppKey/AppSecret HMAC-SHA256 签名认证,支持多账号、私聊文字消息。
4
+
5
+ ## 目录结构
6
+
7
+ ```
8
+ extensions/imwe/
9
+ ├── proto/ # Protobuf 消息定义(语言无关的契约文件)
10
+ │ ├── PbPacket.proto # 传输层:PbPacket / PbRequest / PbResponse
11
+ │ ├── PbChatDeliverMsg.proto # 投递层:PbChatMsgDeliverBody
12
+ │ ├── PbChatMsg.proto # 信封层:PbChatMsgEnvelope / PbMsgRspBody
13
+ │ ├── PbChatTextContent.proto # 内容层:PbChatTextContent / BodyRange
14
+ │ ├── PbSingleChatMsg.proto # 发送层:PbSingleChatMsgReqBody
15
+ │ └── PbBoxPullProto.proto # 事件箱:PbBoxPullReq / PbBoxPullRsp
16
+ ├── index.ts # 插件主入口(defineChannelPluginEntry)
17
+ ├── setup-entry.ts # 轻量入口(setup-only 模式)
18
+ ├── package.json # npm 包配置
19
+ └── src/
20
+ ├── proto/ # Protobuf 编解码层(TypeScript)
21
+ │ ├── codec.ts # encode/decode 统一导出入口
22
+ │ ├── registry.ts # protobufjs 类型注册表(单例)
23
+ │ ├── proto-types.ts # decode 结果的 TypeScript 接口(仅用于类型断言)
24
+ │ ├── inbound.codec.ts # 入站消息四层解包
25
+ │ └── send.codec.ts # 出站消息编码 + 发送响应解码
26
+ ├── types.ts # 类型定义(三层:Config / ImweConfig / ResolvedAccount)
27
+ ├── accounts.ts # 账号解析(listAccountIds / resolveImweAccount)
28
+ ├── config-schema.ts # Zod 配置 schema
29
+ ├── api-client.ts # HTTP API 客户端(签名 + protobuf 传输)
30
+ ├── monitor.ts # 事件箱短轮询消息监听器
31
+ ├── send.ts # 发送文字消息(outbound 适配器入口)
32
+ ├── setup-core.ts # setup 适配器(openclaw setup imwe)
33
+ ├── channel.ts # ChannelPlugin 主对象(组装所有适配器)
34
+ └── channel.runtime.ts # 运行时模块统一导出(懒加载目标)
35
+ ```
36
+
37
+ ## 认证方式
38
+
39
+ 每次 HTTP 请求携带以下签名头(对应文档 §1):
40
+
41
+ ```
42
+ X-Api-Key: <appKey>
43
+ X-Timestamp: <毫秒时间戳>
44
+ X-Signature: Base64(HMAC-SHA256(appSecret, signString))
45
+ ```
46
+
47
+ 签名原文(signString):
48
+
49
+ ```
50
+ {METHOD}&{PATH}&{TIMESTAMP}&{BODY_HASH}
51
+ ```
52
+
53
+ - `METHOD`:HTTP 方法大写,固定为 `POST`
54
+ - `PATH`:请求路径,如 `/api/im/open/bot/sendMessage`
55
+ - `TIMESTAMP`:与 `X-Timestamp` 相同的毫秒时间戳字符串
56
+ - `BODY_HASH`:`hex(SHA-256(body))`,body 为空时对空字符串 `""` 哈希
57
+
58
+ 无需登录流程,无需持久化任何凭证,AppKey/AppSecret 写入 config 即可。
59
+
60
+ ## 数据流
61
+
62
+ ```
63
+ 用户消息
64
+ → imwe 平台
65
+ → monitor.ts (long polling POST /messages/poll)
66
+ → channelRuntime.reply.dispatchReply(...)
67
+ → OpenClaw AI 推理
68
+ → outbound.sendText(...)
69
+ → api-client.sendTextMessage (POST /messages/send + HMAC 签名)
70
+ → imwe 平台 → 用户
71
+ ```
72
+
73
+ ## 配置示例
74
+
75
+ ### 单账号
76
+
77
+ ```json
78
+ {
79
+ "channels": {
80
+ "imwe": {
81
+ "apiBaseUrl": "https://api.imwe.example.com",
82
+ "appKey": "your-app-key",
83
+ "appSecret": "your-app-secret",
84
+ "dmPolicy": "pairing"
85
+ }
86
+ }
87
+ }
88
+ ```
89
+
90
+ ### 多账号
91
+
92
+ ```json
93
+ {
94
+ "channels": {
95
+ "imwe": {
96
+ "apiBaseUrl": "https://api.imwe.example.com",
97
+ "defaultAccount": "bot1",
98
+ "accounts": {
99
+ "bot1": {
100
+ "appKey": "key-a",
101
+ "appSecret": "secret-a",
102
+ "dmPolicy": "pairing"
103
+ },
104
+ "bot2": {
105
+ "appKey": "key-b",
106
+ "appSecret": "secret-b",
107
+ "dmPolicy": "allowlist",
108
+ "allowFrom": ["123456"]
109
+ }
110
+ }
111
+ }
112
+ }
113
+ }
114
+ ```
115
+
116
+ ### 环境变量(单账号 default)
117
+
118
+ ```bash
119
+ IMWE_API_BASE_URL=https://api.imwe.example.com
120
+ IMWE_APP_KEY=your-app-key
121
+ IMWE_APP_SECRET=your-app-secret
122
+ ```
123
+
124
+ ## E2EE 端到端加密
125
+
126
+ 单聊消息默认启用 E2EE(端到端加密),与 imwe iOS 客户端完全互通。无需额外配置,插件启动时自动完成密钥协商。
127
+
128
+ ### 安全边界
129
+
130
+ - **加密范围**:单聊文本 / Markdown / 媒体消息的 `PbChatMsgEnvelope`(含 URL、caption、blurHash 等元数据)均经 E2EE 加密
131
+ - **不在加密范围内**:媒体文件正文(CDN 资源本身以明文上传/下载,仅信令层走 E2EE);typing signal 保持明文
132
+ - 加解密失败时消息**不会降级为明文**发送或投递
133
+
134
+ ### 本地状态
135
+
136
+ 首次启动时,插件会在以下路径创建 E2EE 状态目录:
137
+
138
+ ```
139
+ ~/.openclaw/channel-data/imwe/<accountId>/e2ee/
140
+ ├── state.json # 密钥、session、PreKey 批次等持久化状态
141
+ ├── state.json.tmp # 两阶段写临时文件
142
+ └── state.lock # proper-lockfile 进程锁
143
+ ```
144
+
145
+ `state.json` 损坏时会自动重命名为 `state.json.corrupt-<timestamp>` 并重新 bootstrap。
146
+
147
+ ### Mock Server(开发/测试)
148
+
149
+ 项目提供了一个轻量 mock E2EE 服务端,用于本地开发与集成测试:
150
+
151
+ ```bash
152
+ # 启动(默认端口 3456)
153
+ node tests/mock-server/server.js
154
+
155
+ # 自定义端口
156
+ PORT=4000 node tests/mock-server/server.js
157
+ ```
158
+
159
+ mock server 覆盖 `getMe` / `pullMessages` / `sendMessage` + 5 个 E2EE 端点(create / replenish / apply / queryRequireNum / query),状态机行为详见 `tests/mock-server/README.md`。
160
+
161
+ 在集成测试中可 programmatic 引入:
162
+
163
+ ```js
164
+ import { server, PORT, resetState } from './tests/mock-server/server.js';
165
+ ```
166
+
167
+ ## 安装与配置
168
+
169
+ ```bash
170
+ openclaw plugins install @openclaw/imwe
171
+ openclaw setup imwe --http-url https://api.imwe.example.com --token <appKey> --private-key <appSecret>
172
+ ```
package/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * index.ts — 插件主入口(full 模式)
3
+ *
4
+ * 使用 defineChannelPluginEntry 定义入口。
5
+ * AppKey/AppSecret 签名认证无需 runtime 注入,故省略 setRuntime。
6
+ */
7
+
8
+ import { defineChannelPluginEntry } from 'openclaw/plugin-sdk/channel-core';
9
+ import { imwePlugin } from './src/channel.js';
10
+
11
+ export default defineChannelPluginEntry({
12
+ id: 'imwe',
13
+ name: 'imwe',
14
+ description: 'imwe 即时通讯渠道插件,AppKey/AppSecret 签名认证,支持多账号',
15
+ plugin: imwePlugin,
16
+ });
@@ -0,0 +1,58 @@
1
+ {
2
+ "id": "imwe",
3
+ "channels": ["imwe"],
4
+ "channelEnvVars": {
5
+ "imwe": ["IMWE_API_BASE_URL", "IMWE_APP_KEY", "IMWE_APP_SECRET"]
6
+ },
7
+ "configSchema": {
8
+ "type": "object",
9
+ "additionalProperties": false,
10
+ "properties": {
11
+ "accounts": {
12
+ "type": "object",
13
+ "description": "多账号配置,key 为 accountId",
14
+ "additionalProperties": {
15
+ "type": "object",
16
+ "additionalProperties": false,
17
+ "properties": {
18
+ "name": { "type": "string" },
19
+ "enabled": { "type": "boolean" },
20
+ "apiBaseUrl": { "type": "string" },
21
+ "appKey": { "type": "string" },
22
+ "appSecret": { "type": "string" },
23
+ "dmPolicy": {
24
+ "type": "string",
25
+ "enum": ["pairing", "allowlist", "open", "disabled"]
26
+ },
27
+ "allowFrom": {
28
+ "type": "array",
29
+ "items": { "type": ["string", "number"] }
30
+ },
31
+ "pollIntervalMs": {
32
+ "type": "number",
33
+ "description": "短轮询间隔(毫秒),默认 3000"
34
+ }
35
+ }
36
+ }
37
+ },
38
+ "defaultAccount": { "type": "string" },
39
+ "name": { "type": "string" },
40
+ "enabled": { "type": "boolean" },
41
+ "apiBaseUrl": { "type": "string" },
42
+ "appKey": { "type": "string" },
43
+ "appSecret": { "type": "string" },
44
+ "dmPolicy": {
45
+ "type": "string",
46
+ "enum": ["pairing", "allowlist", "open", "disabled"]
47
+ },
48
+ "allowFrom": {
49
+ "type": "array",
50
+ "items": { "type": ["string", "number"] }
51
+ },
52
+ "pollIntervalMs": {
53
+ "type": "number",
54
+ "description": "短轮询间隔(毫秒),默认 3000"
55
+ }
56
+ }
57
+ }
58
+ }
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@imweapp/openclaw-imwe",
3
+ "version": "2026.4.12-alpha.1",
4
+ "description": "OpenClaw imwe 渠道插件 —— AppKey/AppSecret 签名认证,支持多账号、私聊文字消息",
5
+ "type": "module",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "files": [
10
+ "proto/",
11
+ "src/",
12
+ "!src/**/*.test.ts",
13
+ "!src/**/node_modules/",
14
+ "index.ts",
15
+ "setup-entry.ts",
16
+ "openclaw.plugin.json",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "test": "vitest run --config vitest.config.ts",
21
+ "test:watch": "vitest --config vitest.config.ts"
22
+ },
23
+ "dependencies": {
24
+ "blurhash": "^2.0.5",
25
+ "proper-lockfile": "^4.1.2",
26
+ "protobufjs": "^7.4.0",
27
+ "sharp": "^0.34.5",
28
+ "zod": "^4.3.6"
29
+ },
30
+ "devDependencies": {
31
+ "fast-check": "^4.7.0",
32
+ "vite-plugin-top-level-await": "^1.6.0",
33
+ "vite-plugin-wasm": "^3.6.0",
34
+ "vitest": "^3.1.0"
35
+ },
36
+ "peerDependencies": {
37
+ "openclaw": ">=2026.4.11"
38
+ },
39
+ "peerDependenciesMeta": {
40
+ "openclaw": {
41
+ "optional": true
42
+ }
43
+ },
44
+ "license": "MIT",
45
+ "openclaw": {
46
+ "extensions": [
47
+ "./index.ts"
48
+ ],
49
+ "setupEntry": "./setup-entry.ts",
50
+ "channel": {
51
+ "id": "imwe",
52
+ "label": "ImWe",
53
+ "selectionLabel": "ImWe",
54
+ "docsPath": "",
55
+ "docsLabel": "documentation",
56
+ "blurb": "imwe 即时通讯平台机器人。",
57
+ "aliases": [
58
+ "iw"
59
+ ],
60
+ "order": 95,
61
+ "quickstartAllowFrom": true
62
+ },
63
+ "install": {
64
+ "npmSpec": "@imwe/opencalw-imwe",
65
+ "defaultChoice": "npm",
66
+ "minHostVersion": ">=2026.4.11"
67
+ },
68
+ "compat": {
69
+ "pluginApi": ">=2026.4.11"
70
+ }
71
+ },
72
+ "gitHead": "0feb00ac2bc3a2bbd3e7a5ade033c5319f574a68"
73
+ }
@@ -0,0 +1,43 @@
1
+ // PbBoxPullProto.proto — 事件箱消息拉取协议
2
+ //
3
+ // 来源:im_proto/Core/PbBoxPullProto.proto(原样拷贝)
4
+ // 插件通过 POST /api/im/open/bot/pullMessages 拉取机器人待处理消息。
5
+ //
6
+ // 请求体:PbBoxPullReq(直接序列化,不包装在 PbPacket 中)
7
+ // 响应体:PbBoxPullRsp
8
+ //
9
+ // 增量拉取流程:
10
+ // 1. 首次:startSeq=0,拉取全部历史消息
11
+ // 2. 收到响应后:若 milestone=true,将 endSeq 落库
12
+ // 3. 下次:startSeq=上次落库的 endSeq+1
13
+ // 4. 若 hasMore=true,立即继续拉取;否则等待轮询间隔
14
+
15
+ syntax = "proto3";
16
+
17
+ package proto;
18
+
19
+ option java_outer_classname = "BoxPullProto";
20
+ option java_package = "com.imwe.im.proto";
21
+
22
+ // 事件箱拉取请求
23
+ message PbBoxPullReq {
24
+ string boxId = 1; // 事件箱 ID(机器人的 mainAcctId)
25
+ optional bool onlyLastSeq = 2; // 仅返回最新序列号(用于初始化游标)
26
+ optional uint64 startSeq = 3; // 起始序列号(0=从头同步)
27
+ optional uint64 endSeq = 4; // 结束序列号(可选,不填则拉到最新)
28
+ repeated uint64 excludeSeqs = 5; // 排除的序列号列表
29
+ optional uint64 startTime = 6; // 指定时间后的消息(onlyLastSeq=true 时使用)
30
+ }
31
+
32
+ // 事件箱拉取响应
33
+ message PbBoxPullRsp {
34
+ string boxId = 1; // 事件箱 ID
35
+ uint64 startSeq = 2; // 本次同步起始序列号
36
+ uint64 endSeq = 3; // 本次同步结束序列号(0=无新消息)
37
+ bool milestone = 4; // 是否里程碑(true 时应将 endSeq 落库,下次从 endSeq+1 开始)
38
+ bool hasMore = 5; // 是否还有更多数据(true 时应立即继续拉取)
39
+ repeated PbRequest items = 6; // 事件列表(每个 item.body 为 PbChatMsgDeliverBody)
40
+ }
41
+
42
+ // PbRequest 在此文件中引用,需与 PbPacket.proto 中的定义一致
43
+ // protobufjs 加载时会自动合并同名消息,无需重复定义
@@ -0,0 +1,23 @@
1
+ // PbChatAudioContent.proto — 音频消息内容
2
+ //
3
+ // 来源:im_proto/MsgCore/PbChatAudioContent.proto(精简拷贝,保留插件所需字段)
4
+ // 作为 PbChatMsgEnvelope.audioContent(字段4)的消息体。
5
+
6
+ syntax = "proto3";
7
+
8
+ package proto;
9
+
10
+ option java_package = "com.imwe.im.proto";
11
+
12
+ import "PbChatFileMeta.proto";
13
+
14
+ message PbChatAudioContent {
15
+ message AttachmentMeta {
16
+ string attachmentId = 1;
17
+ int64 duration = 2; // 语音时长(秒)
18
+ optional PbChatFileMeta fileMeta = 4; // 音频文件元数据
19
+ }
20
+
21
+ optional AttachmentMeta meta = 1; // 音频文件信息
22
+ optional string speech = 2; // 语音转文字内容
23
+ }
@@ -0,0 +1,38 @@
1
+ // PbChatDeliverMsg.proto — 聊天消息投递体
2
+ //
3
+ // 来源:im_proto/Core/PbChatDeliverMsg.proto(原样拷贝)
4
+ // 服务端向客户端投递消息的统一载体,对所有会话类型通用。
5
+ // PbRequest.body 按 path 反序列化后得到 PbChatMsgDeliverBody。
6
+
7
+ syntax = "proto3";
8
+
9
+ package proto;
10
+
11
+ option java_outer_classname = "ChatDeliverProto";
12
+ option java_package = "com.imwe.im.proto";
13
+
14
+ // 聊天消息投递body(对客户端投递消息)
15
+ message PbChatMsgDeliverBody {
16
+ int32 convType = 1; // 会话类型,单聊/群聊/系统服务号/公众号
17
+ string fromId = 2; // 发送方IM账户id
18
+ string toId = 3; // 消息接收id,单聊接收账户id/密聊接收账户id/群组id
19
+ string toAcctId = 4; // 消息接收者IM账户id:个人投递时是多分ID(imAcctId);服务号投递时是主账号(mainAcctId)
20
+ optional string winId = 5; // 窗口id:密聊会话id
21
+
22
+ int64 envelopeType = 6; // 信封类型:1-聊天加密类型信封,2-操作内容信封,3-支付内容信封,4-服务号通知消息
23
+ string clientMsgId = 7; // 客户端消息ID
24
+ int64 clientStamp = 8; // 客户端消息时间戳
25
+ string serverMsgId = 9; // 服务端消息ID
26
+ int64 serverStamp = 10; // 服务端处理时间戳
27
+ bool e2eeFlag = 11; // 消息e2ee加密开关
28
+ bytes envelope = 12; // 消息内容(按envelopeType二次反序列化)
29
+
30
+ optional int64 msgAutoDeleteInterval = 13; // 自动删除时间长度配置
31
+ optional int64 msgTimingDeleteStamp = 14; // 定时删除时间
32
+
33
+ optional string fromE2eeId = 15; // 非必需,发送者端对端设备id
34
+ optional string toE2eeId = 16; // 非必需,接收者端对端设备id
35
+ optional string e2eeSid = 17; // 非必需,加密通道SessionId
36
+
37
+ optional int64 envelopeSubType = 18; // 信封子类型
38
+ }
@@ -0,0 +1,34 @@
1
+ // PbChatFileMeta.proto — 文件元数据
2
+ //
3
+ // 来源:im_proto/MsgCore/PbChatFileMeta.proto(精简拷贝,保留插件所需字段)
4
+ // 包含文件 URL、加密信息、文件类型等元数据,被 PbChatRichMediaContent 和 PbChatAudioContent 引用。
5
+
6
+ syntax = "proto3";
7
+
8
+ package proto;
9
+
10
+ option java_package = "com.imwe.im.proto";
11
+
12
+ // 文件类型枚举
13
+ enum PbChatFileMetaType {
14
+ MEDIA_TYPE_UNKNOWN = 0; // 未知类型
15
+ MEDIA_TYPE_IMAGE = 1; // 图片类型,如jpg, png等
16
+ MEDIA_TYPE_VIDEO = 2; // 视频类型,如mp4, mov等
17
+ MEDIA_TYPE_AUDIO = 3; // 音频类型,如mp3, aac等
18
+ MEDIA_TYPE_FILE = 4; // 文件类型,需根据文件后缀名区分具体文件类型
19
+ }
20
+
21
+ // 文件元数据(上传/下载后的文件信息)
22
+ message PbChatFileMeta {
23
+ string url = 1; // 服务端存储的url
24
+ bytes key = 2; // 加密密钥(Algorithm: kCCAlgorithmAES)
25
+ bytes iv = 3; // 加密密钥(Algorithm: AES/CTR)
26
+ string fileType = 4; // 文件类型(jpg, png, mp4, mp3等)
27
+ string digest = 5; // 文件摘要(用于文件校验)
28
+ int64 length = 6; // 密文长度
29
+ int64 plaintextLength = 7; // 明文长度
30
+ int64 expireTime = 8; // 过期时间,单位:毫秒
31
+ string attachmentId = 9;
32
+ PbChatFileMetaType mediaType = 10; // 1: 图片,2: 视频,3: 音频
33
+ string opCreds = 11; // 操作凭证,单聊文件上传时由服务端返回,接收方下载后用于回调删除
34
+ }
@@ -0,0 +1,93 @@
1
+ // PbChatMsg.proto — 聊天消息信封定义
2
+ //
3
+ // 来源:im_proto/Core/PbChatMsg.proto(精简拷贝,仅保留插件所需消息)
4
+ // 插件处理 envelopeType=1(PbChatMsgEnvelope)中的文本消息(textContent 字段3)、
5
+ // 音频消息(audioContent 字段4)、图片/视频消息(richMediaContent 字段9)、文件消息(fileContent 字段13)。
6
+ // 其他 oneof 分支(callContent 等)保留字段号定义,解码时跳过。
7
+ //
8
+ // 注意:原始文件 import 了大量其他 proto,此处仅保留插件实际用到的消息定义,
9
+ // 未用到的消息类型(callContent 等)以 bytes 占位,不影响字段编号。
10
+
11
+ syntax = "proto3";
12
+
13
+ package proto;
14
+
15
+ import "PbChatTextContent.proto";
16
+ import "PbChatAudioContent.proto";
17
+ import "PbChatRichMediaContent.proto";
18
+ import "PbMarkdownContent.proto";
19
+ import "PbMsgReadStampContent.proto";
20
+
21
+ option java_outer_classname = "ChatMessageProto";
22
+ option java_package = "com.imwe.im.proto";
23
+
24
+ // 聊天消息信封(envelopeType=1)
25
+ // PbChatMsgDeliverBody.envelope 按 envelopeType=1 反序列化后得到此消息。
26
+ message PbChatMsgEnvelope {
27
+ optional bytes senderKey = 1; // 群聊senderKey消息
28
+ optional int32 autoDeleteType = 2; // 阅后即焚类型:0(默认),1(火焰)
29
+
30
+ // oneof envelope — 13种消息内容,插件处理 textContent/audioContent/richMediaContent/fileContent
31
+ // 其余字段保留编号,解码时遇到会被 protobufjs 跳过(unknown field)
32
+ oneof envelope {
33
+ PbChatTextContent textContent = 3; // 文本消息内容(插件处理)
34
+ PbChatAudioContent audioContent = 4; // 语音消息(插件处理)
35
+ bytes callContent = 5; // 音视频呼叫(占位,不解析)
36
+ bytes businessCardContent = 6; // 个人名片(占位,不解析)
37
+ bytes forwardContent = 7; // 合并转发(占位,不解析)
38
+ bytes emoteContent = 8; // 表情消息(占位,不解析)
39
+ PbChatRichMediaContent richMediaContent = 9; // 图片/视频(插件处理)
40
+ bytes locationRequestContent = 10; // 位置请求(占位,不解析)
41
+ bytes locationContent = 11; // 位置消息(占位,不解析)
42
+ bytes groupInviteContent = 12; // 群邀请(占位,不解析)
43
+ PbChatRichMediaContent fileContent = 13; // 文件消息(插件处理)
44
+ bytes aiNoteSummaryContent = 14; // AI笔记总结(占位,不解析)
45
+ bytes interactiveContent = 15; // 互动消息(占位,不解析)
46
+ PbMarkdownContent markdownContent = 16; // Markdown 消息内容(插件处理)
47
+ }
48
+ }
49
+
50
+ // 消息响应(服务端返回给客户端的发送结果)
51
+ message PbMsgRspBody {
52
+ string clientMsgId = 1; // 客户端消息ID
53
+ int64 clientStamp = 2; // 客户端消息时间戳
54
+ string serverMsgId = 3; // 服务端消息ID
55
+ int64 serverStamp = 4; // 服务端处理时间戳
56
+ }
57
+
58
+ // 操作消息信封(envelopeType=2)
59
+ // PbSingleChatMsgReqBody.envelope 按 envelopeType=2 序列化后得到此消息。
60
+ // 字段号与 docs/im_proto/MsgCore/PbChatMsg.proto 中的 PbChatOperationEnvelope 一致。
61
+ // 插件仅处理 botThinking(字段9),其余操作类型以 bytes 占位。
62
+ message PbChatOperationEnvelope {
63
+ oneof content {
64
+ bytes msgRecall = 1; // 消息撤回(占位,不解析)
65
+ bytes bidirectionalDelete = 2; // 双向删除(占位,不解析)
66
+ bytes bidirectionalDeleteAnswer = 3; // 双向删除回复(占位,不解析)
67
+ bytes announcement = 4; // 群公告(占位,不解析)
68
+ PbMessageDecryptErrorSendContent decryptError = 5; // 单聊解密失败回执信令(插件处理)
69
+ bytes groupDecryptError = 6; // 群聊解密失败(占位,不解析)
70
+ PbMsgReadStampSendContent msgRead = 7; // 消息已读
71
+ bytes msgArrived = 8; // 消息已送达(占位,不解析)
72
+ PbBotThinkingSignal botThinking = 9; // Bot thinking 信号(插件处理)
73
+ }
74
+ }
75
+
76
+ // Bot thinking 信号
77
+ // status: 1=begin(开始输入),2=end(结束输入)
78
+ // botImAcctId: Bot 的 imAcctId,标识哪个 bot 正在输入
79
+ message PbBotThinkingSignal {
80
+ int32 status = 1; // 1=begin, 2=end
81
+ string botImAcctId = 2; // Bot 的 imAcctId
82
+ }
83
+
84
+ // 单聊解密失败信令内容(envelopeType=2 / oneof content case 5)
85
+ // 本端对收到的加密单聊消息解密失败时,回发给原发送方。
86
+ // 字段方向语义:由"信令发送方"视角填充:
87
+ // - e2eeId:信令发送方自己(= 解密失败消息的接收者设备)
88
+ // - otherE2eeId:失败消息的原发送方(= 信令接收方,应 == 对方 local.e2eeId)
89
+ message PbMessageDecryptErrorSendContent {
90
+ string clientMsgId = 1; // 失败的那条消息的 clientMsgId
91
+ string e2eeId = 2; // 信令发送方自己的 e2eeId
92
+ string otherE2eeId = 3; // 失败消息的原发送方 e2eeId
93
+ }
@@ -0,0 +1,31 @@
1
+ // PbChatRichMediaContent.proto — 图片/视频/文件混合消息内容
2
+ //
3
+ // 来源:im_proto/MsgCore/PbChatRichMediaContent.proto(精简拷贝,保留插件所需字段)
4
+ // 作为 PbChatMsgEnvelope.richMediaContent(字段9)和 fileContent(字段13)的消息体。
5
+
6
+ syntax = "proto3";
7
+
8
+ package proto;
9
+
10
+ option java_package = "com.imwe.im.proto";
11
+
12
+ import "PbChatFileMeta.proto";
13
+
14
+ message PbChatRichMediaContent {
15
+ message AttachmentMeta {
16
+ string attachmentId = 1;
17
+ PbChatFileMetaType mediaType = 2; // 文件类型,1: 图片,2: 视频
18
+ optional PbChatFileMeta fileMeta = 3; // 媒体文件元数据
19
+ optional int64 width = 4; // 宽度(像素)
20
+ optional int64 height = 5; // 高度(像素)
21
+ optional string blurHash = 6; // 模糊哈希(仅图片和视频截图)
22
+ optional int64 duration = 8; // 视频时长(秒)
23
+ optional AttachmentMeta snapshot = 9; // 视频缩略图
24
+ optional string caption = 11; // 文字描述
25
+ optional string fileName = 13; // 文件名
26
+ optional int64 fileSize = 14; // 文件大小(字节)
27
+ }
28
+
29
+ repeated AttachmentMeta meta = 1;
30
+ optional string caption = 2; // 文字描述
31
+ }
@@ -0,0 +1,38 @@
1
+ // PbChatTextContent.proto — 文本消息内容
2
+ //
3
+ // 来源:im_proto/Core/PbChatTextContent.proto(原样拷贝)
4
+ // 文本消息内容,作为 PbChatMsgEnvelope.textContent(字段3)的消息体。
5
+
6
+ syntax = "proto3";
7
+
8
+ package proto;
9
+
10
+ option java_package = "com.imwe.im.proto";
11
+
12
+ // 精确表示消息正文(content)中某一段字符范围,比如@,超链接、格式化文字等
13
+ message BodyRange {
14
+ // 消息正文范围类型
15
+ enum BodyRangeKey {
16
+ UNKNOWN = 0;
17
+ AT_USER = 1;
18
+ LINK = 2;
19
+ }
20
+
21
+ BodyRangeKey key = 1; // 例如 @、超链接 等
22
+ string payload = 2; // 当key==AT_USER时,value是被@的人的acctId
23
+ optional int32 start = 3; // 开始位置,UTF-16 单位
24
+ optional int32 length = 4; // 长度,UTF-16 单位
25
+ }
26
+
27
+ // 文本消息内容
28
+ message PbChatTextContent {
29
+ string text = 1; // 文本内容
30
+ repeated BodyRange bodyRanges = 2; // 消息正文范围(@、超链接、格式化等)
31
+ optional string referenceClientMsgId = 3; // 引用消息的clientID
32
+ }
33
+
34
+ // 文本消息落库的 pb(插件不使用,保留完整性)
35
+ message PbChatTextStoreContent {
36
+ repeated BodyRange bodyRanges = 1; // 消息正文范围(@、超链接、格式化等)
37
+ optional string translation = 2; // 消息的翻译
38
+ }
@@ -0,0 +1,18 @@
1
+ // PbMarkdownContent.proto — Markdown 消息内容
2
+ //
3
+ // 来源:im_proto/MsgCore/PbMarkdownContent.proto(原样拷贝)
4
+ // 作为 PbChatMsgEnvelope.markdownContent(字段16)的消息体。
5
+
6
+ syntax = "proto3";
7
+
8
+ package proto;
9
+
10
+ option java_package = "com.imwe.im.proto";
11
+
12
+ // mark消息
13
+ message PbMarkdownContent {
14
+ // 摘要标题
15
+ string digest = 1;
16
+ // markdown内容
17
+ string markdown = 2;
18
+ }