Files
caliverse_server/GameServer/Global/AIChat/AIChatServerConnector.cs
2025-05-01 07:20:41 +09:00

315 lines
15 KiB
C#

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<Result> 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<AIChatRanking>(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<AIChatRanking>(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<AIChatCharacter>(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<AIChatError>(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<Result> RegisterGeneralNPC()
{
var result = new Result();
var err_msg = string.Empty;
List<string> 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;
}
}
}