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 { 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 { 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 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().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 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(); 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> GetOnlineUsers(List playerIDs) { HashSet longs = new HashSet(); if(client?.Status != RTMClient.ClientStatus.Connected) return longs; var taskCompletionSource = new TaskCompletionSource>(); longs = new HashSet(playerIDs); client.GetOnlineUsers((HashSet 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 groupConversationList, int errorCode) => { if (errorCode == 0) { InitConversationList(groupConversationList); } }, startTime: chatConversation.lastMessageTime); } } void GetConversationList() { long uid = client.Uid; client?.GetGroupConversationList((List groupConversationList, int errorCode) => { if (errorCode == 0) { InitConversationList(groupConversationList); } }); } void InitConversationList(List 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 messages, int errorCode) => //{ // if (errorCode == 0) // { // InitChat(lastCursorId, messages); // } //}, groupConversationId, true, 10); //两个接口一样 GetGroupMessage多一个指定类型 client?.GetGroupChat((int count, long lastCursorId, long beginMsec, long endMsec, List 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 messages, bool push, bool nextContent) { RTMControlCenter.callbackQueue.PostAction(() => { if (chat == null) { chat = new ChatChatData(); chat.messageList = new List(); } 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 CanSend(string message) { if (groupConversationId == 0) return 0; var taskCompletionSource = new TaskCompletionSource(); 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 Send(string message) { if (groupConversationId == 0) return 0; if (!CanSend()) { ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_42")); return 0; } var taskCompletionSource = new TaskCompletionSource(); 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().TbGlobalConfig.ClubChatMaxCount) { return; } client?.GetGroupChat((int count, long lastCursorId, long beginMsec, long endMsec, List 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; } /// /// 解析消息类型 /// /// /// 1.文本消息 2.系统消息(没有点赞)3.系统消息 4.求助帖 5.图片 public string[] ParseChat(string chat) { return chat.Split('#'); } public async Task 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 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 TextAudit(string text) { var taskCompletionSource = new TaskCompletionSource(); 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; } } }