le async has arrived
This commit is contained in:
parent
2eba089a21
commit
7bdf41a047
27 changed files with 282 additions and 309 deletions
SharpChat
C2SPacketHandler.cs
C2SPacketHandlers
ClientCommand.csClientCommands
AFKClientCommand.csActionClientCommand.csBanListClientCommand.csBroadcastClientCommand.csCreateChannelClientCommand.csDeleteChannelClientCommand.csDeleteMessageClientCommand.csJoinChannelClientCommand.csKickBanClientCommand.csNickClientCommand.csPardonAddressClientCommand.csPardonUserClientCommand.csPasswordChannelClientCommand.csRankChannelClientCommand.csRemoteAddressClientCommand.csShutdownRestartClientCommand.csWhisperClientCommand.csWhoClientCommand.cs
Connection.csContext.csProgram.csSockChatServer.cs
|
@ -1,6 +1,6 @@
|
|||
namespace SharpChat {
|
||||
namespace SharpChat {
|
||||
public interface C2SPacketHandler {
|
||||
bool IsMatch(C2SPacketHandlerContext ctx);
|
||||
void Handle(C2SPacketHandlerContext ctx);
|
||||
Task Handle(C2SPacketHandlerContext ctx);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,14 +19,14 @@ namespace SharpChat.C2SPacketHandlers {
|
|||
return ctx.CheckPacketId("1");
|
||||
}
|
||||
|
||||
public void Handle(C2SPacketHandlerContext ctx) {
|
||||
public async Task Handle(C2SPacketHandlerContext ctx) {
|
||||
string[] args = ctx.SplitText(3);
|
||||
|
||||
string? authMethod = args.ElementAtOrDefault(1);
|
||||
string? authToken = args.ElementAtOrDefault(2);
|
||||
|
||||
if(string.IsNullOrWhiteSpace(authMethod) || string.IsNullOrWhiteSpace(authToken)) {
|
||||
ctx.Connection.Send(new AuthFailS2CPacket(AuthFailS2CPacket.Reason.AuthInvalid));
|
||||
await ctx.Connection.Send(new AuthFailS2CPacket(AuthFailS2CPacket.Reason.AuthInvalid));
|
||||
ctx.Connection.Dispose();
|
||||
return;
|
||||
}
|
||||
|
@ -37,78 +37,76 @@ namespace SharpChat.C2SPacketHandlers {
|
|||
authToken = tokenParts[1];
|
||||
}
|
||||
|
||||
Task.Run(async () => {
|
||||
try {
|
||||
AuthResult authResult = await authClient.AuthVerifyAsync(
|
||||
ctx.Connection.RemoteAddress,
|
||||
authMethod,
|
||||
authToken
|
||||
);
|
||||
try {
|
||||
AuthResult authResult = await authClient.AuthVerifyAsync(
|
||||
ctx.Connection.RemoteAddress,
|
||||
authMethod,
|
||||
authToken
|
||||
);
|
||||
|
||||
BanInfo? banInfo = await bansClient.BanGetAsync(authResult.UserId, ctx.Connection.RemoteAddress);
|
||||
if(banInfo is not null) {
|
||||
Logger.Write($"<{ctx.Connection.Id}> User is banned.");
|
||||
ctx.Connection.Send(new AuthFailS2CPacket(AuthFailS2CPacket.Reason.Banned, banInfo.IsPermanent ? DateTimeOffset.MaxValue : banInfo.ExpiresAt));
|
||||
BanInfo? banInfo = await bansClient.BanGetAsync(authResult.UserId, ctx.Connection.RemoteAddress);
|
||||
if(banInfo is not null) {
|
||||
Logger.Write($"<{ctx.Connection.Id}> User is banned.");
|
||||
await ctx.Connection.Send(new AuthFailS2CPacket(AuthFailS2CPacket.Reason.Banned, banInfo.IsPermanent ? DateTimeOffset.MaxValue : banInfo.ExpiresAt));
|
||||
ctx.Connection.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
await ctx.Chat.ContextAccess.WaitAsync();
|
||||
try {
|
||||
User? user = ctx.Chat.Users.FirstOrDefault(u => u.UserId == authResult.UserId);
|
||||
|
||||
if(user == null)
|
||||
user = new User(
|
||||
authResult.UserId,
|
||||
authResult.UserName ?? $"({authResult.UserId})",
|
||||
authResult.UserColour,
|
||||
authResult.UserRank,
|
||||
authResult.UserPermissions
|
||||
);
|
||||
else
|
||||
await ctx.Chat.UpdateUser(
|
||||
user,
|
||||
userName: authResult.UserName ?? $"({authResult.UserId})",
|
||||
colour: authResult.UserColour,
|
||||
rank: authResult.UserRank,
|
||||
perms: authResult.UserPermissions
|
||||
);
|
||||
|
||||
// Enforce a maximum amount of connections per user
|
||||
if(ctx.Chat.Connections.Count(conn => conn.User == user) >= MaxConnections) {
|
||||
await ctx.Connection.Send(new AuthFailS2CPacket(AuthFailS2CPacket.Reason.MaxSessions));
|
||||
ctx.Connection.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
await ctx.Chat.ContextAccess.WaitAsync();
|
||||
try {
|
||||
User? user = ctx.Chat.Users.FirstOrDefault(u => u.UserId == authResult.UserId);
|
||||
ctx.Connection.BumpPing();
|
||||
ctx.Connection.User = user;
|
||||
await ctx.Connection.Send(new CommandResponseS2CPacket(0, LCR.WELCOME, false, $"Welcome to Flashii Chat, {user.UserName}!"));
|
||||
|
||||
if(user == null)
|
||||
user = new User(
|
||||
authResult.UserId,
|
||||
authResult.UserName ?? $"({authResult.UserId})",
|
||||
authResult.UserColour,
|
||||
authResult.UserRank,
|
||||
authResult.UserPermissions
|
||||
);
|
||||
else
|
||||
ctx.Chat.UpdateUser(
|
||||
user,
|
||||
userName: authResult.UserName ?? $"({authResult.UserId})",
|
||||
colour: authResult.UserColour,
|
||||
rank: authResult.UserRank,
|
||||
perms: authResult.UserPermissions
|
||||
);
|
||||
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()));
|
||||
|
||||
// Enforce a maximum amount of connections per user
|
||||
if(ctx.Chat.Connections.Count(conn => conn.User == user) >= MaxConnections) {
|
||||
ctx.Connection.Send(new AuthFailS2CPacket(AuthFailS2CPacket.Reason.MaxSessions));
|
||||
ctx.Connection.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.Connection.BumpPing();
|
||||
ctx.Connection.User = user;
|
||||
ctx.Connection.Send(new CommandResponseS2CPacket(0, LCR.WELCOME, false, $"Welcome to Flashii Chat, {user.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.Connection.Send(new CommandResponseS2CPacket(0, LCR.WELCOME, false, line));
|
||||
}
|
||||
|
||||
ctx.Chat.HandleJoin(user, DefaultChannel, ctx.Connection, MaxMessageLength);
|
||||
} finally {
|
||||
ctx.Chat.ContextAccess.Release();
|
||||
if(!string.IsNullOrWhiteSpace(line))
|
||||
await ctx.Connection.Send(new CommandResponseS2CPacket(0, LCR.WELCOME, false, line));
|
||||
}
|
||||
} catch(AuthFailedException ex) {
|
||||
Logger.Write($"<{ctx.Connection.Id}> Failed to authenticate: {ex}");
|
||||
ctx.Connection.Send(new AuthFailS2CPacket(AuthFailS2CPacket.Reason.AuthInvalid));
|
||||
ctx.Connection.Dispose();
|
||||
throw;
|
||||
} catch(Exception ex) {
|
||||
Logger.Write($"<{ctx.Connection.Id}> Failed to authenticate: {ex}");
|
||||
ctx.Connection.Send(new AuthFailS2CPacket(AuthFailS2CPacket.Reason.Exception));
|
||||
ctx.Connection.Dispose();
|
||||
throw;
|
||||
|
||||
await ctx.Chat.HandleJoin(user, DefaultChannel, ctx.Connection, MaxMessageLength);
|
||||
} finally {
|
||||
ctx.Chat.ContextAccess.Release();
|
||||
}
|
||||
}).Wait();
|
||||
} catch(AuthFailedException ex) {
|
||||
Logger.Write($"<{ctx.Connection.Id}> Failed to authenticate: {ex}");
|
||||
await ctx.Connection.Send(new AuthFailS2CPacket(AuthFailS2CPacket.Reason.AuthInvalid));
|
||||
ctx.Connection.Dispose();
|
||||
throw;
|
||||
} catch(Exception ex) {
|
||||
Logger.Write($"<{ctx.Connection.Id}> Failed to authenticate: {ex}");
|
||||
await ctx.Connection.Send(new AuthFailS2CPacket(AuthFailS2CPacket.Reason.Exception));
|
||||
ctx.Connection.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,14 +11,14 @@ namespace SharpChat.C2SPacketHandlers {
|
|||
return ctx.CheckPacketId("0");
|
||||
}
|
||||
|
||||
public void Handle(C2SPacketHandlerContext ctx) {
|
||||
public async Task Handle(C2SPacketHandlerContext ctx) {
|
||||
string[] parts = ctx.SplitText(2);
|
||||
|
||||
if(!int.TryParse(parts.FirstOrDefault(), out int pTime))
|
||||
return;
|
||||
|
||||
ctx.Connection.BumpPing();
|
||||
ctx.Connection.Send(new PongS2CPacket());
|
||||
await ctx.Connection.Send(new PongS2CPacket());
|
||||
|
||||
ctx.Chat.ContextAccess.Wait();
|
||||
try {
|
||||
|
@ -28,9 +28,7 @@ namespace SharpChat.C2SPacketHandlers {
|
|||
.Select(u => (ctx.Chat.GetRemoteAddresses(u).FirstOrDefault() ?? IPAddress.None, u.UserId))];
|
||||
|
||||
if(bumpList.Length > 0)
|
||||
Task.Run(async () => {
|
||||
await authClient.AuthBumpUsersOnlineAsync(bumpList);
|
||||
}).Wait();
|
||||
await authClient.AuthBumpUsersOnlineAsync(bumpList);
|
||||
|
||||
LastBump = DateTimeOffset.UtcNow;
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace SharpChat.C2SPacketHandlers {
|
|||
return ctx.CheckPacketId("2");
|
||||
}
|
||||
|
||||
public void Handle(C2SPacketHandlerContext ctx) {
|
||||
public async Task Handle(C2SPacketHandlerContext ctx) {
|
||||
string[] args = ctx.SplitText(3);
|
||||
|
||||
User? user = ctx.Connection.User;
|
||||
|
@ -45,7 +45,7 @@ namespace SharpChat.C2SPacketHandlers {
|
|||
return;
|
||||
|
||||
if(user.Status != UserStatus.Online)
|
||||
ctx.Chat.UpdateUser(user, status: UserStatus.Online);
|
||||
await ctx.Chat.UpdateUser(user, status: UserStatus.Online);
|
||||
|
||||
int maxMsgLength = MaxMessageLength;
|
||||
StringInfo messageTextInfo = new(messageText);
|
||||
|
@ -63,12 +63,12 @@ namespace SharpChat.C2SPacketHandlers {
|
|||
ClientCommandContext context = new(messageText, ctx.Chat, user, ctx.Connection, channel);
|
||||
foreach(ClientCommand cmd in Commands)
|
||||
if(cmd.IsMatch(context)) {
|
||||
cmd.Dispatch(context);
|
||||
await cmd.Dispatch(context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
||||
await ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
||||
randomSnowflake.Next(),
|
||||
channel.Name,
|
||||
user.UserId,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
namespace SharpChat {
|
||||
namespace SharpChat {
|
||||
public interface ClientCommand {
|
||||
bool IsMatch(ClientCommandContext ctx);
|
||||
void Dispatch(ClientCommandContext ctx);
|
||||
Task Dispatch(ClientCommandContext ctx);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System.Globalization;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
|
@ -11,7 +11,7 @@ namespace SharpChat.ClientCommands {
|
|||
return ctx.NameEquals("afk");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
public async Task Dispatch(ClientCommandContext ctx) {
|
||||
string? statusText = ctx.Args.FirstOrDefault();
|
||||
if(string.IsNullOrWhiteSpace(statusText))
|
||||
statusText = DEFAULT;
|
||||
|
@ -24,7 +24,7 @@ namespace SharpChat.ClientCommands {
|
|||
statusText = sti.SubstringByTextElements(0, Math.Min(sti.LengthInTextElements, MAX_GRAPHEMES)).Trim();
|
||||
}
|
||||
|
||||
ctx.Chat.UpdateUser(
|
||||
await ctx.Chat.UpdateUser(
|
||||
ctx.User,
|
||||
status: UserStatus.Away,
|
||||
statusText: statusText
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using SharpChat.Events;
|
||||
using SharpChat.Events;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class ActionClientCommand : ClientCommand {
|
||||
|
@ -7,7 +7,7 @@ namespace SharpChat.ClientCommands {
|
|||
|| ctx.NameEquals("me");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
public async Task Dispatch(ClientCommandContext ctx) {
|
||||
if(ctx.Args.Length < 1)
|
||||
return;
|
||||
|
||||
|
@ -15,7 +15,7 @@ namespace SharpChat.ClientCommands {
|
|||
if(string.IsNullOrWhiteSpace(actionStr))
|
||||
return;
|
||||
|
||||
ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
||||
await ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
||||
ctx.Chat.RandomSnowflake.Next(),
|
||||
ctx.Channel.Name,
|
||||
ctx.User.UserId,
|
||||
|
|
|
@ -8,25 +8,23 @@ namespace SharpChat.ClientCommands {
|
|||
|| ctx.NameEquals("banned");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
public async Task Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(!ctx.User.Can(UserPermissions.BanUser | UserPermissions.KickUser)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
Task.Run(async () => {
|
||||
try {
|
||||
BanInfo[] banInfos = await bansClient.BanGetListAsync();
|
||||
ctx.Chat.SendTo(ctx.User, new BanListS2CPacket(
|
||||
msgId,
|
||||
banInfos.Select(bi => new BanListS2CPacket.Entry(bi.Kind, bi.ToString()))
|
||||
));
|
||||
} catch(Exception) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.GENERIC_ERROR, true));
|
||||
}
|
||||
}).Wait();
|
||||
try {
|
||||
BanInfo[] banInfos = await bansClient.BanGetListAsync();
|
||||
await ctx.Chat.SendTo(ctx.User, new BanListS2CPacket(
|
||||
msgId,
|
||||
banInfos.Select(bi => new BanListS2CPacket.Entry(bi.Kind, bi.ToString()))
|
||||
));
|
||||
} catch(Exception) {
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.GENERIC_ERROR, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using SharpChat.Events;
|
||||
using SharpChat.Events;
|
||||
using SharpChat.S2CPackets;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
|
@ -8,15 +8,15 @@ namespace SharpChat.ClientCommands {
|
|||
|| ctx.NameEquals("broadcast");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
public async Task Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(!ctx.User.Can(UserPermissions.Broadcast)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
||||
await ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
||||
msgId,
|
||||
string.Empty,
|
||||
ctx.User.UserId,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using SharpChat.S2CPackets;
|
||||
using SharpChat.S2CPackets;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class CreateChannelClientCommand : ClientCommand {
|
||||
|
@ -6,11 +6,11 @@ namespace SharpChat.ClientCommands {
|
|||
return ctx.NameEquals("create");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
public async Task Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(!ctx.User.Can(UserPermissions.CreateChannel)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ namespace SharpChat.ClientCommands {
|
|||
|
||||
bool createChanHasHierarchy;
|
||||
if(ctx.Args.Length < 1 || (createChanHasHierarchy = firstArg.All(char.IsDigit) && ctx.Args.Length < 2)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -28,19 +28,19 @@ namespace SharpChat.ClientCommands {
|
|||
createChanHierarchy = 0;
|
||||
|
||||
if(createChanHierarchy > ctx.User.Rank) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.INSUFFICIENT_HIERARCHY));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.INSUFFICIENT_HIERARCHY));
|
||||
return;
|
||||
}
|
||||
|
||||
string createChanName = string.Join('_', ctx.Args.Skip(createChanHasHierarchy ? 1 : 0));
|
||||
|
||||
if(!Channel.CheckName(createChanName)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NAME_INVALID));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NAME_INVALID));
|
||||
return;
|
||||
}
|
||||
|
||||
if(ctx.Chat.Channels.Any(c => c.NameEquals(createChanName))) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_ALREADY_EXISTS, true, createChanName));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_ALREADY_EXISTS, true, createChanName));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -53,10 +53,10 @@ namespace SharpChat.ClientCommands {
|
|||
|
||||
ctx.Chat.Channels.Add(createChan);
|
||||
foreach(User ccu in ctx.Chat.Users.Where(u => u.Rank >= ctx.Channel.Rank))
|
||||
ctx.Chat.SendTo(ccu, new ChannelCreateS2CPacket(createChan.Name, createChan.HasPassword, createChan.IsTemporary));
|
||||
await ctx.Chat.SendTo(ccu, new ChannelCreateS2CPacket(createChan.Name, createChan.HasPassword, createChan.IsTemporary));
|
||||
|
||||
ctx.Chat.SwitchChannel(ctx.User, createChan, createChan.Password);
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_CREATED, false, createChan.Name));
|
||||
await ctx.Chat.SwitchChannel(ctx.User, createChan, createChan.Password);
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_CREATED, false, createChan.Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using SharpChat.S2CPackets;
|
||||
using SharpChat.S2CPackets;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class DeleteChannelClientCommand : ClientCommand {
|
||||
|
@ -9,11 +9,11 @@ namespace SharpChat.ClientCommands {
|
|||
);
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
public async Task Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(ctx.Args.Length < 1 || string.IsNullOrWhiteSpace(ctx.Args.FirstOrDefault())) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -21,17 +21,17 @@ namespace SharpChat.ClientCommands {
|
|||
Channel? delChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(delChanName));
|
||||
|
||||
if(delChan == null) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NOT_FOUND, true, delChanName));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NOT_FOUND, true, delChanName));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!ctx.User.Can(UserPermissions.DeleteChannel) && delChan.IsOwner(ctx.User)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_DELETE_FAILED, true, delChan.Name));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_DELETE_FAILED, true, delChan.Name));
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.Chat.RemoveChannel(delChan);
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_DELETED, false, delChan.Name));
|
||||
await ctx.Chat.RemoveChannel(delChan);
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_DELETED, false, delChan.Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using SharpChat.EventStorage;
|
||||
using SharpChat.EventStorage;
|
||||
using SharpChat.S2CPackets;
|
||||
|
||||
namespace SharpChat.ClientCommands
|
||||
|
@ -11,31 +11,31 @@ namespace SharpChat.ClientCommands
|
|||
);
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
public async Task Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
bool deleteAnyMessage = ctx.User.Can(UserPermissions.DeleteAnyMessage);
|
||||
|
||||
if(!deleteAnyMessage && !ctx.User.Can(UserPermissions.DeleteOwnMessage)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
string? firstArg = ctx.Args.FirstOrDefault();
|
||||
|
||||
if(string.IsNullOrWhiteSpace(firstArg) || !firstArg.All(char.IsDigit) || !long.TryParse(firstArg, out long delSeqId)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
StoredEventInfo? delMsg = ctx.Chat.Events.GetEvent(delSeqId);
|
||||
|
||||
if(delMsg?.Sender is null || delMsg.Sender.Rank > ctx.User.Rank || (!deleteAnyMessage && delMsg.Sender.UserId != ctx.User.UserId)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.MESSAGE_DELETE_ERROR));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.MESSAGE_DELETE_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.Chat.Events.RemoveEvent(delMsg);
|
||||
ctx.Chat.Send(new ChatMessageDeleteS2CPacket(delMsg.Id));
|
||||
await ctx.Chat.Send(new ChatMessageDeleteS2CPacket(delMsg.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using SharpChat.S2CPackets;
|
||||
using SharpChat.S2CPackets;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class JoinChannelClientCommand : ClientCommand {
|
||||
|
@ -6,18 +6,18 @@ namespace SharpChat.ClientCommands {
|
|||
return ctx.NameEquals("join");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
public async Task Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
string joinChanStr = ctx.Args.FirstOrDefault() ?? "Channel";
|
||||
Channel? joinChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(joinChanStr));
|
||||
|
||||
if(joinChan is null) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NOT_FOUND, true, joinChanStr));
|
||||
ctx.Chat.ForceChannel(ctx.User);
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NOT_FOUND, true, joinChanStr));
|
||||
await ctx.Chat.ForceChannel(ctx.User);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.Chat.SwitchChannel(ctx.User, joinChan, string.Join(' ', ctx.Args.Skip(1)));
|
||||
await ctx.Chat.SwitchChannel(ctx.User, joinChan, string.Join(' ', ctx.Args.Skip(1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,12 +9,12 @@ namespace SharpChat.ClientCommands {
|
|||
|| ctx.NameEquals("ban");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
public async Task Dispatch(ClientCommandContext ctx) {
|
||||
bool isBanning = ctx.NameEquals("ban");
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(!ctx.User.Can(isBanning ? UserPermissions.BanUser : UserPermissions.KickUser)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -24,19 +24,19 @@ namespace SharpChat.ClientCommands {
|
|||
User? banUser = null;
|
||||
|
||||
if(banUserTarget == null || (banUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(banUserTarget))) == null) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_FOUND, true, banUserTarget ?? "User"));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_FOUND, true, banUserTarget ?? "User"));
|
||||
return;
|
||||
}
|
||||
|
||||
if(banUser.Rank >= ctx.User.Rank && banUser != ctx.User) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.KICK_NOT_ALLOWED, true, banUser.LegacyName));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.KICK_NOT_ALLOWED, true, banUser.LegacyName));
|
||||
return;
|
||||
}
|
||||
|
||||
TimeSpan duration = isBanning ? TimeSpan.MaxValue : TimeSpan.Zero;
|
||||
if(!string.IsNullOrWhiteSpace(banDurationStr) && double.TryParse(banDurationStr, out double durationSeconds)) {
|
||||
if(durationSeconds < 0) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -45,31 +45,29 @@ namespace SharpChat.ClientCommands {
|
|||
}
|
||||
|
||||
if(duration <= TimeSpan.Zero) {
|
||||
ctx.Chat.BanUser(banUser, duration);
|
||||
await ctx.Chat.BanUser(banUser, duration);
|
||||
return;
|
||||
}
|
||||
|
||||
string banReason = string.Join(' ', ctx.Args.Skip(banReasonIndex));
|
||||
|
||||
Task.Run(async () => {
|
||||
BanInfo? banInfo = await bansClient.BanGetAsync(banUser.UserId);
|
||||
if(banInfo is not null) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.KICK_NOT_ALLOWED, true, banUser.LegacyName));
|
||||
return;
|
||||
}
|
||||
BanInfo? banInfo = await bansClient.BanGetAsync(banUser.UserId);
|
||||
if(banInfo is not null) {
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.KICK_NOT_ALLOWED, true, banUser.LegacyName));
|
||||
return;
|
||||
}
|
||||
|
||||
await bansClient.BanCreateAsync(
|
||||
BanKind.User,
|
||||
duration,
|
||||
ctx.Chat.GetRemoteAddresses(banUser).FirstOrDefault() ?? IPAddress.None,
|
||||
banUser.UserId,
|
||||
banReason,
|
||||
ctx.Connection.RemoteAddress,
|
||||
ctx.User.UserId
|
||||
);
|
||||
await bansClient.BanCreateAsync(
|
||||
BanKind.User,
|
||||
duration,
|
||||
ctx.Chat.GetRemoteAddresses(banUser).FirstOrDefault() ?? IPAddress.None,
|
||||
banUser.UserId,
|
||||
banReason,
|
||||
ctx.Connection.RemoteAddress,
|
||||
ctx.User.UserId
|
||||
);
|
||||
|
||||
ctx.Chat.BanUser(banUser, duration);
|
||||
}).Wait();
|
||||
await ctx.Chat.BanUser(banUser, duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,12 +11,12 @@ namespace SharpChat.ClientCommands {
|
|||
return ctx.NameEquals("nick");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
public async Task Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
bool setOthersNick = ctx.User.Can(UserPermissions.SetOthersNickname);
|
||||
|
||||
if(!setOthersNick && !ctx.User.Can(UserPermissions.SetOwnNickname)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ namespace SharpChat.ClientCommands {
|
|||
targetUser ??= ctx.User;
|
||||
|
||||
if(ctx.Args.Length < offset) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -52,12 +52,12 @@ namespace SharpChat.ClientCommands {
|
|||
}
|
||||
|
||||
if(!string.IsNullOrWhiteSpace(nickStr) && ctx.Chat.Users.Any(u => u.NameEquals(nickStr))) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.NAME_IN_USE, true, nickStr));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.NAME_IN_USE, true, nickStr));
|
||||
return;
|
||||
}
|
||||
|
||||
string? previousName = targetUser.UserId == ctx.User.UserId ? (targetUser.NickName ?? targetUser.UserName) : null;
|
||||
ctx.Chat.UpdateUser(targetUser, nickName: nickStr, silent: previousName == null);
|
||||
await ctx.Chat.UpdateUser(targetUser, nickName: nickStr, silent: previousName == null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,34 +9,32 @@ namespace SharpChat.ClientCommands {
|
|||
|| ctx.NameEquals("unbanip");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
public async Task Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(!ctx.User.Can(UserPermissions.BanUser | UserPermissions.KickUser)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
string? unbanAddrTarget = ctx.Args.FirstOrDefault();
|
||||
if(string.IsNullOrWhiteSpace(unbanAddrTarget) || !IPAddress.TryParse(unbanAddrTarget, out IPAddress? unbanAddr)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
unbanAddrTarget = unbanAddr.ToString();
|
||||
|
||||
Task.Run(async () => {
|
||||
BanInfo? banInfo = await bansClient.BanGetAsync(remoteAddr: unbanAddr);
|
||||
if(banInfo?.Kind != BanKind.IPAddress) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_BANNED, true, unbanAddrTarget));
|
||||
return;
|
||||
}
|
||||
BanInfo? banInfo = await bansClient.BanGetAsync(remoteAddr: unbanAddr);
|
||||
if(banInfo?.Kind != BanKind.IPAddress) {
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_BANNED, true, unbanAddrTarget));
|
||||
return;
|
||||
}
|
||||
|
||||
if(await bansClient.BanRevokeAsync(banInfo))
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_UNBANNED, false, unbanAddrTarget));
|
||||
else
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_BANNED, true, unbanAddrTarget));
|
||||
}).Wait();
|
||||
if(await bansClient.BanRevokeAsync(banInfo))
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_UNBANNED, false, unbanAddrTarget));
|
||||
else
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_BANNED, true, unbanAddrTarget));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using SharpChat.Bans;
|
||||
using SharpChat.S2CPackets;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class PardonUserClientCommand(BansClient bansClient) : ClientCommand {
|
||||
|
@ -9,17 +8,17 @@ namespace SharpChat.ClientCommands {
|
|||
|| ctx.NameEquals("unban");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
public async Task Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(!ctx.User.Can(UserPermissions.BanUser | UserPermissions.KickUser)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
string? unbanUserTarget = ctx.Args.FirstOrDefault();
|
||||
if(string.IsNullOrWhiteSpace(unbanUserTarget)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -32,18 +31,16 @@ namespace SharpChat.ClientCommands {
|
|||
unbanUserDisplay = unbanUser.UserName;
|
||||
}
|
||||
|
||||
Task.Run(async () => {
|
||||
BanInfo? banInfo = await bansClient.BanGetAsync(unbanUserTarget);
|
||||
if(banInfo?.Kind != BanKind.User) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_BANNED, true, unbanUserDisplay));
|
||||
return;
|
||||
}
|
||||
BanInfo? banInfo = await bansClient.BanGetAsync(unbanUserTarget);
|
||||
if(banInfo?.Kind != BanKind.User) {
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_BANNED, true, unbanUserDisplay));
|
||||
return;
|
||||
}
|
||||
|
||||
if(await bansClient.BanRevokeAsync(banInfo))
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_UNBANNED, false, unbanUserDisplay));
|
||||
else
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_BANNED, true, unbanUserDisplay));
|
||||
}).Wait();
|
||||
if(await bansClient.BanRevokeAsync(banInfo))
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_UNBANNED, false, unbanUserDisplay));
|
||||
else
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_BANNED, true, unbanUserDisplay));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using SharpChat.S2CPackets;
|
||||
using SharpChat.S2CPackets;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class PasswordChannelClientCommand : ClientCommand {
|
||||
|
@ -7,11 +7,11 @@ namespace SharpChat.ClientCommands {
|
|||
|| ctx.NameEquals("password");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
public async Task Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(!ctx.User.Can(UserPermissions.SetChannelPassword) || ctx.Channel.IsOwner(ctx.User)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -20,8 +20,8 @@ namespace SharpChat.ClientCommands {
|
|||
if(string.IsNullOrWhiteSpace(chanPass))
|
||||
chanPass = string.Empty;
|
||||
|
||||
ctx.Chat.UpdateChannel(ctx.Channel, password: chanPass);
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_PASSWORD_CHANGED, false));
|
||||
await ctx.Chat.UpdateChannel(ctx.Channel, password: chanPass);
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_PASSWORD_CHANGED, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using SharpChat.S2CPackets;
|
||||
using SharpChat.S2CPackets;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class RankChannelClientCommand : ClientCommand {
|
||||
|
@ -8,21 +8,21 @@ namespace SharpChat.ClientCommands {
|
|||
|| ctx.NameEquals("priv");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
public async Task Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(!ctx.User.Can(UserPermissions.SetChannelHierarchy) || ctx.Channel.IsOwner(ctx.User)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
if(ctx.Args.Length < 1 || !int.TryParse(ctx.Args.First(), out int chanHierarchy) || chanHierarchy > ctx.User.Rank) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.INSUFFICIENT_HIERARCHY));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.INSUFFICIENT_HIERARCHY));
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.Chat.UpdateChannel(ctx.Channel, hierarchy: chanHierarchy);
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_HIERARCHY_CHANGED, false));
|
||||
await ctx.Chat.UpdateChannel(ctx.Channel, hierarchy: chanHierarchy);
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_HIERARCHY_CHANGED, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using SharpChat.S2CPackets;
|
||||
using SharpChat.S2CPackets;
|
||||
using System.Net;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
|
@ -8,11 +8,11 @@ namespace SharpChat.ClientCommands {
|
|||
|| ctx.NameEquals("whois");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
public async Task Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(!ctx.User.Can(UserPermissions.SeeIPAddress)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, "/ip"));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, "/ip"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -20,12 +20,12 @@ namespace SharpChat.ClientCommands {
|
|||
User? ipUser = null;
|
||||
|
||||
if(string.IsNullOrWhiteSpace(ipUserStr) || (ipUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(ipUserStr))) == null) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_FOUND, true, ipUserStr ?? "User"));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_FOUND, true, ipUserStr ?? "User"));
|
||||
return;
|
||||
}
|
||||
|
||||
foreach(IPAddress ip in ctx.Chat.GetRemoteAddresses(ipUser))
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.IP_ADDRESS, false, ipUser.UserName, ip));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.IP_ADDRESS, false, ipUser.UserName, ip));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,10 +10,10 @@ namespace SharpChat.ClientCommands {
|
|||
|| ctx.NameEquals("restart");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
public async Task Dispatch(ClientCommandContext ctx) {
|
||||
if(!ctx.User.UserId.Equals("1")) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ namespace SharpChat.ClientCommands {
|
|||
foreach(Connection conn in ctx.Chat.Connections)
|
||||
conn.PrepareForRestart();
|
||||
|
||||
ctx.Chat.Update();
|
||||
await ctx.Chat.Update();
|
||||
WaitHandle?.Set();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using SharpChat.Events;
|
||||
using SharpChat.Events;
|
||||
using SharpChat.S2CPackets;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
|
@ -8,11 +8,11 @@ namespace SharpChat.ClientCommands {
|
|||
|| ctx.NameEquals("msg");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
public async Task Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(ctx.Args.Length < 2) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -20,14 +20,14 @@ namespace SharpChat.ClientCommands {
|
|||
User? whisperUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(whisperUserStr));
|
||||
|
||||
if(whisperUser == null) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_FOUND, true, whisperUserStr));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_FOUND, true, whisperUserStr));
|
||||
return;
|
||||
}
|
||||
|
||||
if(whisperUser == ctx.User)
|
||||
return;
|
||||
|
||||
ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
||||
await ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
||||
msgId,
|
||||
User.GetDMChannelName(ctx.User, whisperUser),
|
||||
ctx.User.UserId,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using SharpChat.S2CPackets;
|
||||
using SharpChat.S2CPackets;
|
||||
using System.Text;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
|
@ -7,7 +7,7 @@ namespace SharpChat.ClientCommands {
|
|||
return ctx.NameEquals("who");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
public async Task Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
StringBuilder whoChanSB = new();
|
||||
string? whoChanStr = ctx.Args.FirstOrDefault();
|
||||
|
@ -27,17 +27,17 @@ namespace SharpChat.ClientCommands {
|
|||
if(whoChanSB.Length > 2)
|
||||
whoChanSB.Length -= 2;
|
||||
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USERS_LISTING_SERVER, false, whoChanSB));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USERS_LISTING_SERVER, false, whoChanSB));
|
||||
} else {
|
||||
Channel? whoChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(whoChanStr));
|
||||
|
||||
if(whoChan is null) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NOT_FOUND, true, whoChanStr));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NOT_FOUND, true, whoChanStr));
|
||||
return;
|
||||
}
|
||||
|
||||
if(whoChan.Rank > ctx.User.Rank || (whoChan.HasPassword && !ctx.User.Can(UserPermissions.JoinAnyChannel))) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USERS_LISTING_ERROR, true, whoChanStr));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USERS_LISTING_ERROR, true, whoChanStr));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ namespace SharpChat.ClientCommands {
|
|||
if(whoChanSB.Length > 2)
|
||||
whoChanSB.Length -= 2;
|
||||
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USERS_LISTING_CHANNEL, false, whoChan.Name, whoChanSB));
|
||||
await ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USERS_LISTING_CHANNEL, false, whoChan.Name, whoChanSB));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using Fleck;
|
||||
using Fleck;
|
||||
using System.Net;
|
||||
|
||||
namespace SharpChat {
|
||||
|
@ -41,13 +41,13 @@ namespace SharpChat {
|
|||
RemotePort = (ushort)sock.ConnectionInfo.ClientPort;
|
||||
}
|
||||
|
||||
public void Send(S2CPacket packet) {
|
||||
public async Task Send(S2CPacket packet) {
|
||||
if(!Socket.IsAvailable)
|
||||
return;
|
||||
|
||||
string data = packet.Pack();
|
||||
if(!string.IsNullOrWhiteSpace(data))
|
||||
Socket.Send(data);
|
||||
await Socket.Send(data);
|
||||
}
|
||||
|
||||
public void BumpPing() {
|
||||
|
|
|
@ -26,10 +26,10 @@ namespace SharpChat {
|
|||
RandomSnowflake = new(SnowflakeGenerator);
|
||||
}
|
||||
|
||||
public void DispatchEvent(ChatEvent eventInfo) {
|
||||
public async Task DispatchEvent(ChatEvent eventInfo) {
|
||||
if(eventInfo is MessageCreateEvent mce) {
|
||||
if(mce.IsBroadcast) {
|
||||
Send(new CommandResponseS2CPacket(RandomSnowflake.Next(), LCR.BROADCAST, false, mce.MessageText));
|
||||
await Send(new CommandResponseS2CPacket(RandomSnowflake.Next(), LCR.BROADCAST, false, mce.MessageText));
|
||||
} else if(mce.IsPrivate) {
|
||||
// The channel name returned by GetDMChannelName should not be exposed to the user, instead @<Target User> should be displayed
|
||||
// e.g. nook sees @Arysil and Arysil sees @nook
|
||||
|
@ -48,7 +48,7 @@ namespace SharpChat {
|
|||
return;
|
||||
|
||||
foreach(User user in users)
|
||||
SendTo(user, new ChatMessageAddS2CPacket(
|
||||
await SendTo(user, new ChatMessageAddS2CPacket(
|
||||
mce.MessageId,
|
||||
DateTimeOffset.Now,
|
||||
mce.SenderId,
|
||||
|
@ -59,7 +59,7 @@ namespace SharpChat {
|
|||
} else {
|
||||
Channel? channel = Channels.FirstOrDefault(c => c.NameEquals(mce.ChannelName));
|
||||
if(channel is not null)
|
||||
SendTo(channel, new ChatMessageAddS2CPacket(
|
||||
await SendTo(channel, new ChatMessageAddS2CPacket(
|
||||
mce.MessageId,
|
||||
DateTimeOffset.Now,
|
||||
mce.SenderId,
|
||||
|
@ -82,7 +82,7 @@ namespace SharpChat {
|
|||
}
|
||||
}
|
||||
|
||||
public void Update() {
|
||||
public async Task Update() {
|
||||
foreach(Connection conn in Connections)
|
||||
if(!conn.IsDisposed && conn.HasTimedOut) {
|
||||
conn.Dispose();
|
||||
|
@ -93,15 +93,15 @@ namespace SharpChat {
|
|||
|
||||
foreach(User user in Users)
|
||||
if(!Connections.Any(conn => conn.User == user)) {
|
||||
HandleDisconnect(user, UserDisconnectS2CPacket.Reason.TimeOut);
|
||||
Logger.Write($"Timed out {user} (no more connections).");
|
||||
Logger.Write($"Timing out {user} (no more connections).");
|
||||
await HandleDisconnect(user, UserDisconnectS2CPacket.Reason.TimeOut);
|
||||
}
|
||||
}
|
||||
|
||||
public void SafeUpdate() {
|
||||
public async Task SafeUpdate() {
|
||||
ContextAccess.Wait();
|
||||
try {
|
||||
Update();
|
||||
await Update();
|
||||
} finally {
|
||||
ContextAccess.Release();
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ namespace SharpChat {
|
|||
return [.. Users.Where(u => ids.Contains(u.UserId))];
|
||||
}
|
||||
|
||||
public void UpdateUser(
|
||||
public async Task UpdateUser(
|
||||
User user,
|
||||
string? userName = null,
|
||||
string? nickName = null,
|
||||
|
@ -185,35 +185,35 @@ namespace SharpChat {
|
|||
|
||||
if(hasChanged) {
|
||||
if(!string.IsNullOrWhiteSpace(previousName))
|
||||
SendToUserChannels(user, new CommandResponseS2CPacket(RandomSnowflake.Next(), LCR.NICKNAME_CHANGE, false, previousName, user.LegacyNameWithStatus));
|
||||
await SendToUserChannels(user, new CommandResponseS2CPacket(RandomSnowflake.Next(), LCR.NICKNAME_CHANGE, false, previousName, user.LegacyNameWithStatus));
|
||||
|
||||
SendToUserChannels(user, new UserUpdateS2CPacket(user.UserId, user.LegacyNameWithStatus, user.Colour, user.Rank, user.Permissions));
|
||||
await SendToUserChannels(user, new UserUpdateS2CPacket(user.UserId, user.LegacyNameWithStatus, user.Colour, user.Rank, user.Permissions));
|
||||
}
|
||||
}
|
||||
|
||||
public void BanUser(User user, TimeSpan duration, UserDisconnectS2CPacket.Reason reason = UserDisconnectS2CPacket.Reason.Kicked) {
|
||||
public async Task BanUser(User user, TimeSpan duration, UserDisconnectS2CPacket.Reason reason = UserDisconnectS2CPacket.Reason.Kicked) {
|
||||
if(duration > TimeSpan.Zero) {
|
||||
DateTimeOffset expires = duration >= TimeSpan.MaxValue ? DateTimeOffset.MaxValue : DateTimeOffset.Now + duration;
|
||||
SendTo(user, new ForceDisconnectS2CPacket(expires));
|
||||
await SendTo(user, new ForceDisconnectS2CPacket(expires));
|
||||
} else
|
||||
SendTo(user, new ForceDisconnectS2CPacket());
|
||||
await SendTo(user, new ForceDisconnectS2CPacket());
|
||||
|
||||
foreach(Connection conn in Connections)
|
||||
if(conn.User == user)
|
||||
conn.Dispose();
|
||||
Connections.RemoveWhere(conn => conn.IsDisposed);
|
||||
|
||||
HandleDisconnect(user, reason);
|
||||
await HandleDisconnect(user, reason);
|
||||
}
|
||||
|
||||
public void HandleJoin(User user, Channel chan, Connection conn, int maxMsgLength) {
|
||||
public async Task HandleJoin(User user, Channel chan, Connection conn, int maxMsgLength) {
|
||||
if(!IsInChannel(user, chan)) {
|
||||
long msgId = RandomSnowflake.Next();
|
||||
SendTo(chan, new UserConnectS2CPacket(msgId, DateTimeOffset.Now, user.UserId, user.LegacyNameWithStatus, user.Colour, user.Rank, user.Permissions));
|
||||
await SendTo(chan, new UserConnectS2CPacket(msgId, DateTimeOffset.Now, user.UserId, user.LegacyNameWithStatus, user.Colour, user.Rank, user.Permissions));
|
||||
Events.AddEvent(msgId, "user:connect", chan.Name, user.UserId, user.UserName, user.Colour, user.Rank, user.NickName, user.Permissions, null, StoredEventFlags.Log);
|
||||
}
|
||||
|
||||
conn.Send(new AuthSuccessS2CPacket(
|
||||
await conn.Send(new AuthSuccessS2CPacket(
|
||||
user.UserId,
|
||||
user.LegacyNameWithStatus,
|
||||
user.Colour,
|
||||
|
@ -222,7 +222,7 @@ namespace SharpChat {
|
|||
chan.Name,
|
||||
maxMsgLength
|
||||
));
|
||||
conn.Send(new ContextUsersS2CPacket(
|
||||
await conn.Send(new ContextUsersS2CPacket(
|
||||
GetChannelUsers(chan).Except([user]).OrderByDescending(u => u.Rank)
|
||||
.Select(u => new ContextUsersS2CPacket.Entry(
|
||||
u.UserId,
|
||||
|
@ -235,9 +235,9 @@ namespace SharpChat {
|
|||
));
|
||||
|
||||
foreach(StoredEventInfo msg in Events.GetChannelEventLog(chan.Name))
|
||||
conn.Send(new ContextMessageS2CPacket(msg));
|
||||
await conn.Send(new ContextMessageS2CPacket(msg));
|
||||
|
||||
conn.Send(new ContextChannelsS2CPacket(
|
||||
await conn.Send(new ContextChannelsS2CPacket(
|
||||
Channels.Where(c => c.Rank <= user.Rank)
|
||||
.Select(c => new ContextChannelsS2CPacket.Entry(c.Name, c.HasPassword, c.IsTemporary))
|
||||
));
|
||||
|
@ -248,8 +248,8 @@ namespace SharpChat {
|
|||
UserLastChannel[user.UserId] = chan;
|
||||
}
|
||||
|
||||
public void HandleDisconnect(User user, UserDisconnectS2CPacket.Reason reason = UserDisconnectS2CPacket.Reason.Leave) {
|
||||
UpdateUser(user, status: UserStatus.Offline);
|
||||
public async Task HandleDisconnect(User user, UserDisconnectS2CPacket.Reason reason = UserDisconnectS2CPacket.Reason.Leave) {
|
||||
await UpdateUser(user, status: UserStatus.Offline);
|
||||
Users.Remove(user);
|
||||
UserLastChannel.Remove(user.UserId);
|
||||
|
||||
|
@ -259,53 +259,53 @@ namespace SharpChat {
|
|||
ChannelUsers.Remove(new ChannelUserAssoc(user.UserId, chan.Name));
|
||||
|
||||
long msgId = RandomSnowflake.Next();
|
||||
SendTo(chan, new UserDisconnectS2CPacket(msgId, DateTimeOffset.Now, user.UserId, user.LegacyNameWithStatus, reason));
|
||||
await SendTo(chan, new UserDisconnectS2CPacket(msgId, DateTimeOffset.Now, user.UserId, user.LegacyNameWithStatus, reason));
|
||||
Events.AddEvent(msgId, "user:disconnect", chan.Name, user.UserId, user.UserName, user.Colour, user.Rank, user.NickName, user.Permissions, new { reason = (int)reason }, StoredEventFlags.Log);
|
||||
|
||||
if(chan.IsTemporary && chan.IsOwner(user))
|
||||
RemoveChannel(chan);
|
||||
await RemoveChannel(chan);
|
||||
}
|
||||
}
|
||||
|
||||
public void SwitchChannel(User user, Channel chan, string password) {
|
||||
public async Task SwitchChannel(User user, Channel chan, string password) {
|
||||
if(UserLastChannel.TryGetValue(user.UserId, out Channel? ulc) && chan == ulc) {
|
||||
ForceChannel(user);
|
||||
await ForceChannel(user);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!user.Can(UserPermissions.JoinAnyChannel) && chan.IsOwner(user)) {
|
||||
if(chan.Rank > user.Rank) {
|
||||
SendTo(user, new CommandResponseS2CPacket(RandomSnowflake.Next(), LCR.CHANNEL_INSUFFICIENT_HIERARCHY, true, chan.Name));
|
||||
ForceChannel(user);
|
||||
await SendTo(user, new CommandResponseS2CPacket(RandomSnowflake.Next(), LCR.CHANNEL_INSUFFICIENT_HIERARCHY, true, chan.Name));
|
||||
await ForceChannel(user);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!string.IsNullOrEmpty(chan.Password) && chan.Password != password) {
|
||||
SendTo(user, new CommandResponseS2CPacket(RandomSnowflake.Next(), LCR.CHANNEL_INVALID_PASSWORD, true, chan.Name));
|
||||
ForceChannel(user);
|
||||
await SendTo(user, new CommandResponseS2CPacket(RandomSnowflake.Next(), LCR.CHANNEL_INVALID_PASSWORD, true, chan.Name));
|
||||
await ForceChannel(user);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ForceChannelSwitch(user, chan);
|
||||
await ForceChannelSwitch(user, chan);
|
||||
}
|
||||
|
||||
public void ForceChannelSwitch(User user, Channel chan) {
|
||||
public async Task ForceChannelSwitch(User user, Channel chan) {
|
||||
if(!Channels.Contains(chan))
|
||||
return;
|
||||
|
||||
Channel oldChan = UserLastChannel[user.UserId];
|
||||
|
||||
long leaveId = RandomSnowflake.Next();
|
||||
SendTo(oldChan, new UserChannelLeaveS2CPacket(leaveId, user.UserId));
|
||||
await SendTo(oldChan, new UserChannelLeaveS2CPacket(leaveId, user.UserId));
|
||||
Events.AddEvent(leaveId, "chan:leave", oldChan.Name, user.UserId, user.UserName, user.Colour, user.Rank, user.NickName, user.Permissions, null, StoredEventFlags.Log);
|
||||
|
||||
long joinId = RandomSnowflake.Next();
|
||||
SendTo(chan, new UserChannelJoinS2CPacket(joinId, user.UserId, user.LegacyNameWithStatus, user.Colour, user.Rank, user.Permissions));
|
||||
await SendTo(chan, new UserChannelJoinS2CPacket(joinId, user.UserId, user.LegacyNameWithStatus, user.Colour, user.Rank, user.Permissions));
|
||||
Events.AddEvent(joinId, "chan:join", chan.Name, user.UserId, user.LegacyName, user.Colour, user.Rank, user.NickName, user.Permissions, null, StoredEventFlags.Log);
|
||||
|
||||
SendTo(user, new ContextClearS2CPacket(ContextClearS2CPacket.Mode.MessagesUsers));
|
||||
SendTo(user, new ContextUsersS2CPacket(
|
||||
await SendTo(user, new ContextClearS2CPacket(ContextClearS2CPacket.Mode.MessagesUsers));
|
||||
await SendTo(user, new ContextUsersS2CPacket(
|
||||
GetChannelUsers(chan).Except([user]).OrderByDescending(u => u.Rank)
|
||||
.Select(u => new ContextUsersS2CPacket.Entry(
|
||||
u.UserId,
|
||||
|
@ -318,69 +318,58 @@ namespace SharpChat {
|
|||
));
|
||||
|
||||
foreach(StoredEventInfo msg in Events.GetChannelEventLog(chan.Name))
|
||||
SendTo(user, new ContextMessageS2CPacket(msg));
|
||||
await SendTo(user, new ContextMessageS2CPacket(msg));
|
||||
|
||||
ForceChannel(user, chan);
|
||||
await ForceChannel(user, chan);
|
||||
|
||||
ChannelUsers.Remove(new ChannelUserAssoc(user.UserId, oldChan.Name));
|
||||
ChannelUsers.Add(new ChannelUserAssoc(user.UserId, chan.Name));
|
||||
UserLastChannel[user.UserId] = chan;
|
||||
|
||||
if(oldChan.IsTemporary && oldChan.IsOwner(user))
|
||||
RemoveChannel(oldChan);
|
||||
await RemoveChannel(oldChan);
|
||||
}
|
||||
|
||||
public void Send(S2CPacket packet) {
|
||||
ArgumentNullException.ThrowIfNull(packet);
|
||||
|
||||
public async Task Send(S2CPacket packet) {
|
||||
foreach(Connection conn in Connections)
|
||||
if(conn.IsAlive && conn.User is not null)
|
||||
conn.Send(packet);
|
||||
await conn.Send(packet);
|
||||
}
|
||||
|
||||
public void SendTo(User user, S2CPacket packet) {
|
||||
ArgumentNullException.ThrowIfNull(user);
|
||||
ArgumentNullException.ThrowIfNull(packet);
|
||||
|
||||
public async Task SendTo(User user, S2CPacket packet) {
|
||||
foreach(Connection conn in Connections)
|
||||
if(conn.IsAlive && conn.User == user)
|
||||
conn.Send(packet);
|
||||
await conn.Send(packet);
|
||||
}
|
||||
|
||||
public void SendTo(Channel channel, S2CPacket packet) {
|
||||
ArgumentNullException.ThrowIfNull(channel);
|
||||
ArgumentNullException.ThrowIfNull(packet);
|
||||
|
||||
public async Task SendTo(Channel channel, S2CPacket packet) {
|
||||
// might be faster to grab the users first and then cascade into that SendTo
|
||||
IEnumerable<Connection> conns = Connections.Where(c => c.IsAlive && c.User is not null && IsInChannel(c.User, channel));
|
||||
foreach(Connection conn in conns)
|
||||
conn.Send(packet);
|
||||
await conn.Send(packet);
|
||||
}
|
||||
|
||||
public void SendToUserChannels(User user, S2CPacket packet) {
|
||||
ArgumentNullException.ThrowIfNull(user);
|
||||
ArgumentNullException.ThrowIfNull(packet);
|
||||
|
||||
public async Task SendToUserChannels(User user, S2CPacket packet) {
|
||||
IEnumerable<Channel> chans = Channels.Where(c => IsInChannel(user, c));
|
||||
IEnumerable<Connection> conns = Connections.Where(conn => conn.IsAlive && conn.User is not null && ChannelUsers.Any(cu => cu.UserId == conn.User.UserId && chans.Any(chan => chan.NameEquals(cu.ChannelName))));
|
||||
foreach(Connection conn in conns)
|
||||
conn.Send(packet);
|
||||
await conn.Send(packet);
|
||||
}
|
||||
|
||||
public IPAddress[] GetRemoteAddresses(User user) {
|
||||
return [.. Connections.Where(c => c.IsAlive && c.User == user).Select(c => c.RemoteAddress).Distinct()];
|
||||
}
|
||||
|
||||
public void ForceChannel(User user, Channel? chan = null) {
|
||||
public async Task ForceChannel(User user, Channel? chan = null) {
|
||||
ArgumentNullException.ThrowIfNull(user);
|
||||
|
||||
if(chan == null && !UserLastChannel.TryGetValue(user.UserId, out chan))
|
||||
throw new ArgumentException("no channel???");
|
||||
|
||||
SendTo(user, new UserChannelForceJoinS2CPacket(chan.Name));
|
||||
await SendTo(user, new UserChannelForceJoinS2CPacket(chan.Name));
|
||||
}
|
||||
|
||||
public void UpdateChannel(Channel channel, bool? temporary = null, int? hierarchy = null, string? password = null) {
|
||||
public async Task UpdateChannel(Channel channel, bool? temporary = null, int? hierarchy = null, string? password = null) {
|
||||
ArgumentNullException.ThrowIfNull(channel);
|
||||
if(!Channels.Contains(channel))
|
||||
throw new ArgumentException("Provided channel is not registered with this manager.", nameof(channel));
|
||||
|
@ -396,10 +385,10 @@ namespace SharpChat {
|
|||
|
||||
// TODO: Users that no longer have access to the channel/gained access to the channel by the hierarchy change should receive delete and create packets respectively
|
||||
foreach(User user in Users.Where(u => u.Rank >= channel.Rank))
|
||||
SendTo(user, new ChannelUpdateS2CPacket(channel.Name, channel.Name, channel.HasPassword, channel.IsTemporary));
|
||||
await SendTo(user, new ChannelUpdateS2CPacket(channel.Name, channel.Name, channel.HasPassword, channel.IsTemporary));
|
||||
}
|
||||
|
||||
public void RemoveChannel(Channel channel) {
|
||||
public async Task RemoveChannel(Channel channel) {
|
||||
if(channel == null || Channels.Count < 1)
|
||||
return;
|
||||
|
||||
|
@ -413,11 +402,11 @@ namespace SharpChat {
|
|||
// Move all users back to the main channel
|
||||
// TODO: Replace this with a kick. SCv2 supports being in 0 channels, SCv1 should force the user back to DefaultChannel.
|
||||
foreach(User user in GetChannelUsers(channel))
|
||||
SwitchChannel(user, defaultChannel, string.Empty);
|
||||
await SwitchChannel(user, defaultChannel, string.Empty);
|
||||
|
||||
// Broadcast deletion of channel
|
||||
foreach(User user in Users.Where(u => u.Rank >= channel.Rank))
|
||||
SendTo(user, new ChannelDeleteS2CPacket(channel.Name));
|
||||
await SendTo(user, new ChannelDeleteS2CPacket(channel.Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ namespace SharpChat {
|
|||
|
||||
if(hasCancelled) return;
|
||||
|
||||
using SockChatServer scs = new(httpClient, flashii, flashii, evtStore, config.ScopeTo("chat"));
|
||||
using SockChatServer scs = new(flashii, flashii, evtStore, config.ScopeTo("chat"));
|
||||
scs.Listen(mre);
|
||||
|
||||
mre.WaitOne();
|
||||
|
|
|
@ -18,7 +18,6 @@ namespace SharpChat {
|
|||
public IWebSocketServer Server { get; }
|
||||
public Context Context { get; }
|
||||
|
||||
private readonly HttpClient HttpClient;
|
||||
private readonly BansClient BansClient;
|
||||
|
||||
private readonly CachedValue<int> MaxMessageLength;
|
||||
|
@ -37,7 +36,6 @@ namespace SharpChat {
|
|||
private Channel DefaultChannel { get; set; }
|
||||
|
||||
public SockChatServer(
|
||||
HttpClient httpClient,
|
||||
AuthClient authClient,
|
||||
BansClient bansClient,
|
||||
EventStorage.EventStorage evtStore,
|
||||
|
@ -45,7 +43,6 @@ namespace SharpChat {
|
|||
) {
|
||||
Logger.Write("Initialising Sock Chat server...");
|
||||
|
||||
HttpClient = httpClient;
|
||||
BansClient = bansClient;
|
||||
|
||||
MaxMessageLength = config.ReadCached("msgMaxLength", DEFAULT_MSG_LENGTH_MAX);
|
||||
|
@ -121,26 +118,26 @@ namespace SharpChat {
|
|||
Connection conn = new(sock);
|
||||
Context.Connections.Add(conn);
|
||||
|
||||
sock.OnOpen = () => OnOpen(conn);
|
||||
sock.OnClose = () => OnClose(conn);
|
||||
sock.OnError = err => OnError(conn, err);
|
||||
sock.OnMessage = msg => OnMessage(conn, msg);
|
||||
sock.OnOpen = () => OnOpen(conn).Wait();
|
||||
sock.OnClose = () => OnClose(conn).Wait();
|
||||
sock.OnError = err => OnError(conn, err).Wait();
|
||||
sock.OnMessage = msg => OnMessage(conn, msg).Wait();
|
||||
});
|
||||
|
||||
Logger.Write("Listening...");
|
||||
}
|
||||
|
||||
private void OnOpen(Connection conn) {
|
||||
private async Task OnOpen(Connection conn) {
|
||||
Logger.Write($"Connection opened from {conn.RemoteAddress}:{conn.RemotePort}");
|
||||
Context.SafeUpdate();
|
||||
await Context.SafeUpdate();
|
||||
}
|
||||
|
||||
private void OnError(Connection conn, Exception ex) {
|
||||
private async Task OnError(Connection conn, Exception ex) {
|
||||
Logger.Write($"[{conn.Id} {conn.RemoteAddress}] {ex}");
|
||||
Context.SafeUpdate();
|
||||
await Context.SafeUpdate();
|
||||
}
|
||||
|
||||
private void OnClose(Connection conn) {
|
||||
private async Task OnClose(Connection conn) {
|
||||
Logger.Write($"Connection closed from {conn.RemoteAddress}:{conn.RemotePort}");
|
||||
|
||||
Context.ContextAccess.Wait();
|
||||
|
@ -148,16 +145,16 @@ namespace SharpChat {
|
|||
Context.Connections.Remove(conn);
|
||||
|
||||
if(conn.User != null && !Context.Connections.Any(c => c.User == conn.User))
|
||||
Context.HandleDisconnect(conn.User);
|
||||
await Context.HandleDisconnect(conn.User);
|
||||
|
||||
Context.Update();
|
||||
await Context.Update();
|
||||
} finally {
|
||||
Context.ContextAccess.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMessage(Connection conn, string msg) {
|
||||
Context.SafeUpdate();
|
||||
private async Task OnMessage(Connection conn, string msg) {
|
||||
await Context.SafeUpdate();
|
||||
|
||||
// this doesn't affect non-authed connections?????
|
||||
if(conn.User is not null && conn.User.Rank < FloodKickExemptRank) {
|
||||
|
@ -186,19 +183,19 @@ namespace SharpChat {
|
|||
|
||||
if(banUser is not null) {
|
||||
if(banDuration == TimeSpan.MinValue) {
|
||||
Context.SendTo(conn.User, new CommandResponseS2CPacket(Context.RandomSnowflake.Next(), LCR.FLOOD_WARN, false));
|
||||
await Context.SendTo(conn.User, new CommandResponseS2CPacket(Context.RandomSnowflake.Next(), LCR.FLOOD_WARN, false));
|
||||
} else {
|
||||
Context.BanUser(conn.User, banDuration, UserDisconnectS2CPacket.Reason.Flood);
|
||||
await Context.BanUser(conn.User, banDuration, UserDisconnectS2CPacket.Reason.Flood);
|
||||
|
||||
if(banDuration > TimeSpan.Zero)
|
||||
BansClient.BanCreateAsync(
|
||||
await BansClient.BanCreateAsync(
|
||||
BanKind.User,
|
||||
banDuration,
|
||||
conn.RemoteAddress,
|
||||
conn.User.UserId,
|
||||
"Kicked from chat for flood protection.",
|
||||
IPAddress.IPv6Loopback
|
||||
).Wait();
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -213,7 +210,8 @@ namespace SharpChat {
|
|||
? GuestHandlers.FirstOrDefault(h => h.IsMatch(context))
|
||||
: AuthedHandlers.FirstOrDefault(h => h.IsMatch(context));
|
||||
|
||||
handler?.Handle(context);
|
||||
if(handler is not null)
|
||||
await handler.Handle(context);
|
||||
}
|
||||
|
||||
private bool IsDisposed;
|
||||
|
@ -237,7 +235,6 @@ namespace SharpChat {
|
|||
conn.Dispose();
|
||||
|
||||
Server?.Dispose();
|
||||
HttpClient?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue