Un-switch packet handlers.
This commit is contained in:
parent
ea56af0210
commit
c8a589c1c1
6 changed files with 345 additions and 219 deletions
27
SharpChat/ChatPacketHandlerContext.cs
Normal file
27
SharpChat/ChatPacketHandlerContext.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
|
||||
namespace SharpChat {
|
||||
public class ChatPacketHandlerContext {
|
||||
public string Text { get; }
|
||||
public ChatContext Chat { get; }
|
||||
public ChatUserSession Session { get; }
|
||||
|
||||
public ChatPacketHandlerContext(
|
||||
string text,
|
||||
ChatContext chat,
|
||||
ChatUserSession session
|
||||
) {
|
||||
Text = text ?? throw new ArgumentNullException(nameof(text));
|
||||
Chat = chat ?? throw new ArgumentNullException(nameof(chat));
|
||||
Session = session ?? throw new ArgumentNullException(nameof(session));
|
||||
}
|
||||
|
||||
public bool CheckPacketId(string packetId) {
|
||||
return Text == packetId || Text.StartsWith(packetId + '\t');
|
||||
}
|
||||
|
||||
public string[] SplitText(int expect) {
|
||||
return Text.Split('\t', expect + 1);
|
||||
}
|
||||
}
|
||||
}
|
6
SharpChat/IChatPacketHandler.cs
Normal file
6
SharpChat/IChatPacketHandler.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace SharpChat {
|
||||
public interface IChatPacketHandler {
|
||||
bool IsMatch(ChatPacketHandlerContext ctx);
|
||||
void Handle(ChatPacketHandlerContext ctx);
|
||||
}
|
||||
}
|
138
SharpChat/PacketHandlers/AuthHandler.cs
Normal file
138
SharpChat/PacketHandlers/AuthHandler.cs
Normal file
|
@ -0,0 +1,138 @@
|
|||
using SharpChat.Config;
|
||||
using SharpChat.Misuzu;
|
||||
using SharpChat.Packet;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharpChat.PacketHandlers {
|
||||
public class AuthHandler : IChatPacketHandler {
|
||||
private readonly MisuzuClient Misuzu;
|
||||
private readonly ChatChannel DefaultChannel;
|
||||
private readonly CachedValue<int> MaxMessageLength;
|
||||
private readonly CachedValue<int> MaxConnections;
|
||||
|
||||
public AuthHandler(
|
||||
MisuzuClient msz,
|
||||
ChatChannel defaultChannel,
|
||||
CachedValue<int> maxMsgLength,
|
||||
CachedValue<int> maxConns
|
||||
) {
|
||||
Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
|
||||
DefaultChannel = defaultChannel ?? throw new ArgumentNullException(nameof(defaultChannel));
|
||||
MaxMessageLength = maxMsgLength ?? throw new ArgumentNullException(nameof(maxMsgLength));
|
||||
MaxConnections = maxConns ?? throw new ArgumentNullException(nameof(maxConns));
|
||||
}
|
||||
|
||||
public bool IsMatch(ChatPacketHandlerContext ctx) {
|
||||
return ctx.CheckPacketId("1");
|
||||
}
|
||||
|
||||
public void Handle(ChatPacketHandlerContext ctx) {
|
||||
string[] args = ctx.SplitText(3);
|
||||
|
||||
string authMethod = args.ElementAtOrDefault(1);
|
||||
if(string.IsNullOrWhiteSpace(authMethod)) {
|
||||
ctx.Session.Send(new AuthFailPacket(AuthFailReason.AuthInvalid));
|
||||
ctx.Session.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
string authToken = args.ElementAtOrDefault(2);
|
||||
if(string.IsNullOrWhiteSpace(authToken)) {
|
||||
ctx.Session.Send(new AuthFailPacket(AuthFailReason.AuthInvalid));
|
||||
ctx.Session.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if(authMethod.All(c => c is >= '0' and <= '9') && authToken.Contains(':')) {
|
||||
string[] tokenParts = authToken.Split(':', 2);
|
||||
authMethod = tokenParts[0];
|
||||
authToken = tokenParts[1];
|
||||
}
|
||||
|
||||
Task.Run(async () => {
|
||||
MisuzuAuthInfo fai;
|
||||
string ipAddr = ctx.Session.RemoteAddress.ToString();
|
||||
|
||||
try {
|
||||
fai = await Misuzu.AuthVerifyAsync(authMethod, authToken, ipAddr);
|
||||
} catch(Exception ex) {
|
||||
Logger.Write($"<{ctx.Session.Id}> Failed to authenticate: {ex}");
|
||||
ctx.Session.Send(new AuthFailPacket(AuthFailReason.AuthInvalid));
|
||||
ctx.Session.Dispose();
|
||||
#if DEBUG
|
||||
throw;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
if(!fai.Success) {
|
||||
Logger.Debug($"<{ctx.Session.Id}> Auth fail: {fai.Reason}");
|
||||
ctx.Session.Send(new AuthFailPacket(AuthFailReason.AuthInvalid));
|
||||
ctx.Session.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
MisuzuBanInfo fbi;
|
||||
try {
|
||||
fbi = await Misuzu.CheckBanAsync(fai.UserId.ToString(), ipAddr);
|
||||
} catch(Exception ex) {
|
||||
Logger.Write($"<{ctx.Session.Id}> Failed auth ban check: {ex}");
|
||||
ctx.Session.Send(new AuthFailPacket(AuthFailReason.AuthInvalid));
|
||||
ctx.Session.Dispose();
|
||||
#if DEBUG
|
||||
throw;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
if(fbi.IsBanned && !fbi.HasExpired) {
|
||||
Logger.Write($"<{ctx.Session.Id}> User is banned.");
|
||||
ctx.Session.Send(new AuthFailPacket(AuthFailReason.Banned, fbi));
|
||||
ctx.Session.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
lock(ctx.Chat.UsersAccess) {
|
||||
ChatUser aUser = ctx.Chat.Users.FirstOrDefault(u => u.UserId == fai.UserId);
|
||||
|
||||
if(aUser == null)
|
||||
aUser = new ChatUser(fai);
|
||||
else {
|
||||
aUser.ApplyAuth(fai);
|
||||
aUser.Channel?.Send(new UserUpdatePacket(aUser));
|
||||
}
|
||||
|
||||
// Enforce a maximum amount of connections per user
|
||||
if(aUser.SessionCount >= MaxConnections) {
|
||||
ctx.Session.Send(new AuthFailPacket(AuthFailReason.MaxSessions));
|
||||
ctx.Session.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
// Bumping the ping to prevent upgrading
|
||||
ctx.Session.BumpPing();
|
||||
|
||||
aUser.AddSession(ctx.Session);
|
||||
|
||||
ctx.Session.Send(new LegacyCommandResponse(LCR.WELCOME, false, $"Welcome to Flashii Chat, {aUser.Username}!"));
|
||||
|
||||
if(File.Exists("welcome.txt")) {
|
||||
IEnumerable<string> lines = File.ReadAllLines("welcome.txt").Where(x => !string.IsNullOrWhiteSpace(x));
|
||||
string line = lines.ElementAtOrDefault(RNG.Next(lines.Count()));
|
||||
|
||||
if(!string.IsNullOrWhiteSpace(line))
|
||||
ctx.Session.Send(new LegacyCommandResponse(LCR.WELCOME, false, line));
|
||||
}
|
||||
|
||||
ctx.Chat.HandleJoin(aUser, DefaultChannel, ctx.Session, MaxMessageLength);
|
||||
}
|
||||
}).Wait();
|
||||
}
|
||||
}
|
||||
}
|
51
SharpChat/PacketHandlers/PingHandler.cs
Normal file
51
SharpChat/PacketHandlers/PingHandler.cs
Normal file
|
@ -0,0 +1,51 @@
|
|||
using SharpChat.Misuzu;
|
||||
using SharpChat.Packet;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharpChat.PacketHandlers {
|
||||
public class PingHandler : IChatPacketHandler {
|
||||
private readonly MisuzuClient Misuzu;
|
||||
|
||||
private readonly object BumpAccess = new();
|
||||
private readonly TimeSpan BumpInterval = TimeSpan.FromMinutes(1);
|
||||
private DateTimeOffset LastBump = DateTimeOffset.MinValue;
|
||||
|
||||
public PingHandler(MisuzuClient msz) {
|
||||
Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
|
||||
}
|
||||
|
||||
public bool IsMatch(ChatPacketHandlerContext ctx) {
|
||||
return ctx.CheckPacketId("0");
|
||||
}
|
||||
|
||||
public void Handle(ChatPacketHandlerContext ctx) {
|
||||
string[] parts = ctx.SplitText(2);
|
||||
|
||||
if(!int.TryParse(parts.FirstOrDefault(), out int pTime))
|
||||
return;
|
||||
|
||||
ctx.Session.BumpPing();
|
||||
ctx.Session.Send(new PongPacket(ctx.Session.LastPing));
|
||||
|
||||
lock(BumpAccess) {
|
||||
if(LastBump < DateTimeOffset.UtcNow - BumpInterval) {
|
||||
(string, string)[] bumpList;
|
||||
lock(ctx.Chat.UsersAccess)
|
||||
bumpList = ctx.Chat.Users
|
||||
.Where(u => u.HasSessions && u.Status == ChatUserStatus.Online)
|
||||
.Select(u => (u.UserId.ToString(), u.RemoteAddresses.FirstOrDefault()?.ToString() ?? string.Empty))
|
||||
.ToArray();
|
||||
|
||||
if(bumpList.Any())
|
||||
Task.Run(async () => {
|
||||
await Misuzu.BumpUsersOnlineAsync(bumpList);
|
||||
}).Wait();
|
||||
|
||||
LastBump = DateTimeOffset.UtcNow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
104
SharpChat/PacketHandlers/SendMessageHandler.cs
Normal file
104
SharpChat/PacketHandlers/SendMessageHandler.cs
Normal file
|
@ -0,0 +1,104 @@
|
|||
using SharpChat.Commands;
|
||||
using SharpChat.Config;
|
||||
using SharpChat.Events;
|
||||
using SharpChat.Packet;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace SharpChat.PacketHandlers {
|
||||
public class SendMessageHandler : IChatPacketHandler {
|
||||
private readonly CachedValue<int> MaxMessageLength;
|
||||
|
||||
private List<IChatCommand> Commands { get; } = new();
|
||||
|
||||
public SendMessageHandler(CachedValue<int> maxMsgLength) {
|
||||
MaxMessageLength = maxMsgLength ?? throw new ArgumentNullException(nameof(maxMsgLength));
|
||||
}
|
||||
|
||||
public void AddCommand(IChatCommand command) {
|
||||
Commands.Add(command ?? throw new ArgumentNullException(nameof(command)));
|
||||
}
|
||||
|
||||
public void AddCommands(IEnumerable<IChatCommand> commands) {
|
||||
Commands.AddRange(commands ?? throw new ArgumentNullException(nameof(commands)));
|
||||
}
|
||||
|
||||
public bool IsMatch(ChatPacketHandlerContext ctx) {
|
||||
return ctx.CheckPacketId("2");
|
||||
}
|
||||
|
||||
public void Handle(ChatPacketHandlerContext ctx) {
|
||||
string[] args = ctx.SplitText(3);
|
||||
|
||||
ChatUser user = ctx.Session.User;
|
||||
|
||||
// No longer concats everything after index 1 with \t, no previous implementation did that either
|
||||
string messageText = args.ElementAtOrDefault(2);
|
||||
|
||||
if(user == null || !user.Can(ChatUserPermissions.SendMessage) || 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)
|
||||
return;
|
||||
ChatChannel channel = user.CurrentChannel;
|
||||
|
||||
if(channel == null
|
||||
|| !user.InChannel(channel)
|
||||
|| (user.IsSilenced && !user.Can(ChatUserPermissions.SilenceUser)))
|
||||
return;
|
||||
|
||||
if(user.Status != ChatUserStatus.Online) {
|
||||
user.Status = ChatUserStatus.Online;
|
||||
channel.Send(new UserUpdatePacket(user));
|
||||
}
|
||||
|
||||
int maxMsgLength = MaxMessageLength;
|
||||
if(messageText.Length > maxMsgLength)
|
||||
messageText = messageText[..maxMsgLength];
|
||||
|
||||
messageText = messageText.Trim();
|
||||
|
||||
#if DEBUG
|
||||
Logger.Write($"<{ctx.Session.Id} {user.Username}> {messageText}");
|
||||
#endif
|
||||
|
||||
IChatMessage message = null;
|
||||
|
||||
if(messageText.StartsWith("/")) {
|
||||
ChatCommandContext context = new(messageText, ctx.Chat, user, ctx.Session, channel);
|
||||
|
||||
IChatCommand command = null;
|
||||
|
||||
foreach(IChatCommand cmd in Commands)
|
||||
if(cmd.IsMatch(context)) {
|
||||
command = cmd;
|
||||
break;
|
||||
}
|
||||
|
||||
if(command != null) {
|
||||
if(command is ActionCommand actionCommand)
|
||||
message = actionCommand.ActionDispatch(context);
|
||||
else {
|
||||
command.Dispatch(context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
message ??= new ChatMessage {
|
||||
Target = channel,
|
||||
TargetName = channel.TargetName,
|
||||
DateTime = DateTimeOffset.UtcNow,
|
||||
Sender = user,
|
||||
Text = messageText,
|
||||
};
|
||||
|
||||
lock(ctx.Chat.EventsAccess) {
|
||||
ctx.Chat.Events.AddEvent(message);
|
||||
channel.Send(new ChatMessageAddPacket(message));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +1,12 @@
|
|||
using Fleck;
|
||||
using SharpChat.Commands;
|
||||
using SharpChat.Config;
|
||||
using SharpChat.Events;
|
||||
using SharpChat.EventStorage;
|
||||
using SharpChat.Misuzu;
|
||||
using SharpChat.Packet;
|
||||
using SharpChat.PacketHandlers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
|
@ -39,7 +38,9 @@ namespace SharpChat {
|
|||
private readonly CachedValue<int> MaxConnections;
|
||||
private readonly CachedValue<int> FloodKickLength;
|
||||
|
||||
private List<IChatCommand> Commands { get; } = new();
|
||||
private readonly List<IChatPacketHandler> GuestHandlers = new();
|
||||
private readonly List<IChatPacketHandler> AuthedHandlers = new();
|
||||
private readonly SendMessageHandler SendMessageHandler;
|
||||
|
||||
private bool IsShuttingDown = false;
|
||||
|
||||
|
@ -76,7 +77,14 @@ namespace SharpChat {
|
|||
DefaultChannel ??= channelInfo;
|
||||
}
|
||||
|
||||
Commands.AddRange(new IChatCommand[] {
|
||||
GuestHandlers.Add(new AuthHandler(Misuzu, DefaultChannel, MaxMessageLength, MaxConnections));
|
||||
|
||||
AuthedHandlers.AddRange(new IChatPacketHandler[] {
|
||||
new PingHandler(Misuzu),
|
||||
SendMessageHandler = new SendMessageHandler(MaxMessageLength),
|
||||
});
|
||||
|
||||
SendMessageHandler.AddCommands(new IChatCommand[] {
|
||||
new AFKCommand(),
|
||||
new NickCommand(),
|
||||
new WhisperCommand(),
|
||||
|
@ -104,7 +112,7 @@ namespace SharpChat {
|
|||
|
||||
public void Listen(ManualResetEvent waitHandle) {
|
||||
if(waitHandle != null)
|
||||
Commands.Add(new ShutdownRestartCommand(waitHandle, () => !IsShuttingDown && (IsShuttingDown = true)));
|
||||
SendMessageHandler.AddCommand(new ShutdownRestartCommand(waitHandle, () => !IsShuttingDown && (IsShuttingDown = true)));
|
||||
|
||||
Server.Start(sock => {
|
||||
if(IsShuttingDown || IsDisposed) {
|
||||
|
@ -171,10 +179,6 @@ namespace SharpChat {
|
|||
Context.Update();
|
||||
}
|
||||
|
||||
private readonly object BumpAccess = new();
|
||||
private readonly TimeSpan BumpInterval = TimeSpan.FromMinutes(1);
|
||||
private DateTimeOffset LastBump = DateTimeOffset.MinValue;
|
||||
|
||||
private void OnMessage(IWebSocketConnection conn, string msg) {
|
||||
Context.Update();
|
||||
|
||||
|
@ -187,6 +191,7 @@ namespace SharpChat {
|
|||
return;
|
||||
}
|
||||
|
||||
// this doesn't affect non-authed connections?????
|
||||
if(sess.User is not null && sess.User.HasFloodProtection) {
|
||||
sess.User.RateLimiter.AddTimePoint();
|
||||
|
||||
|
@ -208,217 +213,12 @@ namespace SharpChat {
|
|||
sess.User.Send(new FloodWarningPacket());
|
||||
}
|
||||
|
||||
string[] args = msg.Split('\t');
|
||||
if(args.Length < 1)
|
||||
return;
|
||||
ChatPacketHandlerContext context = new(msg, Context, sess);
|
||||
IChatPacketHandler handler = sess.User is null
|
||||
? GuestHandlers.FirstOrDefault(h => h.IsMatch(context))
|
||||
: AuthedHandlers.FirstOrDefault(h => h.IsMatch(context));
|
||||
|
||||
switch(args[0]) {
|
||||
case "0":
|
||||
if(!int.TryParse(args[1], out int pTime))
|
||||
break;
|
||||
|
||||
sess.BumpPing();
|
||||
sess.Send(new PongPacket(sess.LastPing));
|
||||
|
||||
lock(BumpAccess) {
|
||||
if(LastBump < DateTimeOffset.UtcNow - BumpInterval) {
|
||||
(string, string)[] bumpList;
|
||||
lock(Context.UsersAccess)
|
||||
bumpList = Context.Users
|
||||
.Where(u => u.HasSessions && u.Status == ChatUserStatus.Online)
|
||||
.Select(u => (u.UserId.ToString(), u.RemoteAddresses.FirstOrDefault()?.ToString() ?? string.Empty))
|
||||
.ToArray();
|
||||
|
||||
if(bumpList.Any())
|
||||
Task.Run(async () => {
|
||||
await Misuzu.BumpUsersOnlineAsync(bumpList);
|
||||
}).Wait();
|
||||
|
||||
LastBump = DateTimeOffset.UtcNow;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "1":
|
||||
if(sess.User != null)
|
||||
break;
|
||||
|
||||
string authMethod = args.ElementAtOrDefault(1);
|
||||
if(string.IsNullOrWhiteSpace(authMethod)) {
|
||||
sess.Send(new AuthFailPacket(AuthFailReason.AuthInvalid));
|
||||
sess.Dispose();
|
||||
break;
|
||||
}
|
||||
|
||||
string authToken = args.ElementAtOrDefault(2);
|
||||
if(string.IsNullOrWhiteSpace(authToken)) {
|
||||
sess.Send(new AuthFailPacket(AuthFailReason.AuthInvalid));
|
||||
sess.Dispose();
|
||||
break;
|
||||
}
|
||||
|
||||
if(authMethod.All(c => c is >= '0' and <= '9') && authToken.Contains(':')) {
|
||||
string[] tokenParts = authToken.Split(':', 2);
|
||||
authMethod = tokenParts[0];
|
||||
authToken = tokenParts[1];
|
||||
}
|
||||
|
||||
Task.Run(async () => {
|
||||
MisuzuAuthInfo fai;
|
||||
string ipAddr = sess.RemoteAddress.ToString();
|
||||
|
||||
try {
|
||||
fai = await Misuzu.AuthVerifyAsync(authMethod, authToken, ipAddr);
|
||||
} catch(Exception ex) {
|
||||
Logger.Write($"<{sess.Id}> Failed to authenticate: {ex}");
|
||||
sess.Send(new AuthFailPacket(AuthFailReason.AuthInvalid));
|
||||
sess.Dispose();
|
||||
#if DEBUG
|
||||
throw;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
if(!fai.Success) {
|
||||
Logger.Debug($"<{sess.Id}> Auth fail: {fai.Reason}");
|
||||
sess.Send(new AuthFailPacket(AuthFailReason.AuthInvalid));
|
||||
sess.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
MisuzuBanInfo fbi;
|
||||
try {
|
||||
fbi = await Misuzu.CheckBanAsync(fai.UserId.ToString(), ipAddr);
|
||||
} catch(Exception ex) {
|
||||
Logger.Write($"<{sess.Id}> Failed auth ban check: {ex}");
|
||||
sess.Send(new AuthFailPacket(AuthFailReason.AuthInvalid));
|
||||
sess.Dispose();
|
||||
#if DEBUG
|
||||
throw;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
if(fbi.IsBanned && !fbi.HasExpired) {
|
||||
Logger.Write($"<{sess.Id}> User is banned.");
|
||||
sess.Send(new AuthFailPacket(AuthFailReason.Banned, fbi));
|
||||
sess.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
lock(Context.UsersAccess) {
|
||||
ChatUser aUser = Context.Users.FirstOrDefault(u => u.UserId == fai.UserId);
|
||||
|
||||
if(aUser == null)
|
||||
aUser = new ChatUser(fai);
|
||||
else {
|
||||
aUser.ApplyAuth(fai);
|
||||
aUser.Channel?.Send(new UserUpdatePacket(aUser));
|
||||
}
|
||||
|
||||
// Enforce a maximum amount of connections per user
|
||||
if(aUser.SessionCount >= MaxConnections) {
|
||||
sess.Send(new AuthFailPacket(AuthFailReason.MaxSessions));
|
||||
sess.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
// Bumping the ping to prevent upgrading
|
||||
sess.BumpPing();
|
||||
|
||||
aUser.AddSession(sess);
|
||||
|
||||
sess.Send(new LegacyCommandResponse(LCR.WELCOME, false, $"Welcome to Flashii Chat, {aUser.Username}!"));
|
||||
|
||||
if(File.Exists("welcome.txt")) {
|
||||
IEnumerable<string> lines = File.ReadAllLines("welcome.txt").Where(x => !string.IsNullOrWhiteSpace(x));
|
||||
string line = lines.ElementAtOrDefault(RNG.Next(lines.Count()));
|
||||
|
||||
if(!string.IsNullOrWhiteSpace(line))
|
||||
sess.Send(new LegacyCommandResponse(LCR.WELCOME, false, line));
|
||||
}
|
||||
|
||||
Context.HandleJoin(aUser, DefaultChannel, sess, MaxMessageLength);
|
||||
}
|
||||
}).Wait();
|
||||
break;
|
||||
|
||||
case "2":
|
||||
if(args.Length < 3)
|
||||
break;
|
||||
|
||||
ChatUser mUser = sess.User;
|
||||
|
||||
// No longer concats everything after index 1 with \t, no previous implementation did that either
|
||||
string messageText = args.ElementAtOrDefault(2);
|
||||
|
||||
if(mUser == null || !mUser.Can(ChatUserPermissions.SendMessage) || string.IsNullOrWhiteSpace(messageText))
|
||||
break;
|
||||
|
||||
// Extra validation step, not necessary at all but enforces proper formatting in SCv1.
|
||||
if(!long.TryParse(args[1], out long mUserId) || mUser.UserId != mUserId)
|
||||
break;
|
||||
ChatChannel mChannel = mUser.CurrentChannel;
|
||||
|
||||
if(mChannel == null
|
||||
|| !mUser.InChannel(mChannel)
|
||||
|| (mUser.IsSilenced && !mUser.Can(ChatUserPermissions.SilenceUser)))
|
||||
break;
|
||||
|
||||
if(mUser.Status != ChatUserStatus.Online) {
|
||||
mUser.Status = ChatUserStatus.Online;
|
||||
mChannel.Send(new UserUpdatePacket(mUser));
|
||||
}
|
||||
|
||||
int maxMsgLength = MaxMessageLength;
|
||||
if(messageText.Length > maxMsgLength)
|
||||
messageText = messageText[..maxMsgLength];
|
||||
|
||||
messageText = messageText.Trim();
|
||||
|
||||
#if DEBUG
|
||||
Logger.Write($"<{sess.Id} {mUser.Username}> {messageText}");
|
||||
#endif
|
||||
|
||||
IChatMessage message = null;
|
||||
|
||||
if(messageText.StartsWith("/")) {
|
||||
ChatCommandContext context = new(messageText, Context, mUser, sess, mChannel);
|
||||
|
||||
IChatCommand command = null;
|
||||
|
||||
foreach(IChatCommand cmd in Commands)
|
||||
if(cmd.IsMatch(context)) {
|
||||
command = cmd;
|
||||
break;
|
||||
}
|
||||
|
||||
if(command != null) {
|
||||
if(command is ActionCommand actionCommand)
|
||||
message = actionCommand.ActionDispatch(context);
|
||||
else {
|
||||
command.Dispatch(context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
message ??= new ChatMessage {
|
||||
Target = mChannel,
|
||||
TargetName = mChannel.TargetName,
|
||||
DateTime = DateTimeOffset.UtcNow,
|
||||
Sender = mUser,
|
||||
Text = messageText,
|
||||
};
|
||||
|
||||
lock(Context.EventsAccess) {
|
||||
Context.Events.AddEvent(message);
|
||||
mChannel.Send(new ChatMessageAddPacket(message));
|
||||
}
|
||||
break;
|
||||
}
|
||||
handler?.Handle(context);
|
||||
}
|
||||
|
||||
~SockChatServer() {
|
||||
|
|
Loading…
Reference in a new issue