npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

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是服务端返回的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 - 非法 token7005 - 业务网关异常 | 网关错误 | 是 | | 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 需要做兼容时,就需要更新插件

后续可能会优化