关于Electron&Vue3中集成讯飞星火AI

前言:我的最终目的是为了在QQ上集成一个AI机器人,因此在这里先实现一个简单的集成
先上效果图
关于Electron&Vue3中集成讯飞星火AI_第1张图片
总体还是很简单的,我在调用websock获取回复内容的基础上另外集成了一个事件总线,让我们在调用获取消息的时候能够更加方便快捷
工具代码如下:

import CryptoJS from 'crypto-js'

export function getWebsocketUrl(API_KEY: string, API_SECRET: string) {
  return new Promise((resolve, reject) => {
    var apiKey = API_KEY
    var apiSecret = API_SECRET
    var url = 'wss://spark-api.xf-yun.com/v1.1/chat'
    var host = location.host
    var date = new Date().toGMTString()
    var algorithm = 'hmac-sha256'
    var headers = 'host date request-line'
    var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v1.1/chat HTTP/1.1`
    var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret)
    var signature = CryptoJS.enc.Base64.stringify(signatureSha)
    var authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`
    var authorization = btoa(authorizationOrigin)
    url = `${url}?authorization=${authorization}&date=${date}&host=${host}`
    resolve(url)
  })
}

export default class TTSRecorder {
  appId: string;
  apiKey: string;
  apiSecret: string;
  status: string;
  onWillStatusChange: any;
  ttsWS: any;
  content: string;
  revertText: string;
  _events: any[];
  constructor(appId: string, API_KEY: string, API_SECRET: string) {
    this.appId = appId
    this.apiKey = API_KEY
    this.apiSecret = API_SECRET
    this._events = [];
    this.status = 'init'
  }

  // 修改状态
  setStatus(status: string) {
    this.onWillStatusChange && this.onWillStatusChange(this.status, status)
    this.status = status
  }

  // 连接websocket
  connectWebSocket() {
    this.setStatus('ttsing')
    return getWebsocketUrl(this.apiKey, this.apiSecret).then(url => {
      let ttsWS
      if ('WebSocket' in window) {
        ttsWS = new WebSocket(url as string)
      } else if ('MozWebSocket' in window) {
        ttsWS = new MozWebSocket(url)
      } else {
        alert('浏览器不支持WebSocket')
        return
      }
      this.ttsWS = ttsWS
      ttsWS.onopen = e => {
        this.webSocketSend()
      }
      ttsWS.onmessage = e => {
        this.result(e.data)
      }
      ttsWS.onerror = e => {
        clearTimeout(this.playTimeout)
        this.setStatus('error')
        alert('WebSocket报错,请f12查看详情')
        console.error(`详情查看:${encodeURI(url.replace('wss:', 'https:'))}`)
      }
      ttsWS.onclose = e => {
        console.log(e)
      }
    })
  }

  // websocket发送数据
  webSocketSend() {
    var params = {
      "header": {
        "app_id": this.appId,
        "uid": "fd3f47e4-d"
      },
      "parameter": {
        "chat": {
          "domain": "general",
          "temperature": 0.5,
          "max_tokens": 1024
        }
      },
      "payload": {
        "message": {
          "text": [
            {
              "role": "user",
              "content": this.content
            }
          ]
        }
      }
    }
    console.log(JSON.stringify(params))
    this.ttsWS.send(JSON.stringify(params))
  }

  start(text: string) {
    this.revertText = ""; // 请空回答历史
    this.content = text
    this.connectWebSocket()
  }

  // websocket接收数据的处理
  result(resultData: string) {
    let jsonData = JSON.parse(resultData)

    // 提问失败
    if (jsonData.header.code !== 0) {
      const data = {
        code: jsonData.header.code,
        content: jsonData.header.message
      }
      this.emit('error', data)
      return
    }
    if (jsonData.header.code === 0 && jsonData.header.status === 2) {
      this.ttsWS.close()
      this.setStatus("init")
      this.emit('message', {
        content: this.revertText,
        code: 0
      })
      this.emit('endRecord', {
        content: this.revertText,
        code: 0
      })
    }
    // 记录回答
    const textArr = jsonData.payload.choices.text && jsonData.payload.choices.text.map(item => item.content) || []
    this.revertText = this.revertText + textArr.join('')
  }

  on(event: string, fn: Function) {
    if (Array.isArray(event)) {
      for (let i = 0, l = event.length; i < l; i++) {
        this.on(event[i], fn)
      }
    } else {
      // 存在直接push, 不存在创建为空数组再push
      (this._events[event] || (this._events[event] = [])).push(fn)
    }
  }

  once(event: string, fn: Function) {
    let _self = this;
    function handler() {
      _self.off(event, handler);
      fn.apply(null, arguments);//emit里面调用时会给on方法传参
    }

    handler.fn = fn;//off里面根据这个判断销毁事件
    this.on(event, handler);
  }

  off(event: string, fn: Function) {
    //不传参数表示清空所有
    if (!arguments.length) {
      this._events = [];
    }
    //数组循环清空
    if (Array.isArray(event)) {
      for (let i = 0, l = event.length; i < l; i++) {
        this.off(event[i], fn)
      }
    }
    const cbs = this._events[event];
    if (!cbs) {
      return;
    }
    //不传第二参表示清空某事件所有监听函数
    if (arguments.length == 1) {
      this._events[event] = null
    }
    let cb, i = cbs.length
    while (i--) {
      cb = cbs[i]
      if (cb === fn || cb.fn === fn) { //cb.fn===fn用来移除once注册的事件
        cbs.splice(i, 1)
        break
      }
    }
  }

  emit(event: string, ...args: any[]) {
    console.log(args, typeof args)
    // 不存在event,直接返回
    if (!this._events[event]) {
      return
    }
    let cbs = [...this._events[event]];
    if (cbs) {
      for (let i = 0, l = cbs.length; i < l; i++) {
        try {
          cbs[i].apply(null, [...arguments].slice(1))
        } catch (e) {
          new Error(`event handler for "${e}"`)
        }
      }
    }
  }
}

不管你是想一次性接收到所有的内容,还是想像官方一样一点一点的接收,都能很方便的使用,视图调用代码如下:

const xfConfig = reactive({
  appid: "",
  apisecret: "",
  apikey: "",
});


function testXfSend() {
  if (!sendTest.content) {
    ElNotification.error({
      title: "请输入发送内容",
    });
    return;
  }

  const XfBot = new XfUtil(xfConfig.appid, xfConfig.apikey, xfConfig.apisecret);
  sendTest.revert = "";
  sendTest.loading = true;
  XfBot.start(sendTest.content);

  XfBot.on("endRecord", (data) => {
    console.log("回复内容", data.content);
    sendTest.loading = false;
    sendTest.revert = data.content;
  });
  
  // XfBot.on("message", (data) => {});

  XfBot.on("error", (data) => {
    sendTest.loading = false;
    ElNotification.error({
      title: data.content,
    });
  });
}

你可能感兴趣的:(electron,javascript,前端)