From c53b6b9348416099c45189019154e1a34b8adb99 Mon Sep 17 00:00:00 2001 From: AlanMoonbase Date: Sun, 2 Mar 2025 18:20:35 -0800 Subject: [PATCH] implement ``MessagingService`` add additional messaging schemas --- src/Program.cs | 1 + src/Schema/ArrayOfCombinedListMessage.cs | 12 + .../ArrayOfKeyValuePairOfStringString.cs | 11 + src/Schema/CombinedListMessage.cs | 17 ++ src/Schema/KeyValuePairOfStringString.cs | 13 + src/Schema/Message.cs | 39 +++ src/Services/MessagingService.cs | 237 ++++++++++++++++++ 7 files changed, 330 insertions(+) create mode 100644 src/Schema/ArrayOfCombinedListMessage.cs create mode 100644 src/Schema/ArrayOfKeyValuePairOfStringString.cs create mode 100644 src/Schema/CombinedListMessage.cs create mode 100644 src/Schema/KeyValuePairOfStringString.cs create mode 100644 src/Schema/Message.cs create mode 100644 src/Services/MessagingService.cs diff --git a/src/Program.cs b/src/Program.cs index 1c1cbe9..1bc1dcd 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -41,6 +41,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); bool assetServer = builder.Configuration.GetSection("AssetServer").GetValue("Enabled"); string assetIP = builder.Configuration.GetSection("AssetServer").GetValue("ListenIP"); diff --git a/src/Schema/ArrayOfCombinedListMessage.cs b/src/Schema/ArrayOfCombinedListMessage.cs new file mode 100644 index 0000000..41b52f0 --- /dev/null +++ b/src/Schema/ArrayOfCombinedListMessage.cs @@ -0,0 +1,12 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema +{ + [XmlRoot(ElementName = "ArrayOfCombinedListMessage")] + [Serializable] + public class ArrayOfCombinedListMessage + { + [XmlElement("CombinedListMessage")] + public CombinedListMessage[] CombinedListMessage; + } +} \ No newline at end of file diff --git a/src/Schema/ArrayOfKeyValuePairOfStringString.cs b/src/Schema/ArrayOfKeyValuePairOfStringString.cs new file mode 100644 index 0000000..774fac2 --- /dev/null +++ b/src/Schema/ArrayOfKeyValuePairOfStringString.cs @@ -0,0 +1,11 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema +{ + [XmlRoot(ElementName = "ArrayOfKeyValuePairOfStringString")] + public class ArrayOfKeyValuePairOfStringString + { + [XmlElement(ElementName = "KeyValuePairOfStringString")] + public KeyValuePairOfStringString[]? KeyValuePairOfStringString { get; set; } + } +} \ No newline at end of file diff --git a/src/Schema/CombinedListMessage.cs b/src/Schema/CombinedListMessage.cs new file mode 100644 index 0000000..073702a --- /dev/null +++ b/src/Schema/CombinedListMessage.cs @@ -0,0 +1,17 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema +{ + [Serializable] + public class CombinedListMessage + { + [XmlElement(ElementName = "MessageType")] + public int MessageType; + + [XmlElement(ElementName = "MessageDate")] + public DateTime MessageDate; + + [XmlElement(ElementName = "Body", IsNullable = true)] + public string MessageBody; + } +} \ No newline at end of file diff --git a/src/Schema/KeyValuePairOfStringString.cs b/src/Schema/KeyValuePairOfStringString.cs new file mode 100644 index 0000000..7895231 --- /dev/null +++ b/src/Schema/KeyValuePairOfStringString.cs @@ -0,0 +1,13 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema +{ + [Serializable] + public class KeyValuePairOfStringString + { + [XmlElement(ElementName = "Key")] + public string? Key { get; set; } + [XmlElement(ElementName = "Value")] + public string? Value { get; set; } + } +} diff --git a/src/Schema/Message.cs b/src/Schema/Message.cs new file mode 100644 index 0000000..d423e2f --- /dev/null +++ b/src/Schema/Message.cs @@ -0,0 +1,39 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema +{ + [XmlRoot(ElementName = "Message")] + [Serializable] + public class Message + { + [XmlElement(ElementName = "MessageID")] + public int? MessageID; + + [XmlElement(ElementName = "Creator")] + public string Creator; + + [XmlElement(ElementName = "MessageLevel")] + public MessageLevel MessageLevel; + + [XmlElement(ElementName = "MessageType")] + public MessageType MessageType; + + [XmlElement(ElementName = "Content", IsNullable = true)] + public string? Content; + + [XmlElement(ElementName = "ReplyToMessageID", IsNullable = true)] + public int? ReplyToMessageID; + + [XmlElement(ElementName = "CreateTime")] + public DateTime CreateTime; + + [XmlElement(ElementName = "UpdateDate", IsNullable = true)] + public DateTime? UpdateDate; + + [XmlElement(ElementName = "ConversationID")] + public int ConversationID; + + [XmlElement(ElementName = "DisplayAttribute", IsNullable = true)] + public string? DisplayAttribute; + } +} \ No newline at end of file diff --git a/src/Services/MessagingService.cs b/src/Services/MessagingService.cs new file mode 100644 index 0000000..5c9c3ce --- /dev/null +++ b/src/Services/MessagingService.cs @@ -0,0 +1,237 @@ +using System; +using sodoff.Model; +using sodoff.Schema; +using sodoff.Util; +using ZstdSharp.Unsafe; + +namespace sodoff.Services; + +public class MessagingService +{ + private readonly DBContext ctx; + public MessagingService(DBContext ctx) + { + this.ctx = ctx; + } + + public Model.Message AddMessageToViking(Viking viking, Viking toViking, MessageType messageType, MessageTypeID messageTypeID, MessageLevel messageLevel, string data, string memberMessage = "", string nonMemberMessage = "", bool IsNew = true, bool IsDeleted = false, bool isReply = false, int parentMessageId = 0) + { + // get execution UTC timestamp + DateTime now = DateTime.UtcNow; + + // for generating ConversationId and QueueId + Random rnd = new Random(); + + // construct message + Model.Message message = new Model.Message + { + Viking = viking, + VikingId = viking.Id, + ToViking = toViking, + ToVikingId = toViking.Id, + QueueID = rnd.Next(1000, 9999), + ConversationID = rnd.Next(1000, 9999), + MessageType = messageType, + MessageTypeID = messageTypeID, + MessageLevel = messageLevel, + Data = data, + MemberMessage = memberMessage, + NonMemberMessage = nonMemberMessage, + CreatedAt = now, + LastUpdatedAt = now, + IsDeleted = IsDeleted, + IsNew = IsNew + }; + + if (isReply) + { + // get message this is in reply to + Model.Message? messageToReplyTo = ctx.Messages.FirstOrDefault(e => e.Id == parentMessageId); + + if (messageToReplyTo != null) + { + message.ParentMessage = messageToReplyTo; + message.ParentMessageId = messageToReplyTo.Id; + } else throw new InvalidOperationException("Tried To Reply To A Message That Doesn't Exist"); + } + + // add message to database + ctx.Messages.Add(message); + ctx.SaveChanges(); + + // return constructed message + return message; + } + + public void RemoveMessage(int id) + { + // find message + Model.Message? message = ctx.Messages.FirstOrDefault(e => e.Id == id); + + if (message != null) + { + // remove it + ctx.Messages.Remove(message); + ctx.SaveChanges(); + } else throw new InvalidOperationException("Tried To Delete A Message That Doesn't Exist"); + } + + public Model.Message? UpdateMessage(int id, bool isNew, bool IsDeleted) + { + // get execution UTC timestamp + DateTime now = DateTime.UtcNow; + + // find message + Model.Message? message = ctx.Messages.FirstOrDefault(e => e.Id == id); + + if (message != null) + { + // set params (TODO - figure out what else the clients update in messages) + message.IsNew = isNew; + message.IsDeleted = IsDeleted; + message.LastUpdatedAt = now; + + return message; + } else return null; + } + + public ArrayOfCombinedListMessage ConstructCombinedMessageArray(Viking viking) + { + // get all messages in viking board + List messages = ctx.Messages.Where(e => e.ToVikingId == viking.Id) + .ToList(); + + List combinedListMessages = new List(); + ArrayOfCombinedListMessage response = new ArrayOfCombinedListMessage(); + + foreach(var message in messages) + { + if (message.IsDeleted) + { + ctx.Messages.Remove(message); + continue; + } + + Viking? msgAuthor = ctx.Vikings.FirstOrDefault(e => e.Id == message.VikingId) ?? new Viking(); + + // construct a CombinedListMessage based on Model.Message + CombinedListMessage clm = new CombinedListMessage + { + MessageType = (int?)message.MessageType ?? 0, + MessageDate = message.CreatedAt + }; + + // if type id is messaging, use Schema.Message, otherwise treat it as Schema.MessageInfo + if (message.MessageTypeID == MessageTypeID.Messaging) + { + clm.MessageBody = XmlUtil.SerializeXml(new Schema.Message + { + MessageID = message.Id, + ConversationID = message.ConversationID ?? 0, + ReplyToMessageID = message.ParentMessageId, + Creator = msgAuthor.Uid.ToString() ?? "Ghost", + CreateTime = message.CreatedAt, + UpdateDate = message.LastUpdatedAt, + MessageType = message.MessageType.Value, + MessageLevel = message.MessageLevel, + Content = message.Data ?? "No Data Found In This Message. This Might Be An Error.", + DisplayAttribute = "C=White" // still having it always white :) + }); + } else if (message.Data.StartsWith("(message.Data); + string data = ""; + List pairList = new List(); + + for(int i = 0; i < arrayOfKVP.KeyValuePairOfStringString.Length; i++) + { + pairList.Add(arrayOfKVP.KeyValuePairOfStringString[i]); + } + + foreach(KeyValuePairOfStringString pair in pairList) + { + data = data + $"[[{pair.Key}]]=[[{pair.Value}]]"; + } + + clm.MessageBody = XmlUtil.SerializeXml(new MessageInfo + { + UserMessageQueueID = message.QueueID, + MessageID = message.Id, + MessageTypeID = (int?)message.MessageTypeID, + FromUserID = msgAuthor.Uid.ToString() ?? "NotFound", + MemberMessage = message.MemberMessage ?? "NoData", + NonMemberMessage = message.NonMemberMessage ?? "NoData", + Data = data + }); + + } else + { + clm.MessageBody = XmlUtil.SerializeXml(new MessageInfo + { + UserMessageQueueID = message.QueueID, + MessageID = message.Id, + MessageTypeID = (int?)message.MessageTypeID, + FromUserID = msgAuthor.Uid.ToString() ?? "NotFound", + MemberMessage = message.MemberMessage ?? "NoData", + NonMemberMessage = message.NonMemberMessage ?? "NoData", + Data = message.Data + }); + } + + // add clm to list + combinedListMessages.Add(clm); + } + + // save any changes made to db + ctx.SaveChanges(); + + // sort messages by newest first + combinedListMessages = combinedListMessages.OrderBy(e => e.MessageDate).ToList(); + + // add list as array to response + response.CombinedListMessage = combinedListMessages.ToArray(); + + // return response + return response; + } + + public ArrayOfMessageInfo ConstructUserMessageInfoArray(Viking viking, bool showOldMessages, bool showDeletedMessages) + { + // get execution UTC timestamp + DateTime now = DateTime.UtcNow; + + // get all recent messages + List messages = ctx.Messages.Where(e => e.ToVikingId == viking.Id) + .OrderBy(e => e.CreatedAt) + .OrderBy(e => e.QueueID) + .ToList(); + + List messageInfos = new List(0); + ArrayOfMessageInfo response = new ArrayOfMessageInfo(); + + foreach(var message in messages) + { + Viking? msgAuthor = ctx.Vikings.FirstOrDefault(e => e.Id == message.VikingId) ?? new Viking(); + + if(!showOldMessages && message.IsNew && DateTime.Compare(message.CreatedAt, now.AddMinutes(30)) > 0 || message.IsDeleted) continue; // sometimes clients won't set IsNew flag when updating messages, so do not add messages more than 30 minutes old to response + if(!message.IsNew && !showOldMessages) continue; + messageInfos.Add(new MessageInfo + { + MessageID = message.Id, + UserMessageQueueID = message.QueueID, + FromUserID = msgAuthor.Uid.ToString() ?? "NotFound", + MessageTypeID = (int?)message.MessageTypeID, + Data = message.Data ?? "NoData", + MemberMessage = message.MemberMessage ?? "NoMessage", + NonMemberMessage = message.NonMemberMessage ?? "NoMessage" + }); + } + + // add list as array to response + response.MessageInfo = messageInfos.ToArray(); + + // return response + return response; + } +}