2023-05-21 17:37:50 +08:00

626 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Javascript
order: 300
---
# Javascript
## 介绍
### 设计理念
像设计书的目录一样设计 api, 通过 WKSDK.shared().xxxManager 我们可以访问到所有需要的功能,例如发送消息 `WKSDK.shared().chatManager.send(xxx)`
### 结构说明
![sdk结构图](./wksdk.png)
```ts
// 聊天管理者
// 负责消息相关的增删改查操作 比如发送消息,删除消息,撤回消息,聊天消息的监听等等
WKSDK.shared().chatManager;
// 连接管理者
// 负责与IM建立连接或断开连接 监听IM连接状态等等
WKSDK.shared().connectionManager;
// 频道管理者
// 负责频道数据的获取和缓存和一些频道的设置,比如置顶,免打扰,禁言等等
WKSDK.shared().channelManager;
// 最近会话管理者
// 负责维护最近会话的相关数据,比如未读数量,草稿,@我,最后一条消息等等
WKSDK.shared().conversationManager;
// 提醒管理者
// 负责最近会话的提醒事项维护
WKSDK.shared().reminderManager;
```
## 集成
### npm 或 yarn 引入
```
$ npm i wukongimjssdk
```
或者
```
$ yarn add wukongimjssdk
```
### 引入
import { WKSDK } from "wukongimjssdk/lib/sdk"
### 初始化
```ts
// 集群模式通过此方法获取连接地址
// WKSDK.shared().config.provider.connectAddrCallback = async (callback: ConnectAddrCallback) => {
// const addr = await xxxx // addr 格式为 ip:port
// callback(addr)
// }
// 单机模式可以直接设置地址
WKSDK.shared().config.addr = 'IP:PORT'; // 默认端口为2122
// 认证信息
WKSDK.shared().config.uid = 'xxxx'; // 用户uid需要在悟空通讯端注册过
WKSDK.shared().config.token = 'xxxx'; // 用户token (需要在悟空通讯端注册过)
// 更多配置,查看:
WKSDK.shared().config;
```
## 连接与断开
```ts
// 连接
WKSDK.shared().connectManager.connect();
// 断开
WKSDK.shared().connectManager.disconnect();
// 连接状态监听
WKSDK.shared().connectManager.addConnectStatusListener(
(status: ConnectStatus, reasonCode?: number) => {
if (status === ConnectStatus.Connected) {
console.log('连接成功');
} else {
console.log('连接失败', reasonCode); // reasonCode: 2表示认证失败uid或token错误
}
},
);
```
## 在线消息收发
#### 数据操作
```ts
/**
* 发送消息
* @param content 消息内容
* @param channel 频道对象 个人频道,群频道
* @param setting 发送设置 比如:已读未读回执,端对端加密
* @returns 完整消息对象
*/
WKSDK.shared().chatManager.send(content: MessageContent, channel: Channel, setting?: Setting)
// 例如发送文本消息hello给用户A
const text = new MessageText("hello")
WKSDK.shared().chatManager.send(text,new Channel("A",ChannelTypePerson))
```
#### 数据监听
```ts
// 消息发送状态监听
WKSDK.shared().chatManager.addMessageStatusListener((packet: SendackPacket) => {
console.log('消息clientSeq->', packet.clientSeq); // 客户端序号用来匹配对应的发送的消息
if (packet.reasonCode === 1) {
// 发送成功
} else {
// 发送失败
}
});
// 消息监听
WKSDK.shared().chatManager.addMessageListener((message: Message) => {});
```
## 离线消息接收
在**悟空 IM**中为了应付海量离线消息,采用了按需拉取的机制,比如 10 个会话一个会话 10 万条消息,\**悟空 IM 不会把这个 10*10 万=100 万条消息都拉取到本地。 而是采用拉取这 10 个会话的信息和对应的最新 20 条消息,也就是实际只拉取了 200 条消息 相对 100 万条消息来说大大提高了离线拉取速度。用户点进对应的会话才会去按需拉取这个会话的消息。 这些机制 SDK 内部都已做好了封装,使用者其实不需要关心。使用者只需要关心最近会话的变化
#### 数据源设置
```ts
// 提供最近会话同步的数据源
WKSDK.shared().config.provider.syncConversationsCallback = async (): Promise<Array<Conversation>> => {
// 后端提供的获取最近会话列表的接口数据 然后构建成 Conversation对象数组返回
let conversations = new Array<Conversation>();
conversations = await request(...)
return conversations
})
// 提供频道内消息同步的数据源
WKSDK.shared().config.provider.syncMessagesCallback = async(channel:Channel,opts:SyncOptions):Promise<Array<Message>>=> {
// 后端提供的获取某个频道的消息列表的接口数据然后构建成Message对象数组返回
let messages = new Array<Message>();
messages = await request(...)
return messages
}
```
##### 数据操作
```ts
// 同步最近会话会触发WKSDK.shared().config.provider.syncConversationsCallback
const conversations = await WKSDK.shared().conversationManager.sync({});
// 同步频道的消息会触发WKSDK.shared().config.provider.syncMessagesCallback
const messages = WKSDK.shared().chatManager.syncMessages(channel, opts);
```
##### 数据监听
```ts
// 监听最近会话数据
WKSDK.shared().conversationManager.addConversationListener(
(conversation: Conversation, action: ConversationAction) => {
// conversation发送数据变化的最近会话对象
// action变化行为 add添加 update更新
},
);
```
## 文本消息
```ts
class MessageText extends MessageContent {
text?: string;
}
```
## 图片消息
```ts
class ImageContent extends MediaMessageContent {
width!: number; // 图片宽度
height!: number; // 图片高度
url!: string; // 图片远程地址
constructor(file?: File, width?: number, height?: number) {
super();
this.file = file;
this.width = width || 0;
this.height = height || 0;
}
}
```
## 语音消息
web 语音消息暂只支持收不支持发送
```ts
class VoiceContent extends MediaMessageContent {
url!: string; // 语音文件下载地址
timeTrad!: number; // 语音秒长
waveform!: string; // 语音波纹 base64编码
}
```
## 小视频消息
web 语小视频消息暂只支持收不支持发送
```ts
class VideoContent extends MessageContent {
url!: string; // 小视频下载地址
cover!: string; // 小视频封面图片下载地址
size: number = 0; // 小视频大小 单位byte
width!: number; // 小视频宽度
height!: number; // 小视频高度
second!: number; // 小视频秒长
}
```
## 名片消息
```ts
class Card extends MessageContent {
name!: string; // 好友名字
uid!: string; // 好友uid
vercode!: string; // 好友验证码
}
```
## 位置消息
```ts
class LocationContent extends MessageContent {
lng: number = 0; // 纬度
lat: number = 0; // 经度
title!: string; // 位置标题
address!: string; // 具体地址
img!: string; // 封面图远程地址
}
```
## CMD 消息
cmd 消息由服务端下发客户端解析执行。
```ts
class CMDContent extends MessageContent {
cmd!: string; // cmd的指令
param: any; // 指令对应的参数
}
```
## 最近会话
最近会话用于表示会话列表页的数据模型。当用户发送,收取及删除消息时,都会同时去修改最近会话。
当收到或者一条消息时,会自动生成这个消息对应的最近会话。但值得注意的是最近会话和会话并不是一一对应的关系,删除最近会话并不会影响会话
最近会话主要属性
```ts
class Conversation {
channel!: Channel; // 频道
unread!: number; // 未读消息
lastMessage?: Message; // 最后一条消息
extra?: any // 扩展数据(用户自定义的数据)
remoteExtra!: ConversationExtra // 远程扩展数据
reminders = new Array<Reminder>() // 提醒项
...
}
```
## 频道管理(置顶,免打扰等等)
[什么是频道](/guide/initialize#频道)
频道是**悟空 IM**里比较重要的一个抽象概念,发送消息都是先发送给频道,频道根据自己的配置规则进行投递消息,频道分频道和频道详情
频道的属性
```ts
// 频道
class Channel {
channelID!: string; // 频道ID 个人频道为用户uid群频道为群ID
channelType!: number; // 频道类型 1.个人频道 2.群聊频道
}
```
```ts
// 频道详情
class ChannelInfo {
channel!: Channel; // 频道
title!: string; // 频道标题
logo!: string; // 频道logo
mute!: boolean; // 是否免打扰
top!: boolean; // 是否置顶
online: boolean = false; // 是否在线
lastOffline: number = 0; // 最后一次离线时间
orgData: any; // 频道原始数据由第三方自定义
}
```
#### 数据源设置
```ts
// 频道详情获取数据源提供
WKSDK.shared().config.provider.channelInfoCallback = async function (channel: Channel): Promise<ChannelInfo> {
let channelInfo:ChannelInfo
channelInfo = await request(...)
return channelInfo
}
// 订阅者获取数据源提供者
WKSDK.shared().config.provider.syncSubscribersCallback = async function (channel: Channel, version: number): Promise<Array<Subscriber>> {
let subscribers:Subscriber[]
subscribers = await request(...)
return subscribers
}
```
#### 数据操作
```ts
// 获取频道详情(不会触发数据源的远程获取)
const channelInfo = WKSDK.shared().channelManager.getChannelInfo(channel);
// 获取频道的订阅者(不会触发数据源的远程获取)
const subscribes = WKSDK.shared().channelManager.getSubscribes(channel);
// 从远程提取频道详情(会触发数据源的远程获取和频道信息监听,是异步过程)
WKSDK.shared().channelManager.fetchChannelInfo(channel);
// 从远程提取频道列表(会触发数据源的远程获取和订阅者的监听,是异步过程)
WKSDK.shared().channelManager.syncSubscribes(channel);
```
#### 数据监听
```ts
// 监听频道详情
WKSDK.shared().channelManager.addListener((channelInfo: ChannelInfo) => {});
```
## 进阶使用
### 消息附件上传设置
所有带附件的消息的上传都会通过此任务上传
伪代码如下:
```ts
// 实现四个方法 initWithMessage resume cancel suspend
class MediaMessageUploadTask extends MessageTask {
- (instancetype)initWithMessage:(WKMessage *)message {
self = [super initWithMessage:message];
if(self) {
[self initTask];
}
return self;
}
async start(): Promise<void> {
const mediaContent = this.message.content as MediaMessageContent
const param = new FormData();
param.append("file", mediaContent.file);
const resp = await axios.post(uploadURL,param,{
headers: { "Content-Type": "multipart/form-data" },
cancelToken: new axios.CancelToken((c: Canceler) => {
this.canceler = c
}),
onUploadProgress: e => {
// 更新任务进度
var completeProgress = ((e.loaded / e.total) | 0);
this._progress = completeProgress
this.update()
}
}).catch(error => {
// 更新任务错误
console.log('文件上传失败!->', error);
this.status = TaskStatus.fail
this.update()
})
if(resp) {
if(resp.data.path) {
// 更新任务成功
const mediaContent = this.message.content as MediaMessageContent
mediaContent.remoteUrl = resp.data.path
this.status = TaskStatus.success
this.update()
}
}
}
// 任务取消
cancel(): void {
this.status = TaskStatus.cancel
if(this.canceler) {
this.canceler()
}
this.update()
}
// 任务进度
progress(): number {
return this._progress??0
}
@end
```
注册上传任务
```ts
WKSDK.shared().config.provider.messageUploadTaskCallback = (
message: Message,
): MessageTask => {
return new MediaMessageUploadTask(message);
};
```
### 自定义普通消息
我们以自定义一个 gif 消息为例。
#### 第一步继承 MessageContent 和定义 gif 消息的正文结构
```ts
class GifContent extends MessageContent {
width!: number; // gif宽度
height!: number; // gif高度
url!: string; // gif远程下载地址
}
```
#### 第二步 编码解码
```ts
最终传递的消息内容为 {"type":3,"url":"xxxx","width":xxx,"height":xxx}
class GifContent extends MessageContent {
width!: number // gif宽度
height!: number // gif高度
url!: string // gif远程下载地址
// 解码
decodeJSON(content: any) {
this.width = content["width"] || 0
this.height = content["height"] || 0
this.url = content["url"]
}
// 编码
encodeJSON() {
return { "width": this.width, "height": this.height, "url": this.url }
}
}
```
#### 第三步 注册
```ts
WKSDK.shared().register(MessageContentTypeConst.gif, () => new GifContent()); // gif动图
```
### 自定义附件消息
自定义附件消息的流程与普通消息差异不大,我们以图片消息为例
#### 第一步继承 MediaMessageContent
注意这里是继承 MediaMessageContent 不是 MessageContent当发送附件消息的时候sdk 会调用[上传任务](/web/onlysdk.html#消息附件上传设置),将本地的文件上传到服务器,然后再进行消息的编码和发送
最终传递的消息内容为 {"type":4,"url":"xxxx","width":xxx,"height":xxx}
```ts
class ImageContent : MediaMessageContent {
width!: number // gif宽度
height!: number // gif高度
url!: string // gif远程下载地址
}
```
#### 第二步编码解码
```ts
class ImageContent : MediaMessageContent {
width!: number // gif宽度
height!: number // gif高度
url!: string // gif远程下载地址
constructor(file?: File,width?:number,height?:number) {
super()
this.file = file // File为要上传的图片文件对象
this.width = width || 0
this.height = height || 0
}
// 附件file上传成功后会得到 this.remoteUrl这个远程下载地址这时可以将此地址编码到消息内
encodeJSON() {
return { "width": this.width || 0, "height": this.height || 0, "url": this.remoteUrl || "" }
}
// 解码消息
decodeJSON(content: any) {
this.width = content["width"] || 0
this.height = content["height"] || 0
this.url = content["url"] || ''
}
}
```
#### 第三步 注册
```ts
WKSDK.shared().register(
MessageContentTypeConst.image,
() => new ImageContent(),
);
```
### 消息扩展
待开发
### 消息编辑
待开发
### 消息回应(点赞)
待开发
### 已读未读管理
待开发
### 端对端加密
web 不支持端对端加密
### 会话提醒管理
会话提醒目前只支持服务端下发指令,客户端同步提醒然后显示提醒,会话提醒由 WKSDK.shared().reminderManager 管理
```ts
class Reminder {
channel!: Channel;
reminderID!: number; // 提醒ID
messageID!: string;
messageSeq!: number;
reminderType!: ReminderType; // 提醒类型
text?: string; // 文本提示
data?: any; // 提醒包含的自定义数据
isLocate: boolean = false; // 是否需要进行消息定位
version: number = 0; // 数据版本
done: boolean = false; // 用户是否完成提醒
}
```
###### 数据源设置
```ts
// 提供提醒项列表的数据源设置
WKSDK.shared().config.provider.syncRemindersCallback = async (version:number):Reminder[] => {
let reminders:Reminder[]
reminders = request(...)
return reminders
}
// 提供提醒项完成的数据源
WKSDK.shared().config.provider.reminderDoneCallback = async (ids: number[]) => {
request(...)
}
```
###### 数据操作
```ts
// 同步提醒
WKSDK.shared().reminderManager.sync();
// 提醒项已处理完成
WKSDK.shared().reminderManager.done(ids);
```
###### 数据监听
```ts
// 提醒项的更新会触发对应的最近会话的更新
WKSDK.shared().conversationManager.addConversationListener(
(conversation: Conversation, action: ConversationAction) => {
console.log('conversation.reminders-->', conversation.reminders);
},
);
```