bm-aichat-core
v0.1.5
Published
聊天插件核心
Downloads
14
Readme
聊天插件核心
本插件提供业务底层 websocket 流程
一.安装
npm i bm-aichat-core -S
or
yarn add bm-aichat-core
二.使用方法
1.初始化
import AiChat from 'bm-aichat-core'
const aiChat = new AiChat(
{
getAuthReqParams: () => {},
getHeartBeatParams: () => {},
getAichatReqParams: (params) => {},
},
framework,
)
getAuthReqParams
获取鉴权请求协议的方法
参数:/
返回值:Object 鉴权请求协议
示例:
getAuthReqParams: () => { return { method: 'authReq', param1: 'xxx', param2: 'xxx', } }
若不传递该方法,在建立 websocket 连接后,不会发送鉴权请求
getHeartBeatParams
获取心跳消息协议的方法
参数:/
返回值:Object 心跳消息协议
示例:
getHeartBeatParams: () => { return { method: 'heartbeat', param1: 'xxx', param2: 'xxx', } }
若不传递该方法,在建立 websocket 连接后,不会开启心跳消息定时器
返回的 Object 中可配置一些特殊含义的字段:
getHeartBeatParams: () => { return { ..., _interval:10, // 发送心跳消息间隔时间(单位秒,默认10s) _monitor:30, // 监听心跳消息时间(单位秒,默认30s,即30s未收到心跳返回就断连重连) } }
这些字段不会作为心跳消息协议发送给服务端
getAichatReqParams
获取聊天消息协议的方法
参数:Object
返回值:Object | Array 聊天消息协议
示例:
getAichatReqParams: (params) => { return { method: 'aichatReq', a: params.a, b: params.b, } }
该方法的
params
参数,取自sendMessage
:aiChat.sendMessage({ a: 'xxx', b: 'xxx' })
该方法也可以返回一个数组,若返回数组,则在
sendMessage
时会发送多条消息:getAichatReqParams: (params) => { return params.map((item) => ({ method: 'aichatReq', a: item.a, b: item.b, })) }
aiChat.sendMessage([ { a: 'a1', b: 'b1' }, { a: 'a2', b: 'b2' }, ]) /* * 会连续发送两条消息: * { method: 'aichatReq', a:'a1', b:'b1' } * { method: 'aichatReq', a:'a2', b:'b2' } * 等价于 上文中返回Object形式 时: * aiChat.sendMessage({a:'a1',b:'b1'}) * aiChat.sendMessage({a:'a2',b:'b2'}) */
若不传递该方法,
sendMessage
方法无用framework
框架实例对象(非必填)
详见 框架兼容
2.创建连接 createConnect(url)
创建 websocket 连接
aiChat.createConnect('ws:xxxxxx')
插件内会暂存创建连接时的url
,若后续出现断连后需手动重连的情况(如:互踢),同时url
未发生改变,可以直接调用createConnect()
3.关闭连接 closeConnect()
关闭 websocket 连接
aiChat.closeConnect()
4.发送消息 sendMessage(params)
发送聊天消息
aiChat.sendMessage(params)
该方法的参数是由getAichatReqParams
决定的,params
会经过getAichatReqParams
的处理,最终转换成符合业务场景的聊天消息协议
该方法返回Promise
5.设置是否自动重连 setReconnect(value)
设置是否自动重连
aiChat.setReconnect(true)
// or
aiChat.setReconnect(false)
插件内部默认断连后自动重连,除非手动关闭连接closeConnect
但一些时候我们并不想如此,比如当浏览器处于后台时,由于浏览器的休眠机制,心跳消息可能无法正确发送,从而触发30s未收到心跳消息
机制断开连接,又因为自动重连,故而会出现反复创建连接的情况
通过该方法就可以控制自动重连:
document.addEventListener('visibilitychange', handleVisibilitychange)
function handleVisibilitychange() {
if (!document.hidden) {
aiChat.setReconnect(true)
if (断开连接) {
aiChat.createConnect()
}
} else {
aiChat.setReconnect(false)
}
}
6.开启聊天消息监听 onMessage(callback)
开启聊天消息监听
aiChat.onMessage((msg) => {})
其中msg
是服务端返回的aiChatResp
或aichatResp
消息
若想关闭监听,再次调用方法且不传回调函数即可:
aiChat.onMessage()
7.开启异常情况监听 onError(callback)
开启异常情况监听
aiChat.onError(({ type, data }) => {})
这并不是
SocketTask
的onError
或者其他错误,而是插件内自定义的业务流程上的异常情况
回调函数的参数对象中有两个属性:type
和data
,data
为触发该错误的原消息,type
类型如下:
| type | code 码 | 描述 | 是否会断开连接 |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | -------------- |
| maintain | 1002 | 服务器正在维护 | 是 |
| gatewayError | 1004 - token 过期(appServ)7001 - token 过期7002 - 当前用户访问接口没有权限7003 - 非法 token7005 - 业务网关异常 | 网关错误 | 是 |
| wordFilter | 8001 | 触发敏感词 | 否 |
| loginError | !0 | 登录错误(鉴权结果 code 码不为 0) | 是 |
| kickoff | / | 触发互踢 | 是 |
| acknowledgeError | !0 | 消息异常(acknowledge
消息 code 码不为 0) | 否 |
**注意:**当触发部分异常时,插件在向外抛出的同时会断开连接(不会自动重连),是否需要重连由宿主项目自行决定
若想关闭监听,再次调用方法且不传回调函数即可:
aiChat.onError()
8.开启连接状态改变监听 onStatusChange(callback)
开启连接状态改变监听
aiChat.onStatusChange(({ websocketIsOk, authIsOk }) => {})
注意:
- 当 websocket 成功建立连接,
websocketIsOk
会置为true
- 在初始化时,如果传递了
getAuthReqParams
方法,默认认为开启鉴权(登录)流程,在接收到鉴权结果并且code === 0
后authIsOk
会置为true
- 在 websocket 断开连接时,
websocketIsOk
和authIsOk
都会置为false
websocketIsOk
和authIsOk
两者任意一方改变,都会触发该回调函数
若想关闭监听,再次调用方法且不传回调函数即可:
aiChat.onStatusChange()
9.未知消息类型监听 onUnknownMethod(callback)
未知消息类型监听
aiChat.onUnknownMethod((msg) => {})
插件内根据业务流程,已处理以下method
:
- loginResp/authResp
- heartbeat
- kickoff/kickoffResp
- aiChatResp/aichatResp
- acknowledge
- servExceptionNotice
如遇未知消息类型,可以通过该监听方法自行处理
若想关闭监听,再次调用方法且不传回调函数即可:
aiChat.onUnknownMethod()
三.使用模板
以 react 为例:
import React, { useState, useEffect, useCallback } from 'react'
import AiChat from 'bm-aichat-core'
const mockUserInfo = {
uid: 'xxx',
token: 'xxx',
}
let isConnected = false
let isKickOff = false
const aiChat = new AiChat({
getAuthReqParams: () => ({
method: 'authReq',
uid: mockUserInfo.uid,
token: mockUserInfo.token,
}),
getHeartBeatParams: () => ({
method: 'heartbeat',
uid: mockUserInfo.uid,
}),
getAichatReqParams: (params) => ({
method: 'aichatReq',
uid: mockUserInfo.uid,
contents: params.contents,
}),
})
export default () => {
const [value, setValue] = useState('')
useEffect(() => {
aiChat.createConnect('ws:xxxxxx')
aiChat.onStatusChange(({ websocketIsOk }) => {
isConnected = websocketIsOk
})
aiChat.onMessage((msg) => {
console.log(msg)
// 处理聊天消息
})
aiChat.onError((err) => {
console.log(err)
// 处理异常情况
switch (err.type) {
case 'kickoff': {
isKickOff = true
}
default: {
break
}
}
})
document.addEventListener('visibilitychange', handleVisibilitychange)
return () => {
document.removeEventListener('visibilitychange', handleVisibilitychange)
aiChat.closeConnect()
}
}, [])
const handleVisibilitychange = useCallback(() => {
if (!document.hidden) {
aiChat.setReconnect(true)
if (!isConnected && !isKickOff) {
aiChat.createConnect()
}
} else {
aiChat.setReconnect(false)
}
}, [])
const send = () => {
aiChat.sendMessage({
contents: value,
})
setValue('')
}
return (
<div>
<input
value={value}
onChange={(e) => {
setValue(e.target.value)
}}
></input>
<button onClick={send}>发送</button>
</div>
)
}
相比于直接使用,本插件更推荐根据业务进行扩展封装:
import AiChat from 'bm-aichat-core';
export default class Example extends AiChat {
constructor(opts, framework = null) {
super({...}, framework)
}
}
详见 bm-aichat-mts 或 bm-aichat-platform
四.框架兼容
本插件兼容部分跨端框架(如Taro
、uni-app
)以及微信小程序
有此需求时请在初始化时传递框架实例对象,如微信小程序请传递wx
:
import AiChat from 'bm-aichat-core'
const aiChat = new AiChat({...}, wx)
传递后将使用该对象身上的 API,否则默认使用原生 API
如果是本插件的扩展封装,同时也需要多端兼容,可对使用到的 API 单独处理,或者直接利用本插件的兼容:
import { Framework } from 'bm-aichat-core'
// 目前支持的API如下:
Framework.connectSocket()
Framework.getStorageSync()
Framework.setStorageSync()
Framework.request()
这样做的好处是扩展封装时不需要考虑 API 的兼容问题,直接按照标准方式使用即可
缺点是当有更多 API 需要做兼容时,就需要更新插件
后续可能会优化