bm-aichat-core

0.1.5 • Public • Published

聊天插件核心

本插件提供业务底层 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是服务端返回的aiChatRespaichatResp消息

若想关闭监听,再次调用方法且不传回调函数即可:

aiChat.onMessage()

7.开启异常情况监听 onError(callback)

开启异常情况监听

aiChat.onError(({ type, data }) => {})

这并不是SocketTaskonError或者其他错误,而是插件内自定义的业务流程上的异常情况

回调函数的参数对象中有两个属性:typedatadata为触发该错误的原消息,type类型如下:

type code 码 描述 是否会断开连接
maintain 1002 服务器正在维护
gatewayError 1004 - token 过期(appServ)
7001 - token 过期
7002 - 当前用户访问接口没有权限
7003 - 非法 token
7005 - 业务网关异常
网关错误
wordFilter 8001 触发敏感词
loginError !0 登录错误(鉴权结果 code 码不为 0)
kickoff / 触发互踢
acknowledgeError !0 消息异常(acknowledge消息 code 码不为 0)

**注意:**当触发部分异常时,插件在向外抛出的同时会断开连接(不会自动重连),是否需要重连由宿主项目自行决定

若想关闭监听,再次调用方法且不传回调函数即可:

aiChat.onError()

8.开启连接状态改变监听 onStatusChange(callback)

开启连接状态改变监听

aiChat.onStatusChange(({ websocketIsOk, authIsOk }) => {})

注意:

  1. 当 websocket 成功建立连接,websocketIsOk会置为true
  2. 在初始化时,如果传递了getAuthReqParams方法,默认认为开启鉴权(登录)流程,在接收到鉴权结果并且code === 0authIsOk会置为true
  3. 在 websocket 断开连接时,websocketIsOkauthIsOk都会置为false
  4. websocketIsOkauthIsOk两者任意一方改变,都会触发该回调函数

若想关闭监听,再次调用方法且不传回调函数即可:

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-mtsbm-aichat-platform

扩展分层


四.框架兼容

本插件兼容部分跨端框架(如Tarouni-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 需要做兼容时,就需要更新插件

后续可能会优化


Readme

Keywords

none

Package Sidebar

Install

npm i bm-aichat-core

Weekly Downloads

5

Version

0.1.5

License

ISC

Unpacked Size

62.3 kB

Total Files

14

Last publish

Collaborators

  • lonelytaker