using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Signers; using ServerCommon; using System.Text; using ServerCore; using ServerBase; using System.Text.Json.Nodes; using Newtonsoft.Json; using MetaAssets; using META_ID = System.UInt32; namespace GameServer { static public class AIChatServerConnector { static string server_jwt = string.Empty; static string? jwtAdminJson = null; static string? jwtUserJson = null; static public async Task onInit() { var messageBytes = MakeMessage("admin"); var signatureBytes = SignMessage(messageBytes); AIChatJwt AIChatAdminJwt = new AIChatJwt(); AIChatAdminJwt.messageBase64 = Convert.ToBase64String(messageBytes); AIChatAdminJwt.signatureBase64 = Convert.ToBase64String(signatureBytes); jwtAdminJson = JsonConvert.SerializeObject(AIChatAdminJwt); messageBytes = MakeMessage("user"); signatureBytes = SignMessage(messageBytes); AIChatJwt AIChatUserJwt = new AIChatJwt(); AIChatUserJwt.messageBase64 = Convert.ToBase64String(messageBytes); AIChatUserJwt.signatureBase64 = Convert.ToBase64String(signatureBytes); jwtUserJson = JsonConvert.SerializeObject(AIChatUserJwt); await serverJwtIssue(); await RegisterGeneralNPC(); } static public string getUserJwtJson() { NullReferenceCheckHelper.throwIfNull(jwtUserJson, () => $"jwtUserJson is null !!!"); return jwtUserJson; } //서버의 JWT를 발행합니다. static private async Task serverJwtIssue() { (var result, string resMsg) = await sendAIChatServer("POST", $"/api/auth/jwt/issue", null, jwtAdminJson); if (result.isFail()) return result; var jwt = JsonNode.Parse(resMsg)?["jwt"]?.ToString(); if (jwt != null) { server_jwt = jwt; } return result; } //전체 채팅량 랭킹 (매개변수인 languageType은 res로 받는 캐릭터 이름이나 owner의 이름의 번역 차이임.) static public async Task<(Result, AIChatRanking?)> getAllRanking(LanguageType language, int page, int numPerPage) { (var result, string resMsg) = await sendAIChatServerWithServerJwt("GET", $"/api/ranking/all?lang={language.ToString().ToLower()}&page={page}&numPerPage={numPerPage}", null); if (result.isFail()) return (result, null); try { var aichat_ranking = JsonConvert.DeserializeObject(resMsg); return (result, aichat_ranking); } catch (Exception ex) { Log.getLogger().error($"Exception !!!, DeserializeObject : exception:{ex}, resMsg:{resMsg}"); return (result, null); } } //최근 24시간 채팅량 랭킹 (매개변수인 languageType은 res로 받는 캐릭터 이름이나 owner의 이름의 번역 차이임.) static public async Task<(Result, AIChatRanking?)> getTodayRanking(LanguageType language, int page, int numPerPage) { (var result, string resMsg) = await sendAIChatServerWithServerJwt("GET", $"/api/ranking/today?lang={language.ToString().ToLower()}&page={page}&numPerPage={numPerPage}", null); if (result.isFail()) return (result, null); try { var aichat_ranking = JsonConvert.DeserializeObject(resMsg); return (result, aichat_ranking); } catch (Exception ex) { Log.getLogger().error($"Exception !!!, DeserializeObject : exception:{ex}, resMsg:{resMsg}"); return (result, null); } } //특정 캐릭터 조회 static public async Task<(Result, AIChatCharacter?)> getCharacterByTargetGuid(string target_guid) { (var result, string resMsg) = await sendAIChatServerWithServerJwt("GET", $"/api/character/{target_guid}", null); if (result.isFail()) return (result, null); try { var aichat_character = JsonConvert.DeserializeObject(resMsg); return (result, aichat_character); } catch (Exception ex) { Log.getLogger().error($"Exception !!!, DeserializeObject : exception:{ex}, resMsg:{resMsg}"); return (result, null); } } static private byte[] MakeMessage(string role) { var message = "{\"role\":\"" + role + "\"}"; var messageBytes = Encoding.UTF8.GetBytes(message); return messageBytes; } static private byte[] SignMessage(byte[] messageBytes) { var privateKey = Convert.FromBase64String(GameServerApp.getServerLogic().getServerConfig().AIChatConfig.PrivateKey); var privateKeyParams = new Ed25519PrivateKeyParameters(privateKey, 0); // 서명합니다. var signer = new Ed25519Signer(); signer.Init(true, privateKeyParams); signer.BlockUpdate(messageBytes, 0, messageBytes.Length); var signature = signer.GenerateSignature(); return signature; } static public async Task<(Result, string)> sendAIChatServerWithServerJwt(string httpMethod, string url, string? bodyJson) { var result = new Result(); var server_logic = GameServerApp.getServerLogic(); if (server_logic.getServerConfig().OfflineMode == true) { result.setFail(ServerErrorCode.AiChatServerInactive); return (result, string.Empty); } var fullUrl = makeAiChatServerURL(url); (var IsSuccessStatusCode, var readMsg) = await HttpClientHelper.sendHttpRequest( httpMethod , fullUrl, server_jwt, bodyJson , "application/json" , "caliverse", "1.0" ); if (IsSuccessStatusCode == false) { Log.getLogger().info($"sendAIChatServer Failed. fullUrl : {fullUrl}, return msg : {readMsg} !!!"); result = ErrorHelper(readMsg, fullUrl); if (result.ErrorCode == ServerErrorCode.AiChatServerTokenExpired) { result = await serverJwtIssue(); if (result.isSuccess()) { (IsSuccessStatusCode, readMsg) = await HttpClientHelper.sendHttpRequest( httpMethod , fullUrl, server_jwt, bodyJson , "application/json" , "caliverse", "1.0" ); if (IsSuccessStatusCode == false) result.setFail(ServerErrorCode.AiChatServerTokenExpired); } } } return (result, readMsg); } static public async Task<(Result, string)> sendAIChatServer(string httpMethod, string url, string? jwt, string? bodyJson) { var result = new Result(); var server_logic = GameServerApp.getServerLogic(); if (server_logic.getServerConfig().OfflineMode == true) { result.setFail(ServerErrorCode.AiChatServerInactive); return (result, string.Empty); } var fullUrl = makeAiChatServerURL(url); (var IsSuccessStatusCode, var readMsg) = await HttpClientHelper.sendHttpRequest( httpMethod , fullUrl, jwt, bodyJson , "application/json" , "caliverse", "1.0" ); if (IsSuccessStatusCode == false) { Log.getLogger().info($"sendAIChatServer Failed. fullUrl : {fullUrl}, return msg : {readMsg} !!!"); result = ErrorHelper(readMsg, fullUrl); return (result, readMsg); } return (result, readMsg); } static public string makeAiChatServerURL(string url) { return $"{GameServerApp.getServerLogic().getServerConfig().AIChatConfig.BaseAddress}{url}"; } static private Result ErrorHelper(string errorMsg, string fullUrl) { var result = new Result(); var err_msg = string.Empty; try { AIChatError? error = JsonConvert.DeserializeObject(errorMsg); NullReferenceCheckHelper.throwIfNull(error, () => $"error is null !!!"); switch (error.code) { case 998: result.setFail(ServerErrorCode.AiChatServerBadrequest, err_msg); break; case 999: result.setFail(ServerErrorCode.AiChatServerForbidden, err_msg); break; case 1000: result.setFail(ServerErrorCode.AiChatServerUnauthorized, err_msg); break; case 1001: result.setFail(ServerErrorCode.AiChatServerUserNotFound, err_msg); break; case 1002: result.setFail(ServerErrorCode.AiChatServerCharacterNotFound, err_msg); break; case 1003: result.setFail(ServerErrorCode.AiChatServerReactionNotFound, err_msg); break; case 1004: result.setFail(ServerErrorCode.AiChatServerExampleDialongNotFound, err_msg); break; case 1005: result.setFail(ServerErrorCode.AiChatServerSessionNotFound, err_msg); break; case 1006: result.setFail(ServerErrorCode.AiChatServerModelNotFound, err_msg); break; case 1007: result.setFail(ServerErrorCode.AiChatServerNotEnoughPoint, err_msg); break; case 1008: result.setFail(ServerErrorCode.AiChatServerInvalidParameters, err_msg); break; case 1009: result.setFail(ServerErrorCode.AiChatServerSessionLimitExceeded, err_msg); break; case 1010: result.setFail(ServerErrorCode.AiChatServerInvalidSignature, err_msg); break; case 1011: result.setFail(ServerErrorCode.AiChatServerUserAlreadyExists, err_msg); break; case 1012: result.setFail(ServerErrorCode.AiChatServerRemoveFailed, err_msg); break; case 1013: result.setFail(ServerErrorCode.AiChatServerDuplicateGuid, err_msg); break; case 1014: result.setFail(ServerErrorCode.AiChatServerDuplicateId, err_msg); break; case 1015: result.setFail(ServerErrorCode.AiChatServerChatNotFound, err_msg); break; case 1016: result.setFail(ServerErrorCode.AiChatServerTokenExpired, err_msg); break; case 1017: result.setFail(ServerErrorCode.AiChatServerInternalServer, err_msg); break; case 1018: result.setFail(ServerErrorCode.AiChatServerInvalidMessage, err_msg); break; case 1019: result.setFail(ServerErrorCode.AiChatServerUserOnlyFeature, err_msg); break; case 1020: result.setFail(ServerErrorCode.AiChatServerOwnershipError, err_msg); break; case 1024: result.setFail(ServerErrorCode.AiChatServerChargeOrderNotFoundError, err_msg); break; default: { err_msg = $"sendAIChatServer Failed. fullUrl : {fullUrl}, error code : {error.code}, error name : {error.name}, error message : {error.message} !!!"; result.setFail(ServerErrorCode.AiChatServerReqFailed, err_msg); Log.getLogger().info(err_msg); break; } } } catch (Exception ex) { err_msg = $"AiChat Exception message : {ex.Message}"; result.setFail(ServerErrorCode.AiChatServerReqFailed, err_msg); Log.getLogger().info(err_msg); } return result; } static private async Task RegisterGeneralNPC() { var result = new Result(); var err_msg = string.Empty; List npclist = new(); var npcGeneralMetaDatas = MetaData.Instance.NpcGeneralMetaData; foreach (var generalNpc in npcGeneralMetaDatas) { (result, var ai_chat_character) = await getCharacterByTargetGuid(generalNpc.Value.Npc_id.ToString()); if (result.isSuccess()) { continue; } EGenderType gender = EGenderType.MALE; if(generalNpc.Value.Gender == EGenderType.ALL) gender = EGenderType.MALE; AIChatRegisterCharacter to_register_ai_chat_server = new(); to_register_ai_chat_server.guid = generalNpc.Value.Npc_id.ToString(); to_register_ai_chat_server.ownerUserGuid = string.Empty; to_register_ai_chat_server.lang = generalNpc.Value.Language.ToString().ToLower(); to_register_ai_chat_server.name = generalNpc.Value.Name; to_register_ai_chat_server.persona = UgcNpcHelper.toPersonaWithJsonString(generalNpc.Value.WorldScenario, generalNpc.Value.Description); to_register_ai_chat_server.firstMes = generalNpc.Value.FirstMes; to_register_ai_chat_server.shortDesc = generalNpc.Value.ShortDesc; to_register_ai_chat_server.tags = generalNpc.Value.Tags.Select(x => (META_ID)x).ToList(); to_register_ai_chat_server.isOfficial = true; to_register_ai_chat_server.socialActionConfig = UgcNpcHelper.toSocialActionConfigWithAIChatServer((META_ID)generalNpc.Value.SocialAction_default, generalNpc.Value.SocialAction_habit.Select(x => (META_ID)x).ToList()); to_register_ai_chat_server.attributes.gender = (int)gender; string json = JsonConvert.SerializeObject(to_register_ai_chat_server); var bodyParam = new StringContent(json, Encoding.UTF8, "application/json"); (result, string resMsg) = await sendAIChatServerWithServerJwt("POST", $"/api/character", json); if (result.isFail()) { continue; } } return result; } } }