using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Text;
using Newtonsoft.Json.Linq;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using Newtonsoft.Json;
public class IChatManager : MonoBehaviour
{
public static IChatManager instance;
ClientWebSocket ws;
CancellationToken cancellation;
///
/// chat
///
const string wsChatUrl = "ws://spark-api.xf-yun.com/v1.1/chat";
const string chat_appid = "a83ddb30";//
const string chat_key = "df2f1b36593607691d184c2282977959";
const string chat_secret = "ZDUwMjczZjM2YTI2YTVhZDU2MjljZDI4";
///
/// 用户唯一id
///
string clientId = System.Guid.NewGuid().ToString("N");
private void Awake()
{
instance = this;
}
private void OnDestroy()
{
StopAllCoroutines();
if (ws != null && ws.State == WebSocketState.Open)
{
ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "", cancellation);
ws = null;
}
}
// Start is called before the first frame update
void Start()
{
}
IEnumerator CallapiForChat(JArray text, Action callback)
{
Debug.Log("开始调用API");
MyDebuggers.Log("开始聊天");
string url = GetAuthUrl(wsChatUrl, chat_key, chat_secret);
using (ws = new ClientWebSocket())
{
//连接
ws.ConnectAsync(new Uri(url), cancellation).Wait(2000);
if (ws.State == WebSocketState.Open)
{
Task re = Task.Run(() => ReciceChat(ws));
//连接成功,开始发送数据
//请求数据均为json字符串
JObject data = new JObject();
JObject header = new JObject();
JObject parameter = new JObject();
JObject payload = new JObject();
data.Add("header", header);
data.Add("parameter", parameter);
data.Add("payload", payload);
header.Add("app_id", chat_appid);
header.Add("uid", clientId);
JObject chat = new JObject();
parameter.Add("chat", chat);
chat.Add("domain", "general");//领域
chat.Add("max_tokens", 2048);//最大tokebs长度
chat.Add("auditing", "default");//审核
JObject message = new JObject();
payload.Add("message", message);
message.Add("text", text);
string da = data.ToString();
var frameData = Encoding.UTF8.GetBytes(da);
//发送数据
ws.SendAsync(new ArraySegment(frameData), WebSocketMessageType.Text, true, cancellation);
yield return new WaitUntil(() => re.IsCompleted);
Debug.Log("完成");
callback(re.Result);
}
else
{
Debug.LogError("连接失败");
callback(null);
}
}
}
///
/// 获取语音听写url
///
///
private string GetAuthUrl(string whichUrl, string key, string secret)
{
//当前时间戳,RFC1123格式
string date = DateTime.UtcNow.ToString("r");
//string date = "Thu, 15 Jun 2023 02:32:41 GMT";
string host = whichUrl.Split("://")[1].Split('/')[0];
string localPath = whichUrl.Split(host)[1];
string str = "host: " + host + "\n" + "date: " + date + "\n" + "GET " + localPath + " HTTP/1.1";
//hmac-sha256计算签名
string sha = HMACsha256(secret, str);
//授权(api_key,授权算法,头部,签名)
//authorization格式:api_key="$api_key",algorithm="hmac-sha256",headers="host date request-line",signature="$signature"
string authorization = string.Format("api_key=\"{0}\", algorithm=\"{1}\", headers=\"{2}\", signature=\"{3}\"", key, "hmac-sha256", "host date request-line", sha);
//System.Web.HttpUtility.UrlEncode
//鉴权参数(host,data,authorization):
//authorization使用base64编码的签名相关信息(签名基于hmac-sha256计算)
string path1 = "authorization" + "=" + Convert.ToBase64String(Encoding.UTF8.GetBytes(authorization));
date = date.Replace(" ", "%20").Replace(":", "%3A").Replace(",", "%2C");
string path2 = "date" + "=" + date;
string path3 = "host" + "=" + host;
string newurl = whichUrl + "?" + path1 + "&" + path2 + "&" + path3;
return newurl;
}
///
/// 星火大模型对话(联系上文)
///
/// 依次历史对话,不要多
/// 问题
///
public void Chat(List historyChat, string question, Action callback)
{
JArray text = new JArray();
for (int i = 0; i < historyChat.Count; i++)
{
JObject jb = new JObject();
jb.Add("role", "user");
jb.Add("content", historyChat[i].ask);
text.Add(jb);
JObject jb2 = new JObject();
jb2.Add("role", "assistant");
jb2.Add("content", historyChat[i].answer);
text.Add(jb2);
}
//问题
JObject jb111 = new JObject();
jb111.Add("role", "user");
jb111.Add("content", question);
text.Add(jb111);
StartCoroutine(CallapiForChat(text, callback));
}
///
/// 星火大模型对话(不联系上文)
///
/// 问题
///
public void Chat(string question, Action callback)
{
JArray text = new JArray();
JObject jb = new JObject();
jb.Add("role", "user");
jb.Add("content", question);
text.Add(jb);
StartCoroutine(CallapiForChat(text, callback));
}
string ReciveText(ClientWebSocket ws)
{
string msgRecive = "";
//全部消息容器
List bs = new List();
//缓冲区
var buffer = new byte[1024 * 4];
while (ws.State == WebSocketState.Open)
{
Debug.Log("等待消息接收");
var task = ws.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None);
task.Wait();
WebSocketReceiveResult result = task.Result;
//文本消息
if (result.CloseStatus.HasValue)
{
Debug.Log("接收关闭");
break;
}
if (result.MessageType == WebSocketMessageType.Text)
{
bs.AddRange(buffer.Take(result.Count));
//消息是否已接收完全
if (result.EndOfMessage)
{
//发送过来的消息
string userMsg = Encoding.UTF8.GetString(bs.ToArray(), 0, bs.Count);
var resultObj = GetTextResultData(userMsg);
string msg = resultObj.data.result.GetResultText();
//清空消息容器
bs = new List();
Debug.Log(msg);
msgRecive = msgRecive + msg;
if (resultObj.data.result.ls)
{
//关闭
Debug.Log("最后一块数据");
break;
}
}
}
}
ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "", cancellation);
Debug.Log("退出接收");
return msgRecive;
}
string ReciceChat(ClientWebSocket ws)
{
string msgRecive = "";
//全部消息容器
List bs = new List();
//缓冲区
var buffer = new byte[1024 * 4];
while (ws.State == WebSocketState.Open)
{
//Debug.Log("等待消息接收");
var task = ws.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None);
task.Wait();
WebSocketReceiveResult result = task.Result;
//文本消息
if (result.CloseStatus.HasValue)
{
Debug.Log("接收关闭");
break;
}
if (result.MessageType == WebSocketMessageType.Text)
{
bs.AddRange(buffer.Take(result.Count));
//消息是否已接收完全
if (result.EndOfMessage)
{
//发送过来的消息
string userMsg = Encoding.UTF8.GetString(bs.ToArray(), 0, bs.Count);
bs = new List();
JObject jb = JObject.Parse(userMsg);
int code = jb["header"]["code"].ToObject();
string sid = jb["header"]["sid"].ToString();
int status = jb["header"]["status"].ToObject();
string message = jb["header"]["message"].ToString();
if (code == 0)
{
//正常
JToken[] ja = jb["payload"]["choices"]["text"].ToArray();
foreach (var item in ja)
{
msgRecive = msgRecive + item["content"].ToString();
//Debug.Log(item["content"].ToString());
}
if (status == 2)
{
//完成
Debug.Log("最后一条");
break;
}
}
else
{
//出错
Debug.LogError(message);
break;
}
}
}
}
Debug.Log("退出接收");
return msgRecive;
}
private string HMACsha256(string apiSecretIsKey, string buider)
{
byte[] bytes = Encoding.UTF8.GetBytes(apiSecretIsKey);
System.Security.Cryptography.HMACSHA256 hMACSHA256 = new System.Security.Cryptography.HMACSHA256(bytes);
byte[] date = Encoding.UTF8.GetBytes(buider);
date = hMACSHA256.ComputeHash(date);
hMACSHA256.Clear();
return Convert.ToBase64String(date);
}
///
/// 从此实例检索子数组
///
/// 要检索的数组
/// 起始索引号
/// 检索最大长度
/// 与此实例中在 startIndex 处开头、长度为 length 的子数组等效的一个数组
public static byte[] SubArray(byte[] source, int startIndex, int length)
{
if (startIndex < 0 || startIndex > source.Length || length < 0)
{
return null;
}
byte[] Destination;
if (startIndex + length <= source.Length)
{
Destination = new byte[length];
Array.Copy(source, startIndex, Destination, 0, length);
}
else
{
Destination = new byte[(source.Length - startIndex)];
Array.Copy(source, startIndex, Destination, 0, source.Length - startIndex);
}
return Destination;
}
///
/// 解析文本
///
///
///
private ResponseData GetTextResultData(string ReceviceStr)
{
ResponseData temp = new ResponseData();
ReaponseDataInfo dataInfo = new ReaponseDataInfo();
TextInfo resultInfo = new TextInfo();
List tempwsS;
List tempcwS;
Ws tempWs;
Cw temocw;
var jsonObj = (JObject)JsonConvert.DeserializeObject(ReceviceStr);
temp.code = jsonObj["code"].ToObject();
temp.message = jsonObj["message"].ToObject();
temp.sid = jsonObj["sid"].ToObject();
var data = jsonObj["data"]/*.ToObject()*/;
dataInfo.status = data["status"].ToObject();
var result = data["result"]/*.ToObject()*/;
resultInfo.bg = result["bg"].ToObject();
resultInfo.ed = result["ed"].ToObject();
//resultInfo.pgs = result["pgs"].ToObject();
//resultInfo.rg = result["rg"].ToObject();
resultInfo.sn = result["sn"].ToObject(); ;
resultInfo.ls = result["ls"].ToObject(); ;
var wss = result["ws"];
tempwsS = new List();
JArray wsArray = wss.ToObject();
for (int i = 0; i < wsArray.Count; i++)
{
tempWs = new Ws();
tempWs.bg = wsArray[i]["bg"].ToObject();
//tempWs.ed = wsArray[i]["ed"].ToObject();
var cws = wsArray[i]["cw"];
tempcwS = new List();
JArray cwArray = cws.ToObject();
for (int j = 0; j < cwArray.Count; j++)
{
temocw = new Cw();
temocw.sc = cwArray[j]["sc"].ToObject();
temocw.w = cwArray[j]["w"].ToObject();
tempcwS.Add(temocw);
}
tempWs.cw = tempcwS.ToArray();
tempwsS.Add(tempWs);
}
resultInfo.ws = tempwsS.ToArray();
dataInfo.result = resultInfo;
temp.data = dataInfo;
return temp;
}
}
///
/// 返回数据
///
///
public class ResponseData
{
public int code;
public string message;
public string sid;
public ReaponseDataInfo data;
}
public class ReaponseDataInfo
{
public int status;
public T result;
}
public class Ws
{
public Cw[] cw;
public int bg;
public int ed;
}
public class Cw
{
public int sc;
public string w;
}
///
/// 问答对话组
///
public class ChatInfo
{
public string ask;
public string answer;
}
///
/// 语音听写数据
///
public class TextInfo
{
public int bg;
public int ed;
public string pgs;
public int[] rg;
public int sn;
public bool ls;
public Ws[] ws;
public string GetResultText()
{
StringBuilder strB = new StringBuilder();
for (int i = 0; i < ws.Length; i++)
{
strB.Append(ws[i].cw[0].w);
}
return strB.ToString();
}
}