Files
back_cantanBuilding/Assets/Scripts/Services/ChatService.cs
2026-05-26 16:15:54 +08:00

646 lines
21 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using asap.core;
using com.fpnn;
using com.fpnn.rtm;
using GameCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UniRx;
namespace game
{
public class ChatQuestProcessor : RTMQuestProcessor
{
public override void SessionClosed(int ClosedByErrorCode)
{
Debug.Log("SessionClosed errorCode = " + ClosedByErrorCode);
}
public override bool ReloginWillStart(int lastErrorCode, int retriedCount)
{
Debug.Log("ReloginWillStart lastErrorCode = " + lastErrorCode + ", retriedCount = " + retriedCount);
return true;
}
public override void Kickout()
{
Debug.Log("Kickout");
}
public override void KickoutRoom(long roomId)
{
Debug.Log("KickoutRoom roomId = " + roomId);
}
}
[Serializable]
public class ChatMessage : IComparable<ChatMessage>
{
public ChatMessage(long messageId, long fromUid, string message, long mtime, long cursorId = 0)
{
this.messageId = messageId;
this.fromUid = fromUid;
this.message = message;
this.mtime = mtime;
this.cursorId = cursorId;
}
public static ChatMessage ConvertoFromRTMMessage(RTMMessage message)
{
ChatMessage chatDemoMessage = new ChatMessage(message.messageId, message.fromUid, message.stringMessage, message.modifiedTime);
return chatDemoMessage;
}
public static ChatMessage ConvertoFromHistoryMessage(HistoryMessage message)
{
ChatMessage chatDemoMessage = new ChatMessage(message.messageId, message.fromUid, message.stringMessage, message.modifiedTime, message.cursorId);
return chatDemoMessage;
}
public int CompareTo(ChatMessage message)
{
if (mtime > message.mtime)
return 1;
else if (mtime < message.mtime)
return -1;
else
{
if (messageId >= message.messageId)
return 1;
else
return -1;
}
}
public long messageId;
public long fromUid;
public string message;
public long mtime;
public long cursorId;
}
public class ChatMessageCompare : IEqualityComparer<ChatMessage>
{
public bool Equals(ChatMessage msg1, ChatMessage msg2)
{
if (msg1.mtime == msg2.mtime && msg1.messageId == msg2.messageId)
{
if (msg1.cursorId == msg2.cursorId)
return true;
else if ((msg1.cursorId == 0 && msg2.cursorId != 0) || (msg1.cursorId != 0 && msg2.cursorId == 0))
return true;
else
return false;
}
else
return false;
}
public int GetHashCode(ChatMessage msg)
{
if (msg == null)
return 0;
else
return string.Format("{0}_{1}_{2}", msg.mtime, msg.messageId, msg.message).GetHashCode();
}
}
[Serializable]
public class ChatConversation
{
//public Int32 unreadCount;
public Int64 lastMessageTime;
//public ChatMessage lastMessage;
//public void AddMessage(ChatMessage message)
//{
// if (lastMessage == null)
// {
// lastMessage = message;
// return;
// }
// if (message.mtime > lastMessage.mtime)
// {
// lastMessage = message;
// return;
// }
// else if (message.mtime == lastMessage.mtime && lastMessage.messageId > message.messageId)
// {
// lastMessage = message;
// return;
// }
//}
}
public class ChatChatData
{
public Int64 lastId;
public List<ChatMessage> messageList;
}
public class ChatChangeEvent
{
public bool nextContent;
public bool push;
}
public enum ChatType
{
Text = 1,//文本消息
System = 2,//系统内提示
System2 = 3,//解锁地图
Help = 4,//请求照片
Emoji = 5,//发送表情
HelpEnergy = 6//请求体力帮助
}
public class ChatService : IChatService
{
private IUserService userService { get; set; }
private IConfig config { get; set; }
private int sendInterval = 2;
long groupConversationId = 0;
RTMClient client;
ChatConversation chatConversation;
ChatChatData chat;
public ChatService(IUserService userService, IConfig config, cfg.Tables tables)
{
this.userService = userService;
this.config = config;
this.sendInterval = tables.TbGlobalConfig.ClubChatLimitTime;
ClientEngine.Init();
RTMControlCenter.Init();
}
private IDisposable disposable;
public void WaitingForLogin()
{
disposable = RootCtx.OnEvent<EvtAPISvrLoginSuccess>().Subscribe(OnApiSvrLoginSuccess);
}
private async void OnApiSvrLoginSuccess(EvtAPISvrLoginSuccess evt)
{
disposable.Dispose();
int retryCount = 0;
while(! await LoginRTM())
{
retryCount++;
await Task.Delay(Math.Min(5000 + retryCount * 1000, 10000));
}
}
private async Task<bool> LoginRTM(int timeout = 5)
{
var userIdLong = userService.InternalId;
var pid = long.Parse(config.Get(GConstant.K_RTMP_ID, "80000560"));
var rtmServerEndpoint = config.Get(GConstant.K_RTM_Server_Endpoint, "rtm-nx-front.ilivedata.com:13321");
var hmacSecret = config.Get(GConstant.K_RTM_Hmac_Secret, "7a1ba6bb85a4473d96fa4bf45b01f1dd");
ChatQuestProcessor processor = new ChatQuestProcessor();
InitProcessor(processor);
client = RTMClient.getInstance(rtmServerEndpoint, pid, userIdLong, processor);
var taskCompletionSource = new TaskCompletionSource<bool>();
string token = GetToken(pid, hmacSecret, userIdLong, out long ts);
client.Login((long pid, long uid, bool successful, int errorCode) =>
{
if (successful)
{
taskCompletionSource.SetResult(true);
//RTMControlCenter.callbackQueue.PostAction(() =>
//{
// GetConversationList();
//});
}
else
{
taskCompletionSource.SetResult(false);
Debug.Log("Login failed, errorCode = " + errorCode);
#if AGG
using (var e = GEvent.GameEvent("ilivedata"))
{
e.AddContent("connection_failure", errorCode)
.AddContent("pid", pid)
.AddContent("uid", uid)
.AddContent("userIdLong", userIdLong);
}
#endif
}
}, token, ts, timeout);
return await(taskCompletionSource.Task);
}
string GetToken(long pid, string hmacSecret, long uid, out long ts)
{
ts = ClientEngine.GetCurrentSeconds();
string text = pid.ToString() + ":" + uid.ToString() + ":" + ts.ToString();
using (var hmacsha256 = new HMACSHA256(Convert.FromBase64String(hmacSecret)))
{
var hash = hmacsha256.ComputeHash(Encoding.UTF8.GetBytes(text));
return Convert.ToBase64String(hash);
}
}
public async Task<HashSet<long>> GetOnlineUsers(List<long> playerIDs)
{
HashSet<long> longs = new HashSet<long>();
if(client?.Status != RTMClient.ClientStatus.Connected)
return longs;
var taskCompletionSource = new TaskCompletionSource<HashSet<long>>();
longs = new HashSet<long>(playerIDs);
client.GetOnlineUsers((HashSet<long> uids, int errorCode) =>
{
if (errorCode == 0)
{
taskCompletionSource.SetResult(uids);
}
else
{
taskCompletionSource.SetResult(null);
}
}, longs);
return await taskCompletionSource.Task;
}
public void GetConversationList(long groupConversationId)
{
this.groupConversationId = groupConversationId;
if(client?.Status != RTMClient.ClientStatus.Connected)
return;
RTMControlCenter.callbackQueue.PostAction(() =>
{
GetConversationList();
});
}
void InitProcessor(ChatQuestProcessor processor)
{
processor.PushGroupChatCallback = OnPushGroupChat;
processor.PushGroupMessageCallback = OnPushGroupMessage;
//processor.PushMessageCallback = OnPushGroupMessage;
//processor.PushChatCallback = OnPushGroupChat;
processor.ReloginCompletedCallback = OnRelogin;
}
void OnPushGroupChat(RTMMessage message)
{
if (message.toId == groupConversationId)
{
AddMessage(groupConversationId, true);
}
//else if (message.toId == userIdLong)
//{
// //个人聊天列表
//}
}
void OnPushGroupMessage(RTMMessage message)
{
if (message.toId == groupConversationId)
{
//主界面推送消息
}
//else if (message.toId == userIdLong)
//{
// //个人聊天列表
//}
}
void OnRelogin(bool successful, bool retryAgain, int errorCode, int retriedCount)
{
//Debug.Log("OnRelogin successful = " + successful + " retryAgain = " + retryAgain + ", errorCode = " + errorCode + ", retriedCount = " + retriedCount);
if (successful && chatConversation != null)
{
client?.GetGroupUnreadConversationList((List<Conversation> groupConversationList, int errorCode) =>
{
if (errorCode == 0)
{
InitConversationList(groupConversationList);
}
}, startTime: chatConversation.lastMessageTime);
}
}
void GetConversationList()
{
long uid = client.Uid;
client?.GetGroupConversationList((List<Conversation> groupConversationList, int errorCode) =>
{
if (errorCode == 0)
{
InitConversationList(groupConversationList);
}
});
}
void InitConversationList(List<Conversation> list)
{
RTMControlCenter.callbackQueue.PostAction(() =>
{
foreach (Conversation conversation in list)
{
if (conversation.id == groupConversationId)
{
if (chatConversation == null)
{
chatConversation = new ChatConversation();
}
//chatConversation.unreadCount += conversation.unreadCount;
//if (conversation.lastMessage != null && conversation.lastMessage.messageId != 0)
// chatConversation.AddMessage(ChatMessage.ConvertoFromHistoryMessage(conversation.lastMessage));
UpdateConversationList(false);
return;
}
}
});
}
void UpdateConversationList(bool push)
{
if (chatConversation == null)
return;
//client?.GetGroupMessage((int count, long lastCursorId, long beginMsec, long endMsec, List<HistoryMessage> messages, int errorCode) =>
//{
// if (errorCode == 0)
// {
// InitChat(lastCursorId, messages);
// }
//}, groupConversationId, true, 10);
//两个接口一样 GetGroupMessage多一个指定类型
client?.GetGroupChat((int count, long lastCursorId, long beginMsec, long endMsec, List<HistoryMessage> messages, int errorCode) =>
{
if (errorCode == 0)
{
InitChat(lastCursorId, messages, push, false);
}
else
{
Debug.Log("GetGroupChat errorCode = " + errorCode);
}
}, groupConversationId, true, 10);
}
void InitChat(long lastId, List<HistoryMessage> messages, bool push, bool nextContent)
{
RTMControlCenter.callbackQueue.PostAction(() =>
{
if (chat == null)
{
chat = new ChatChatData();
chat.messageList = new List<ChatMessage>();
}
if (messages != null)
{
if ((chat.lastId == 0 || lastId < chat.lastId) && lastId != 0)
chat.lastId = lastId;
foreach (HistoryMessage message in messages)
{
chat.messageList.Add(ChatMessage.ConvertoFromHistoryMessage(message));
}
chat.messageList = chat.messageList.Distinct(new ChatMessageCompare()).ToList();
chat.messageList.Sort();
GContext.Publish(new ChatChangeEvent() { nextContent = nextContent, push = push });
}
});
}
async void AddMessage(long conversationId, bool push)
{
await Task.Delay(100);//服务器聊天记录存储延时
RTMControlCenter.callbackQueue.PostAction(() =>
{
if (conversationId == groupConversationId)
{
if (chatConversation == null)
{
chatConversation = new ChatConversation();
//chatConversation.unreadCount = 1;
//chatConversation.lastMessage = message;
}
//else
//{
// chatConversation.AddMessage(message);
//}
UpdateConversationList(push);
//if (chat == null)
// return;
//chat.messageList.Add(message);
//chat.messageList = chat.messageList.Distinct(new ChatMessageCompare()).ToList();
//chat.messageList.Sort();
//GContext.Publish(new ChatChangeEvent());
}
});
}
async Task<long> CanSend(string message)
{
if (groupConversationId == 0)
return 0;
var taskCompletionSource = new TaskCompletionSource<long>();
message = string.Format("{0}#{1}", userService.UserId, message);
client?.SendGroupChat((long messageId, long mtime, int errorCode) =>
{
if (errorCode != 0)
{
taskCompletionSource.SetResult(0);
Debug.Log("SendGroupChat errorCode = " + errorCode);
}
else
{
taskCompletionSource.SetResult(messageId);
RTMControlCenter.callbackQueue.PostAction(() =>
{
AddMessage(groupConversationId, false);
chatConversation.lastMessageTime = mtime;
});
}
}, groupConversationId, message);
long messageId = await taskCompletionSource.Task;
return messageId;
}
async Task<long> Send(string message)
{
if (groupConversationId == 0)
return 0;
if (!CanSend())
{
ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_42"));
return 0;
}
var taskCompletionSource = new TaskCompletionSource<long>();
message = string.Format("{0}#{1}", userService.UserId, message);
client?.SendGroupChat((long messageId, long mtime, int errorCode) =>
{
if (errorCode != 0)
{
taskCompletionSource.SetResult(0);
Debug.Log("SendGroupChat errorCode = " + errorCode);
}
else
{
taskCompletionSource.SetResult(messageId);
RTMControlCenter.callbackQueue.PostAction(() =>
{
AddMessage(groupConversationId, false);
chatConversation.lastMessageTime = mtime;
});
}
}, groupConversationId, message);
long messageId = await taskCompletionSource.Task;
return messageId;
}
Task sendTask = null;
private bool CanSend()
{
if(sendTask == null || sendTask.IsCompleted)
{
sendTask = Task.Delay(TimeSpan.FromSeconds(sendInterval));
return true;
}
return false;
}
public void NextPage()
{
if (groupConversationId == 0)
return;
if (chat == null)
return;
if (chat.messageList.Count >= GContext.container.Resolve<cfg.Tables>().TbGlobalConfig.ClubChatMaxCount)
{
return;
}
client?.GetGroupChat((int count, long lastCursorId, long beginMsec, long endMsec, List<HistoryMessage> messages, int errorCode) =>
{
if (errorCode == 0)
{
InitChat(lastCursorId, messages, false, true);
}
else
{
Debug.Log("GetGroupChat errorCode = " + errorCode);
}
}, groupConversationId, true, 10, lastId: chat.lastId);
}
public void OnQuit()
{
chat = null;
chatConversation = null;
groupConversationId = 0;
}
public void InitChatData()
{
chat = null;
}
public ChatChatData GetGroupChat()
{
return chat;
}
/// <summary>
/// 解析消息类型
/// </summary>
/// <param name="chat"></param>
/// <returns>1.文本消息 2.系统消息没有点赞3.系统消息 4.求助帖 5.图片</returns>
public string[] ParseChat(string chat)
{
return chat.Split('#');
}
public async Task<long> SendHelpEnergyMessage(int itemId)
{
string chat = $"{(int)ChatType.HelpEnergy}#{itemId}";
return await CanSend(chat);
}
public void SendEmoji(string chat)
{
chat = $"{(int)ChatType.Emoji}#{chat}";
_ = Send(chat);
}
public async Task<long> SendGetPhotoMessage(string chat)
{
chat = $"{(int)ChatType.Help}#{chat}";
return await CanSend(chat);
}
//谁干了什么
public void SendSystemMessage2(string chat)
{
chat = $"{(int)ChatType.System2}#{chat}";
_ = CanSend(chat);
}
//俱乐部系统提示
public void SendSystemMessage(string chat)
{
chat = $"{(int)ChatType.System}#{chat}";
_ = CanSend(chat);
}
public async void SendTxTMessage(string chat)
{
//审核
bool value = await TextAudit(chat);
if (value)
{
chat = $"{(int)ChatType.Text}#{chat}";
_ = Send(chat);
}
else
{
ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_33"));
}
}
public async Task<bool> TextAudit(string text)
{
var taskCompletionSource = new TaskCompletionSource<bool>();
client.TextCheck((TextCheckResult result, int errorCode) =>
{
if (errorCode != com.fpnn.ErrorCode.FPNN_EC_OK)
{
taskCompletionSource.SetResult(true);
}
else
{
//Debug.Log(" -- result " + result.result);
//if (result.tags != null)
// Debug.Log(" -- tags.Count " + result.tags.Count);
//if (result.wlist != null)
// Debug.Log(" -- wlist.Count " + result.wlist.Count);
taskCompletionSource.SetResult(result.result == 0);
}
}, text);
return await taskCompletionSource.Task;
}
}
}