vanshi-sdk 1.0.4 → 1.0.6
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.
- package/README.md +178 -59
- package/dist/index.cjs +88 -8
- package/dist/index.d.ts +77 -6
- package/dist/index.js +88 -8
- package/dist/index.mjs +88 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,96 +1,215 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Vanshi Chat SDK
|
|
2
2
|
|
|
3
|
+
浏览器侧 IM SDK,提供 socket 连接、登录、会话、文本消息、媒体消息、历史消息、消息回执和事件监听能力。
|
|
3
4
|
|
|
5
|
+
当前版本:`vanshi-sdk@1.0.5`
|
|
4
6
|
|
|
5
|
-
##
|
|
7
|
+
## 安装
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
内网 npm:
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
```bash
|
|
12
|
+
npm install vanshi-sdk@1.0.5 --registry=https://npm.whatimcloud.com/
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
默认 registry 已配置到内网时:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install vanshi-sdk@1.0.5
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 引入方式
|
|
10
22
|
|
|
11
|
-
|
|
23
|
+
### ESM
|
|
12
24
|
|
|
13
|
-
|
|
14
|
-
|
|
25
|
+
```ts
|
|
26
|
+
import VanshiSdk from 'vanshi-sdk'
|
|
15
27
|
|
|
28
|
+
await VanshiSdk.create()
|
|
29
|
+
await VanshiSdk.login({ token: 'your_token' })
|
|
16
30
|
```
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
31
|
+
|
|
32
|
+
### 命名导入
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { create, login, sendMessage } from 'vanshi-sdk'
|
|
36
|
+
|
|
37
|
+
await create()
|
|
38
|
+
await login({ token: 'your_token' })
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### CommonJS
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
const VanshiSdkModule = require('vanshi-sdk')
|
|
45
|
+
const VanshiSdk = VanshiSdkModule.default || VanshiSdkModule
|
|
21
46
|
```
|
|
22
47
|
|
|
23
|
-
|
|
48
|
+
### script 标签
|
|
49
|
+
|
|
50
|
+
```html
|
|
51
|
+
<script src="./node_modules/vanshi-sdk/dist/index.js"></script>
|
|
52
|
+
<script>
|
|
53
|
+
async function main() {
|
|
54
|
+
await VanshiSdk.create()
|
|
55
|
+
await VanshiSdk.login({ token: 'your_token' })
|
|
56
|
+
}
|
|
24
57
|
|
|
25
|
-
|
|
58
|
+
main()
|
|
59
|
+
</script>
|
|
60
|
+
```
|
|
26
61
|
|
|
27
|
-
|
|
62
|
+
script 引入后会暴露全局变量:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
window.VanshiSdk
|
|
66
|
+
```
|
|
28
67
|
|
|
29
|
-
|
|
30
|
-
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
|
|
31
|
-
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
|
|
32
|
-
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
|
|
33
|
-
- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
|
|
68
|
+
## 服务地址配置
|
|
34
69
|
|
|
35
|
-
|
|
70
|
+
如需切换环境,请在调用 `create()` 前设置:
|
|
36
71
|
|
|
37
|
-
|
|
72
|
+
```ts
|
|
73
|
+
window.__VANSHI_SOCKET_URL = 'wss://your-socket-host'
|
|
74
|
+
window.__VANSHI_HTTP_URL = 'https://your-api-host/api/files/sdkUpload'
|
|
75
|
+
```
|
|
38
76
|
|
|
39
|
-
|
|
40
|
-
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
|
|
41
|
-
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
|
|
42
|
-
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
|
|
43
|
-
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
|
|
77
|
+
## 快速开始
|
|
44
78
|
|
|
45
|
-
|
|
79
|
+
```ts
|
|
80
|
+
import VanshiSdk from 'vanshi-sdk'
|
|
46
81
|
|
|
47
|
-
|
|
82
|
+
async function bootstrap() {
|
|
83
|
+
await VanshiSdk.create()
|
|
48
84
|
|
|
49
|
-
|
|
85
|
+
const success = await VanshiSdk.login({
|
|
86
|
+
token: 'your_token',
|
|
87
|
+
})
|
|
50
88
|
|
|
51
|
-
|
|
52
|
-
|
|
89
|
+
if (!success) {
|
|
90
|
+
throw new Error('SDK 登录失败')
|
|
91
|
+
}
|
|
53
92
|
|
|
54
|
-
|
|
55
|
-
|
|
93
|
+
const conversations = await VanshiSdk.queryConversation({
|
|
94
|
+
count: 20,
|
|
95
|
+
nextConversation: undefined,
|
|
96
|
+
})
|
|
56
97
|
|
|
57
|
-
|
|
58
|
-
|
|
98
|
+
console.log('会话列表', conversations)
|
|
99
|
+
}
|
|
59
100
|
|
|
60
|
-
|
|
61
|
-
|
|
101
|
+
bootstrap().catch(console.error)
|
|
102
|
+
```
|
|
62
103
|
|
|
63
|
-
##
|
|
64
|
-
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
|
|
104
|
+
## 发送文本消息
|
|
65
105
|
|
|
66
|
-
|
|
67
|
-
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
|
|
106
|
+
SDK 会在发送消息前自动生成 `clientBizId`。该字段包含时间戳,可用于客户端本地消息和服务端返回消息合并。
|
|
68
107
|
|
|
69
|
-
|
|
70
|
-
|
|
108
|
+
```ts
|
|
109
|
+
const result = await VanshiSdk.sendMessage({
|
|
110
|
+
message: {
|
|
111
|
+
type: '1',
|
|
112
|
+
message: '你好',
|
|
113
|
+
extendedData: '{}',
|
|
114
|
+
// 可选;不传时 SDK 自动生成 cbiz_${timestamp}_${sequence}_${random}
|
|
115
|
+
clientBizId: `biz_${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
|
116
|
+
},
|
|
117
|
+
toConversationID: 'conversation_id',
|
|
118
|
+
conversationType: 2,
|
|
119
|
+
})
|
|
71
120
|
|
|
72
|
-
|
|
73
|
-
|
|
121
|
+
console.log(result.clientBizId)
|
|
122
|
+
```
|
|
74
123
|
|
|
75
|
-
##
|
|
76
|
-
|
|
124
|
+
## 发送媒体消息并即时渲染
|
|
125
|
+
|
|
126
|
+
`sendMediaMessage` 支持 `onMessageAttached`。SDK 会先生成 `clientBizId` 和本地媒体消息,立即回调给客户端渲染,然后继续上传文件并发送到后端。
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
const file = input.files?.[0]
|
|
130
|
+
|
|
131
|
+
if (file) {
|
|
132
|
+
const result = await VanshiSdk.sendMediaMessage(
|
|
133
|
+
{
|
|
134
|
+
message: {
|
|
135
|
+
type: '12',
|
|
136
|
+
message: '文件消息',
|
|
137
|
+
extendedData: '{}',
|
|
138
|
+
file,
|
|
139
|
+
},
|
|
140
|
+
toConversationID: 'conversation_id',
|
|
141
|
+
conversationType: 2,
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
onMessageAttached(localMessage) {
|
|
145
|
+
// localMessage.clientBizId 可用于后续和服务端返回消息合并
|
|
146
|
+
// localMessage.localPreviewUrl 可用于本地预览
|
|
147
|
+
appendMessage(localMessage)
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
updateMessage(result.clientBizId, result)
|
|
153
|
+
}
|
|
154
|
+
```
|
|
77
155
|
|
|
78
|
-
|
|
79
|
-
State if you are open to contributions and what your requirements are for accepting them.
|
|
156
|
+
`onMessageAttached` 不会影响 SDK 后续发送流程;回调内部异常会被 SDK 忽略。
|
|
80
157
|
|
|
81
|
-
|
|
158
|
+
本地媒体消息常用字段:
|
|
82
159
|
|
|
83
|
-
|
|
160
|
+
| 字段 | 类型 | 说明 |
|
|
161
|
+
| --- | --- | --- |
|
|
162
|
+
| `clientBizId` | `string` | 客户端业务预发 ID |
|
|
163
|
+
| `localMessageID` | `string` | 本地消息 ID,默认等于 `clientBizId` |
|
|
164
|
+
| `conversationID` | `string` | 会话 ID |
|
|
165
|
+
| `conversationType` | `number` | 会话类型 |
|
|
166
|
+
| `type` | `string` | 消息类型 |
|
|
167
|
+
| `message` | `string` | 消息内容 |
|
|
168
|
+
| `sentStatus` | `number` | 本地发送状态,`0` 表示发送中 |
|
|
169
|
+
| `fileName` | `string` | 文件名,最长 20 个字符;上传接口也使用截取后的文件名 |
|
|
170
|
+
| `fileSize` | `string` | 文件大小 |
|
|
171
|
+
| `localPreviewUrl` | `string` | 浏览器本地预览地址 |
|
|
84
172
|
|
|
85
|
-
##
|
|
86
|
-
Show your appreciation to those who have contributed to the project.
|
|
173
|
+
## 常用事件
|
|
87
174
|
|
|
88
|
-
|
|
89
|
-
|
|
175
|
+
```ts
|
|
176
|
+
const handler = (payload: unknown) => {
|
|
177
|
+
console.log(payload)
|
|
178
|
+
}
|
|
90
179
|
|
|
91
|
-
|
|
92
|
-
|
|
180
|
+
VanshiSdk.on('conversationChanged', handler)
|
|
181
|
+
VanshiSdk.on('receiveGroupMessage', handler)
|
|
182
|
+
VanshiSdk.on('messageReceiptChanged', handler)
|
|
93
183
|
|
|
94
|
-
|
|
184
|
+
VanshiSdk.off('conversationChanged', handler)
|
|
185
|
+
```
|
|
95
186
|
|
|
96
|
-
|
|
187
|
+
## 常用 API
|
|
188
|
+
|
|
189
|
+
| API | 说明 |
|
|
190
|
+
| --- | --- |
|
|
191
|
+
| `create()` | 创建 socket 连接 |
|
|
192
|
+
| `login({ token })` | 登录 SDK |
|
|
193
|
+
| `logout()` | 退出登录并清理本地 token |
|
|
194
|
+
| `isConnected()` | 判断 socket 是否连接 |
|
|
195
|
+
| `queryConversation(params)` | 查询会话列表 |
|
|
196
|
+
| `sendMessage(params)` | 发送文本消息 |
|
|
197
|
+
| `sendMediaMessage(params, options?)` | 发送媒体消息,支持本地消息预回调 |
|
|
198
|
+
| `queryHistoryMessage(params)` | 查询历史消息 |
|
|
199
|
+
| `sendMessageReceiptsRead(...)` | 发送已读回执 |
|
|
200
|
+
| `queryMessageReceiptsInfo(...)` | 查询消息回执 |
|
|
201
|
+
|
|
202
|
+
## 详细文档
|
|
203
|
+
|
|
204
|
+
- [VANSHI_SDK.md](./VANSHI_SDK.md):客户接入完整文档
|
|
205
|
+
- [SDK.md](./SDK.md):SDK API 说明
|
|
206
|
+
- [USAGE.md](./USAGE.md):基础使用示例
|
|
207
|
+
|
|
208
|
+
## 发布产物
|
|
209
|
+
|
|
210
|
+
| 文件 | 用途 |
|
|
211
|
+
| --- | --- |
|
|
212
|
+
| `dist/index.js` | 浏览器 script 标签全局版本 |
|
|
213
|
+
| `dist/index.mjs` | ESM 版本 |
|
|
214
|
+
| `dist/index.cjs` | CommonJS 版本 |
|
|
215
|
+
| `dist/index.d.ts` | TypeScript 类型声明 |
|
package/dist/index.cjs
CHANGED
|
@@ -774,6 +774,62 @@ var postSdkDeleteAllMessageApiRequest = (data) => {
|
|
|
774
774
|
};
|
|
775
775
|
|
|
776
776
|
// src/sdk/message.ts
|
|
777
|
+
var clientBizIdSeq = 0;
|
|
778
|
+
var MAX_FILE_NAME_LENGTH = 20;
|
|
779
|
+
function generateClientBizId() {
|
|
780
|
+
clientBizIdSeq = (clientBizIdSeq + 1) % 1e6;
|
|
781
|
+
const timestamp = Date.now();
|
|
782
|
+
const sequence = clientBizIdSeq.toString(36);
|
|
783
|
+
const random = Math.random().toString(36).slice(2, 10);
|
|
784
|
+
return `cbiz_${timestamp}_${sequence}_${random}`;
|
|
785
|
+
}
|
|
786
|
+
function withClientBizId(message) {
|
|
787
|
+
return {
|
|
788
|
+
...message,
|
|
789
|
+
clientBizId: message.clientBizId || generateClientBizId()
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
function truncateFileName(fileName) {
|
|
793
|
+
return fileName.length > MAX_FILE_NAME_LENGTH ? fileName.slice(0, MAX_FILE_NAME_LENGTH) : fileName;
|
|
794
|
+
}
|
|
795
|
+
function getDisplayFileName(file, fallback = "") {
|
|
796
|
+
return truncateFileName(file?.name || fallback || "");
|
|
797
|
+
}
|
|
798
|
+
function createLocalMediaMessage(message, toConversationID, conversationType) {
|
|
799
|
+
const file = message.file;
|
|
800
|
+
const localPreviewUrl = file && typeof URL !== "undefined" && URL.createObjectURL ? URL.createObjectURL(file) : void 0;
|
|
801
|
+
return {
|
|
802
|
+
massage: message.message,
|
|
803
|
+
message: message.message,
|
|
804
|
+
type: message.type,
|
|
805
|
+
extendedData: message.extendedData,
|
|
806
|
+
messageID: "",
|
|
807
|
+
orderKey: "",
|
|
808
|
+
clientSid: "",
|
|
809
|
+
localMessageID: message.clientBizId,
|
|
810
|
+
clientBizId: message.clientBizId,
|
|
811
|
+
conversationID: toConversationID,
|
|
812
|
+
conversationType,
|
|
813
|
+
senderUserID: "",
|
|
814
|
+
direction: 0,
|
|
815
|
+
sentStatus: 0,
|
|
816
|
+
timestamp: Date.now(),
|
|
817
|
+
receiptStatus: 0,
|
|
818
|
+
isServerMessage: false,
|
|
819
|
+
isBroadcastMessage: false,
|
|
820
|
+
isGroupTargetedMessage: "",
|
|
821
|
+
id: "",
|
|
822
|
+
file,
|
|
823
|
+
fileName: getDisplayFileName(file, message.fileName),
|
|
824
|
+
fileSize: file ? String(file.size) : message.fileSize || "",
|
|
825
|
+
fileDownloadUrl: message.fileDownloadUrl || localPreviewUrl || "",
|
|
826
|
+
thumbnailDownloadUrl: message.thumbnailDownloadUrl || localPreviewUrl || "",
|
|
827
|
+
thumbnailWidth: message.thumbnailWidth || "",
|
|
828
|
+
thumbnailHeight: message.thumbnailHeight || "",
|
|
829
|
+
duration: message.duration || "",
|
|
830
|
+
localPreviewUrl
|
|
831
|
+
};
|
|
832
|
+
}
|
|
777
833
|
async function getImageSize(file) {
|
|
778
834
|
return new Promise((resolve, reject) => {
|
|
779
835
|
const url = URL.createObjectURL(file);
|
|
@@ -792,9 +848,14 @@ async function getImageSize(file) {
|
|
|
792
848
|
img.src = url;
|
|
793
849
|
});
|
|
794
850
|
}
|
|
795
|
-
async function uploadSdkFile(file) {
|
|
851
|
+
async function uploadSdkFile(file, fileName) {
|
|
796
852
|
const formData = new FormData();
|
|
797
|
-
|
|
853
|
+
const uploadFileName = truncateFileName(fileName || file.name || "");
|
|
854
|
+
if (uploadFileName) {
|
|
855
|
+
formData.append("file", file, uploadFileName);
|
|
856
|
+
} else {
|
|
857
|
+
formData.append("file", file);
|
|
858
|
+
}
|
|
798
859
|
const response = await fetch(getUploadUrl(), {
|
|
799
860
|
method: "POST",
|
|
800
861
|
headers: {
|
|
@@ -821,7 +882,7 @@ async function enrichMediaMessage(message) {
|
|
|
821
882
|
}
|
|
822
883
|
const enrichedMessage = {
|
|
823
884
|
...message,
|
|
824
|
-
fileName: file
|
|
885
|
+
fileName: getDisplayFileName(file, message.fileName),
|
|
825
886
|
fileSize: String(file.size)
|
|
826
887
|
};
|
|
827
888
|
if (isSameUid(message.type, "11" /* Image */)) {
|
|
@@ -836,9 +897,12 @@ async function enrichMediaMessage(message) {
|
|
|
836
897
|
enrichedMessage.duration = String(res.duration);
|
|
837
898
|
enrichedMessage.thumbnailWidth = String(res.videoWidth);
|
|
838
899
|
enrichedMessage.thumbnailHeight = String(res.videoHeight);
|
|
839
|
-
enrichedMessage.thumbnailDownloadUrl = await uploadSdkFile(
|
|
900
|
+
enrichedMessage.thumbnailDownloadUrl = await uploadSdkFile(
|
|
901
|
+
res.firstFrameFile,
|
|
902
|
+
res.firstFrameFile.name
|
|
903
|
+
);
|
|
840
904
|
}
|
|
841
|
-
const fileDownloadUrl = await uploadSdkFile(file);
|
|
905
|
+
const fileDownloadUrl = await uploadSdkFile(file, enrichedMessage.fileName);
|
|
842
906
|
enrichedMessage.fileDownloadUrl = fileDownloadUrl;
|
|
843
907
|
if (isSameUid(message.type, "11" /* Image */)) {
|
|
844
908
|
enrichedMessage.thumbnailDownloadUrl = fileDownloadUrl;
|
|
@@ -847,13 +911,29 @@ async function enrichMediaMessage(message) {
|
|
|
847
911
|
return enrichedMessage;
|
|
848
912
|
}
|
|
849
913
|
async function sendMessage(payload) {
|
|
850
|
-
const response = await postSdkSendMessageApiRequest(
|
|
914
|
+
const response = await postSdkSendMessageApiRequest({
|
|
915
|
+
...payload,
|
|
916
|
+
message: withClientBizId(payload.message)
|
|
917
|
+
});
|
|
851
918
|
return unwrapSdkData(response);
|
|
852
919
|
}
|
|
853
|
-
async function sendMediaMessage(payload) {
|
|
920
|
+
async function sendMediaMessage(payload, options) {
|
|
921
|
+
const messageWithClientBizId = withClientBizId(payload.message);
|
|
922
|
+
if (options?.onMessageAttached) {
|
|
923
|
+
try {
|
|
924
|
+
options.onMessageAttached(
|
|
925
|
+
createLocalMediaMessage(
|
|
926
|
+
messageWithClientBizId,
|
|
927
|
+
payload.toConversationID,
|
|
928
|
+
payload.conversationType
|
|
929
|
+
)
|
|
930
|
+
);
|
|
931
|
+
} catch {
|
|
932
|
+
}
|
|
933
|
+
}
|
|
854
934
|
const response = await postSdkSendMediaMessageApiRequest({
|
|
855
935
|
...payload,
|
|
856
|
-
message: await enrichMediaMessage(
|
|
936
|
+
message: await enrichMediaMessage(messageWithClientBizId)
|
|
857
937
|
});
|
|
858
938
|
return unwrapSdkData(response);
|
|
859
939
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -226,6 +226,10 @@ interface YapiPostSdkSendMediaMessageData {
|
|
|
226
226
|
* 消息表 id
|
|
227
227
|
*/
|
|
228
228
|
localMessageID: string
|
|
229
|
+
/**
|
|
230
|
+
* 预发 id
|
|
231
|
+
*/
|
|
232
|
+
clientBizId: string
|
|
229
233
|
/**
|
|
230
234
|
* 会话 id
|
|
231
235
|
*/
|
|
@@ -366,6 +370,10 @@ interface LastMessageData {
|
|
|
366
370
|
* SDK 本地生成的 MessageID,开发者无需关注。
|
|
367
371
|
*/
|
|
368
372
|
localMessageID: string;
|
|
373
|
+
/**
|
|
374
|
+
* 预发 id
|
|
375
|
+
*/
|
|
376
|
+
clientBizId: string;
|
|
369
377
|
/**
|
|
370
378
|
* 0-不是回执。,1-回执进行中。2-回执已完成。3-回执已过期。3-回执已失败。
|
|
371
379
|
*/
|
|
@@ -569,6 +577,10 @@ interface ZIMSEDMessage {
|
|
|
569
577
|
* 自定义的拓展字段
|
|
570
578
|
*/
|
|
571
579
|
extendedData: string;
|
|
580
|
+
/**
|
|
581
|
+
* 客户端业务预发 ID。未传时 SDK 会自动生成。
|
|
582
|
+
*/
|
|
583
|
+
clientBizId?: string;
|
|
572
584
|
}
|
|
573
585
|
interface ZIMSEDMediaMessage {
|
|
574
586
|
message: string;
|
|
@@ -577,6 +589,10 @@ interface ZIMSEDMediaMessage {
|
|
|
577
589
|
* 自定义的拓展字段
|
|
578
590
|
*/
|
|
579
591
|
extendedData: string;
|
|
592
|
+
/**
|
|
593
|
+
* 客户端业务预发 ID。未传时 SDK 会自动生成。
|
|
594
|
+
*/
|
|
595
|
+
clientBizId?: string;
|
|
580
596
|
file?: File;
|
|
581
597
|
thumbnailHeight?: string;
|
|
582
598
|
thumbnailWidth?: string;
|
|
@@ -586,6 +602,49 @@ interface ZIMSEDMediaMessage {
|
|
|
586
602
|
fileName?: string;
|
|
587
603
|
fileSize?: string;
|
|
588
604
|
}
|
|
605
|
+
interface ZIMLocalMediaMessage {
|
|
606
|
+
/**
|
|
607
|
+
* 服务端返回消息内容字段兼容名。
|
|
608
|
+
*/
|
|
609
|
+
massage: string;
|
|
610
|
+
/**
|
|
611
|
+
* 消息内容。
|
|
612
|
+
*/
|
|
613
|
+
message: string;
|
|
614
|
+
type: SEDMessageType$1;
|
|
615
|
+
extendedData: string;
|
|
616
|
+
messageID: string;
|
|
617
|
+
orderKey: string;
|
|
618
|
+
clientSid: string;
|
|
619
|
+
localMessageID: string;
|
|
620
|
+
clientBizId: string;
|
|
621
|
+
conversationID: string;
|
|
622
|
+
conversationType: ZIMConversationType;
|
|
623
|
+
senderUserID: string;
|
|
624
|
+
direction: number;
|
|
625
|
+
sentStatus: number;
|
|
626
|
+
timestamp: number;
|
|
627
|
+
receiptStatus: number;
|
|
628
|
+
isServerMessage: boolean;
|
|
629
|
+
isBroadcastMessage: boolean;
|
|
630
|
+
isGroupTargetedMessage: string;
|
|
631
|
+
id: string;
|
|
632
|
+
file?: File;
|
|
633
|
+
fileName: string;
|
|
634
|
+
fileSize: string;
|
|
635
|
+
fileDownloadUrl: string;
|
|
636
|
+
thumbnailDownloadUrl: string;
|
|
637
|
+
thumbnailWidth: string;
|
|
638
|
+
thumbnailHeight: string;
|
|
639
|
+
duration: string;
|
|
640
|
+
/**
|
|
641
|
+
* 浏览器本地预览 URL。客户端使用完后可自行 URL.revokeObjectURL。
|
|
642
|
+
*/
|
|
643
|
+
localPreviewUrl?: string;
|
|
644
|
+
}
|
|
645
|
+
interface ZIMMediaMessageSendOptions {
|
|
646
|
+
onMessageAttached?: (message: ZIMLocalMediaMessage) => void;
|
|
647
|
+
}
|
|
589
648
|
interface ZIMMessageSeDResult {
|
|
590
649
|
/**
|
|
591
650
|
* 消息
|
|
@@ -630,6 +689,10 @@ interface ZIMMessageSeDResult {
|
|
|
630
689
|
* 消息表 id
|
|
631
690
|
*/
|
|
632
691
|
localMessageID: string;
|
|
692
|
+
/**
|
|
693
|
+
* 预发 id
|
|
694
|
+
*/
|
|
695
|
+
clientBizId: string;
|
|
633
696
|
/**
|
|
634
697
|
* 会话 id
|
|
635
698
|
*/
|
|
@@ -707,7 +770,7 @@ interface ZIM {
|
|
|
707
770
|
message: ZIMSEDMediaMessage;
|
|
708
771
|
toConversationID: string;
|
|
709
772
|
conversationType: ZIMConversationType;
|
|
710
|
-
}): Promise<IYapiPostSdkSendMediaMessageApiResponse['data']>;
|
|
773
|
+
}, options?: ZIMMediaMessageSendOptions): Promise<IYapiPostSdkSendMediaMessageApiResponse['data']>;
|
|
711
774
|
deleteAllConversationMessages(): Promise<{
|
|
712
775
|
code: string;
|
|
713
776
|
message: string;
|
|
@@ -847,10 +910,14 @@ interface IYapiPostSdkSendMessageApiRequestMessage {
|
|
|
847
910
|
*
|
|
848
911
|
*/
|
|
849
912
|
type: string
|
|
850
|
-
/**
|
|
851
|
-
* 自定义的拓展字段
|
|
852
|
-
*/
|
|
853
|
-
extendedData: string
|
|
913
|
+
/**
|
|
914
|
+
* 自定义的拓展字段
|
|
915
|
+
*/
|
|
916
|
+
extendedData: string
|
|
917
|
+
/**
|
|
918
|
+
* 客户端业务预发 ID。未传时 SDK 会自动生成。
|
|
919
|
+
*/
|
|
920
|
+
clientBizId?: string
|
|
854
921
|
}
|
|
855
922
|
declare enum SEDMessageType {
|
|
856
923
|
Unknown = '0',
|
|
@@ -911,6 +978,10 @@ interface YapiPostSdkSendMessageMessage {
|
|
|
911
978
|
* 消息表 id
|
|
912
979
|
*/
|
|
913
980
|
localMessageID: string
|
|
981
|
+
/**
|
|
982
|
+
* 预发 id
|
|
983
|
+
*/
|
|
984
|
+
clientBizId: string
|
|
914
985
|
/**
|
|
915
986
|
* 会话 id
|
|
916
987
|
*/
|
|
@@ -1148,7 +1219,7 @@ declare function sendMediaMessage(payload: {
|
|
|
1148
1219
|
message: ZIMSEDMediaMessage;
|
|
1149
1220
|
toConversationID: string;
|
|
1150
1221
|
conversationType: ZIMConversationType;
|
|
1151
|
-
}): Promise<IYapiPostSdkSendMediaMessageApiResponse['data']>;
|
|
1222
|
+
}, options?: ZIMMediaMessageSendOptions): Promise<IYapiPostSdkSendMediaMessageApiResponse['data']>;
|
|
1152
1223
|
/**
|
|
1153
1224
|
* 查询历史消息
|
|
1154
1225
|
* @param payload 查询参数
|
package/dist/index.js
CHANGED
|
@@ -4142,6 +4142,62 @@ var VanshiSdk = (() => {
|
|
|
4142
4142
|
};
|
|
4143
4143
|
|
|
4144
4144
|
// src/sdk/message.ts
|
|
4145
|
+
var clientBizIdSeq = 0;
|
|
4146
|
+
var MAX_FILE_NAME_LENGTH = 20;
|
|
4147
|
+
function generateClientBizId() {
|
|
4148
|
+
clientBizIdSeq = (clientBizIdSeq + 1) % 1e6;
|
|
4149
|
+
const timestamp = Date.now();
|
|
4150
|
+
const sequence = clientBizIdSeq.toString(36);
|
|
4151
|
+
const random = Math.random().toString(36).slice(2, 10);
|
|
4152
|
+
return `cbiz_${timestamp}_${sequence}_${random}`;
|
|
4153
|
+
}
|
|
4154
|
+
function withClientBizId(message) {
|
|
4155
|
+
return {
|
|
4156
|
+
...message,
|
|
4157
|
+
clientBizId: message.clientBizId || generateClientBizId()
|
|
4158
|
+
};
|
|
4159
|
+
}
|
|
4160
|
+
function truncateFileName(fileName) {
|
|
4161
|
+
return fileName.length > MAX_FILE_NAME_LENGTH ? fileName.slice(0, MAX_FILE_NAME_LENGTH) : fileName;
|
|
4162
|
+
}
|
|
4163
|
+
function getDisplayFileName(file, fallback = "") {
|
|
4164
|
+
return truncateFileName(file?.name || fallback || "");
|
|
4165
|
+
}
|
|
4166
|
+
function createLocalMediaMessage(message, toConversationID, conversationType) {
|
|
4167
|
+
const file = message.file;
|
|
4168
|
+
const localPreviewUrl = file && typeof URL !== "undefined" && URL.createObjectURL ? URL.createObjectURL(file) : void 0;
|
|
4169
|
+
return {
|
|
4170
|
+
massage: message.message,
|
|
4171
|
+
message: message.message,
|
|
4172
|
+
type: message.type,
|
|
4173
|
+
extendedData: message.extendedData,
|
|
4174
|
+
messageID: "",
|
|
4175
|
+
orderKey: "",
|
|
4176
|
+
clientSid: "",
|
|
4177
|
+
localMessageID: message.clientBizId,
|
|
4178
|
+
clientBizId: message.clientBizId,
|
|
4179
|
+
conversationID: toConversationID,
|
|
4180
|
+
conversationType,
|
|
4181
|
+
senderUserID: "",
|
|
4182
|
+
direction: 0,
|
|
4183
|
+
sentStatus: 0,
|
|
4184
|
+
timestamp: Date.now(),
|
|
4185
|
+
receiptStatus: 0,
|
|
4186
|
+
isServerMessage: false,
|
|
4187
|
+
isBroadcastMessage: false,
|
|
4188
|
+
isGroupTargetedMessage: "",
|
|
4189
|
+
id: "",
|
|
4190
|
+
file,
|
|
4191
|
+
fileName: getDisplayFileName(file, message.fileName),
|
|
4192
|
+
fileSize: file ? String(file.size) : message.fileSize || "",
|
|
4193
|
+
fileDownloadUrl: message.fileDownloadUrl || localPreviewUrl || "",
|
|
4194
|
+
thumbnailDownloadUrl: message.thumbnailDownloadUrl || localPreviewUrl || "",
|
|
4195
|
+
thumbnailWidth: message.thumbnailWidth || "",
|
|
4196
|
+
thumbnailHeight: message.thumbnailHeight || "",
|
|
4197
|
+
duration: message.duration || "",
|
|
4198
|
+
localPreviewUrl
|
|
4199
|
+
};
|
|
4200
|
+
}
|
|
4145
4201
|
async function getImageSize(file) {
|
|
4146
4202
|
return new Promise((resolve, reject) => {
|
|
4147
4203
|
const url2 = URL.createObjectURL(file);
|
|
@@ -4160,9 +4216,14 @@ var VanshiSdk = (() => {
|
|
|
4160
4216
|
img.src = url2;
|
|
4161
4217
|
});
|
|
4162
4218
|
}
|
|
4163
|
-
async function uploadSdkFile(file) {
|
|
4219
|
+
async function uploadSdkFile(file, fileName) {
|
|
4164
4220
|
const formData = new FormData();
|
|
4165
|
-
|
|
4221
|
+
const uploadFileName = truncateFileName(fileName || file.name || "");
|
|
4222
|
+
if (uploadFileName) {
|
|
4223
|
+
formData.append("file", file, uploadFileName);
|
|
4224
|
+
} else {
|
|
4225
|
+
formData.append("file", file);
|
|
4226
|
+
}
|
|
4166
4227
|
const response = await fetch(getUploadUrl(), {
|
|
4167
4228
|
method: "POST",
|
|
4168
4229
|
headers: {
|
|
@@ -4189,7 +4250,7 @@ var VanshiSdk = (() => {
|
|
|
4189
4250
|
}
|
|
4190
4251
|
const enrichedMessage = {
|
|
4191
4252
|
...message,
|
|
4192
|
-
fileName: file
|
|
4253
|
+
fileName: getDisplayFileName(file, message.fileName),
|
|
4193
4254
|
fileSize: String(file.size)
|
|
4194
4255
|
};
|
|
4195
4256
|
if (isSameUid(message.type, "11" /* Image */)) {
|
|
@@ -4204,9 +4265,12 @@ var VanshiSdk = (() => {
|
|
|
4204
4265
|
enrichedMessage.duration = String(res.duration);
|
|
4205
4266
|
enrichedMessage.thumbnailWidth = String(res.videoWidth);
|
|
4206
4267
|
enrichedMessage.thumbnailHeight = String(res.videoHeight);
|
|
4207
|
-
enrichedMessage.thumbnailDownloadUrl = await uploadSdkFile(
|
|
4268
|
+
enrichedMessage.thumbnailDownloadUrl = await uploadSdkFile(
|
|
4269
|
+
res.firstFrameFile,
|
|
4270
|
+
res.firstFrameFile.name
|
|
4271
|
+
);
|
|
4208
4272
|
}
|
|
4209
|
-
const fileDownloadUrl = await uploadSdkFile(file);
|
|
4273
|
+
const fileDownloadUrl = await uploadSdkFile(file, enrichedMessage.fileName);
|
|
4210
4274
|
enrichedMessage.fileDownloadUrl = fileDownloadUrl;
|
|
4211
4275
|
if (isSameUid(message.type, "11" /* Image */)) {
|
|
4212
4276
|
enrichedMessage.thumbnailDownloadUrl = fileDownloadUrl;
|
|
@@ -4215,13 +4279,29 @@ var VanshiSdk = (() => {
|
|
|
4215
4279
|
return enrichedMessage;
|
|
4216
4280
|
}
|
|
4217
4281
|
async function sendMessage(payload) {
|
|
4218
|
-
const response = await postSdkSendMessageApiRequest(
|
|
4282
|
+
const response = await postSdkSendMessageApiRequest({
|
|
4283
|
+
...payload,
|
|
4284
|
+
message: withClientBizId(payload.message)
|
|
4285
|
+
});
|
|
4219
4286
|
return unwrapSdkData(response);
|
|
4220
4287
|
}
|
|
4221
|
-
async function sendMediaMessage(payload) {
|
|
4288
|
+
async function sendMediaMessage(payload, options) {
|
|
4289
|
+
const messageWithClientBizId = withClientBizId(payload.message);
|
|
4290
|
+
if (options?.onMessageAttached) {
|
|
4291
|
+
try {
|
|
4292
|
+
options.onMessageAttached(
|
|
4293
|
+
createLocalMediaMessage(
|
|
4294
|
+
messageWithClientBizId,
|
|
4295
|
+
payload.toConversationID,
|
|
4296
|
+
payload.conversationType
|
|
4297
|
+
)
|
|
4298
|
+
);
|
|
4299
|
+
} catch {
|
|
4300
|
+
}
|
|
4301
|
+
}
|
|
4222
4302
|
const response = await postSdkSendMediaMessageApiRequest({
|
|
4223
4303
|
...payload,
|
|
4224
|
-
message: await enrichMediaMessage(
|
|
4304
|
+
message: await enrichMediaMessage(messageWithClientBizId)
|
|
4225
4305
|
});
|
|
4226
4306
|
return unwrapSdkData(response);
|
|
4227
4307
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -717,6 +717,62 @@ var postSdkDeleteAllMessageApiRequest = (data) => {
|
|
|
717
717
|
};
|
|
718
718
|
|
|
719
719
|
// src/sdk/message.ts
|
|
720
|
+
var clientBizIdSeq = 0;
|
|
721
|
+
var MAX_FILE_NAME_LENGTH = 20;
|
|
722
|
+
function generateClientBizId() {
|
|
723
|
+
clientBizIdSeq = (clientBizIdSeq + 1) % 1e6;
|
|
724
|
+
const timestamp = Date.now();
|
|
725
|
+
const sequence = clientBizIdSeq.toString(36);
|
|
726
|
+
const random = Math.random().toString(36).slice(2, 10);
|
|
727
|
+
return `cbiz_${timestamp}_${sequence}_${random}`;
|
|
728
|
+
}
|
|
729
|
+
function withClientBizId(message) {
|
|
730
|
+
return {
|
|
731
|
+
...message,
|
|
732
|
+
clientBizId: message.clientBizId || generateClientBizId()
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
function truncateFileName(fileName) {
|
|
736
|
+
return fileName.length > MAX_FILE_NAME_LENGTH ? fileName.slice(0, MAX_FILE_NAME_LENGTH) : fileName;
|
|
737
|
+
}
|
|
738
|
+
function getDisplayFileName(file, fallback = "") {
|
|
739
|
+
return truncateFileName(file?.name || fallback || "");
|
|
740
|
+
}
|
|
741
|
+
function createLocalMediaMessage(message, toConversationID, conversationType) {
|
|
742
|
+
const file = message.file;
|
|
743
|
+
const localPreviewUrl = file && typeof URL !== "undefined" && URL.createObjectURL ? URL.createObjectURL(file) : void 0;
|
|
744
|
+
return {
|
|
745
|
+
massage: message.message,
|
|
746
|
+
message: message.message,
|
|
747
|
+
type: message.type,
|
|
748
|
+
extendedData: message.extendedData,
|
|
749
|
+
messageID: "",
|
|
750
|
+
orderKey: "",
|
|
751
|
+
clientSid: "",
|
|
752
|
+
localMessageID: message.clientBizId,
|
|
753
|
+
clientBizId: message.clientBizId,
|
|
754
|
+
conversationID: toConversationID,
|
|
755
|
+
conversationType,
|
|
756
|
+
senderUserID: "",
|
|
757
|
+
direction: 0,
|
|
758
|
+
sentStatus: 0,
|
|
759
|
+
timestamp: Date.now(),
|
|
760
|
+
receiptStatus: 0,
|
|
761
|
+
isServerMessage: false,
|
|
762
|
+
isBroadcastMessage: false,
|
|
763
|
+
isGroupTargetedMessage: "",
|
|
764
|
+
id: "",
|
|
765
|
+
file,
|
|
766
|
+
fileName: getDisplayFileName(file, message.fileName),
|
|
767
|
+
fileSize: file ? String(file.size) : message.fileSize || "",
|
|
768
|
+
fileDownloadUrl: message.fileDownloadUrl || localPreviewUrl || "",
|
|
769
|
+
thumbnailDownloadUrl: message.thumbnailDownloadUrl || localPreviewUrl || "",
|
|
770
|
+
thumbnailWidth: message.thumbnailWidth || "",
|
|
771
|
+
thumbnailHeight: message.thumbnailHeight || "",
|
|
772
|
+
duration: message.duration || "",
|
|
773
|
+
localPreviewUrl
|
|
774
|
+
};
|
|
775
|
+
}
|
|
720
776
|
async function getImageSize(file) {
|
|
721
777
|
return new Promise((resolve, reject) => {
|
|
722
778
|
const url = URL.createObjectURL(file);
|
|
@@ -735,9 +791,14 @@ async function getImageSize(file) {
|
|
|
735
791
|
img.src = url;
|
|
736
792
|
});
|
|
737
793
|
}
|
|
738
|
-
async function uploadSdkFile(file) {
|
|
794
|
+
async function uploadSdkFile(file, fileName) {
|
|
739
795
|
const formData = new FormData();
|
|
740
|
-
|
|
796
|
+
const uploadFileName = truncateFileName(fileName || file.name || "");
|
|
797
|
+
if (uploadFileName) {
|
|
798
|
+
formData.append("file", file, uploadFileName);
|
|
799
|
+
} else {
|
|
800
|
+
formData.append("file", file);
|
|
801
|
+
}
|
|
741
802
|
const response = await fetch(getUploadUrl(), {
|
|
742
803
|
method: "POST",
|
|
743
804
|
headers: {
|
|
@@ -764,7 +825,7 @@ async function enrichMediaMessage(message) {
|
|
|
764
825
|
}
|
|
765
826
|
const enrichedMessage = {
|
|
766
827
|
...message,
|
|
767
|
-
fileName: file
|
|
828
|
+
fileName: getDisplayFileName(file, message.fileName),
|
|
768
829
|
fileSize: String(file.size)
|
|
769
830
|
};
|
|
770
831
|
if (isSameUid(message.type, "11" /* Image */)) {
|
|
@@ -779,9 +840,12 @@ async function enrichMediaMessage(message) {
|
|
|
779
840
|
enrichedMessage.duration = String(res.duration);
|
|
780
841
|
enrichedMessage.thumbnailWidth = String(res.videoWidth);
|
|
781
842
|
enrichedMessage.thumbnailHeight = String(res.videoHeight);
|
|
782
|
-
enrichedMessage.thumbnailDownloadUrl = await uploadSdkFile(
|
|
843
|
+
enrichedMessage.thumbnailDownloadUrl = await uploadSdkFile(
|
|
844
|
+
res.firstFrameFile,
|
|
845
|
+
res.firstFrameFile.name
|
|
846
|
+
);
|
|
783
847
|
}
|
|
784
|
-
const fileDownloadUrl = await uploadSdkFile(file);
|
|
848
|
+
const fileDownloadUrl = await uploadSdkFile(file, enrichedMessage.fileName);
|
|
785
849
|
enrichedMessage.fileDownloadUrl = fileDownloadUrl;
|
|
786
850
|
if (isSameUid(message.type, "11" /* Image */)) {
|
|
787
851
|
enrichedMessage.thumbnailDownloadUrl = fileDownloadUrl;
|
|
@@ -790,13 +854,29 @@ async function enrichMediaMessage(message) {
|
|
|
790
854
|
return enrichedMessage;
|
|
791
855
|
}
|
|
792
856
|
async function sendMessage(payload) {
|
|
793
|
-
const response = await postSdkSendMessageApiRequest(
|
|
857
|
+
const response = await postSdkSendMessageApiRequest({
|
|
858
|
+
...payload,
|
|
859
|
+
message: withClientBizId(payload.message)
|
|
860
|
+
});
|
|
794
861
|
return unwrapSdkData(response);
|
|
795
862
|
}
|
|
796
|
-
async function sendMediaMessage(payload) {
|
|
863
|
+
async function sendMediaMessage(payload, options) {
|
|
864
|
+
const messageWithClientBizId = withClientBizId(payload.message);
|
|
865
|
+
if (options?.onMessageAttached) {
|
|
866
|
+
try {
|
|
867
|
+
options.onMessageAttached(
|
|
868
|
+
createLocalMediaMessage(
|
|
869
|
+
messageWithClientBizId,
|
|
870
|
+
payload.toConversationID,
|
|
871
|
+
payload.conversationType
|
|
872
|
+
)
|
|
873
|
+
);
|
|
874
|
+
} catch {
|
|
875
|
+
}
|
|
876
|
+
}
|
|
797
877
|
const response = await postSdkSendMediaMessageApiRequest({
|
|
798
878
|
...payload,
|
|
799
|
-
message: await enrichMediaMessage(
|
|
879
|
+
message: await enrichMediaMessage(messageWithClientBizId)
|
|
800
880
|
});
|
|
801
881
|
return unwrapSdkData(response);
|
|
802
882
|
}
|