using SharpChat.Channels; using SharpChat.Configuration; using SharpChat.Events; using SharpChat.Snowflake; using System.Globalization; using System.Text; namespace SharpChat.C2SPacketHandlers; public class SendMessageC2SPacketHandler( RandomSnowflake randomSnowflake, CachedValue<int> maxMsgLength ) : C2SPacketHandler { private readonly CachedValue<int> MaxMessageLength = maxMsgLength ?? throw new ArgumentNullException(nameof(maxMsgLength)); private List<ClientCommand> Commands { get; } = []; public void AddCommand(ClientCommand command) { Commands.Add(command ?? throw new ArgumentNullException(nameof(command))); } public void AddCommands(IEnumerable<ClientCommand> commands) { Commands.AddRange(commands ?? throw new ArgumentNullException(nameof(commands))); } public bool IsMatch(C2SPacketHandlerContext ctx) { return ctx.CheckPacketId("2"); } public async Task Handle(C2SPacketHandlerContext ctx) { string[] args = ctx.SplitText(3); User? user = ctx.Connection.User; string? messageText = args.ElementAtOrDefault(2); if(user?.Permissions.HasFlag(UserPermissions.SendMessage) != true || string.IsNullOrWhiteSpace(messageText)) return; // Extra validation step, not necessary at all but enforces proper formatting in SCv1. if(!long.TryParse(args[1], out long mUserId) || user.UserId != mUserId.ToString()) return; ctx.Chat.ContextAccess.Wait(); try { if(!ctx.Chat.UserLastChannel.TryGetValue(user.UserId, out Channel? channel) && (channel is null || !ctx.Chat.IsInChannel(user, channel))) return; if(user.Status != UserStatus.Online) await ctx.Chat.UpdateUser(user, status: UserStatus.Online); int maxMsgLength = MaxMessageLength; StringInfo messageTextInfo = new(messageText); if(Encoding.UTF8.GetByteCount(messageText) > (maxMsgLength * 10) || messageTextInfo.LengthInTextElements > maxMsgLength) messageText = messageTextInfo.SubstringByTextElements(0, Math.Min(messageTextInfo.LengthInTextElements, maxMsgLength)); messageText = messageText.Trim(); if(messageText.StartsWith('/')) { ClientCommandContext context = new(messageText, ctx.Chat, user, ctx.Connection, channel); foreach(ClientCommand cmd in Commands) if(cmd.IsMatch(context)) { await cmd.Dispatch(context); return; } } await ctx.Chat.DispatchEvent(new MessageCreateEvent( randomSnowflake.Next(), channel.Name, user.UserId, user.UserName, user.Colour, user.Rank, user.NickName, user.Permissions, DateTimeOffset.Now, messageText, false, false, false )); } finally { ctx.Chat.ContextAccess.Release(); } } }