No longer keep track of connections within the ChatUser class.

This commit is contained in:
flash 2023-02-16 23:33:48 +01:00
parent 06af94e94f
commit 13ae843c8d
28 changed files with 202 additions and 209 deletions

View file

@ -46,11 +46,6 @@ namespace SharpChat {
user.LeaveChannel(this); user.LeaveChannel(this);
} }
public void Send(IServerPacket packet) {
foreach(ChatUser user in Users)
user.Send(packet);
}
public IEnumerable<ChatUser> GetUsers(IEnumerable<ChatUser> exclude = null) { public IEnumerable<ChatUser> GetUsers(IEnumerable<ChatUser> exclude = null) {
IEnumerable<ChatUser> users = Users.OrderByDescending(x => x.Rank); IEnumerable<ChatUser> users = Users.OrderByDescending(x => x.Rank);

View file

@ -17,7 +17,7 @@ namespace SharpChat {
public string Id { get; private set; } public string Id { get; private set; }
public bool IsDisposed { get; private set; } public bool IsDisposed { get; private set; }
public DateTimeOffset LastPing { get; set; } = DateTimeOffset.MinValue; public DateTimeOffset LastPing { get; set; } = DateTimeOffset.Now;
public ChatUser User { get; set; } public ChatUser User { get; set; }
private int CloseCode { get; set; } = 1000; private int CloseCode { get; set; } = 1000;
@ -39,6 +39,10 @@ namespace SharpChat {
} }
} }
public bool IsAlive => !IsDisposed && !HasTimedOut;
public bool IsAuthed => IsAlive && User is not null;
public ChatConnection(IWebSocketConnection sock) { public ChatConnection(IWebSocketConnection sock) {
Socket = sock; Socket = sock;
Id = RNG.SecureRandomString(ID_LENGTH); Id = RNG.SecureRandomString(ID_LENGTH);

View file

@ -5,6 +5,7 @@ using SharpChat.Packet;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net;
namespace SharpChat { namespace SharpChat {
public class ChatContext { public class ChatContext {
@ -25,19 +26,22 @@ namespace SharpChat {
} }
public void Update() { public void Update() {
lock(UsersAccess) lock(ConnectionsAccess) {
foreach(ChatUser user in Users) { foreach(ChatConnection conn in Connections)
IEnumerable<ChatConnection> timedOut = user.GetDeadConnections(); if(!conn.IsDisposed && conn.HasTimedOut) {
foreach(ChatConnection conn in timedOut) {
user.RemoveConnection(conn);
conn.Dispose(); conn.Dispose();
Logger.Write($"Nuked session {conn.Id} from {user.Username} (timeout)"); Logger.Write($"Nuked connection {conn.Id} associated with {conn.User}.");
} }
if(!user.HasConnections) Connections.RemoveWhere(conn => conn.IsDisposed);
UserLeave(null, user, UserDisconnectReason.TimeOut);
} lock(UsersAccess)
foreach(ChatUser user in Users)
if(!Connections.Any(conn => conn.User == user)) {
UserLeave(null, user, UserDisconnectReason.TimeOut);
Logger.Write($"Timed out {user} (no more connections).");
}
}
} }
public ChatConnection GetConnection(IWebSocketConnection sock) { public ChatConnection GetConnection(IWebSocketConnection sock) {
@ -46,18 +50,24 @@ namespace SharpChat {
public void BanUser(ChatUser user, TimeSpan duration, UserDisconnectReason reason = UserDisconnectReason.Kicked) { public void BanUser(ChatUser user, TimeSpan duration, UserDisconnectReason reason = UserDisconnectReason.Kicked) {
if(duration > TimeSpan.Zero) if(duration > TimeSpan.Zero)
user.Send(new ForceDisconnectPacket(ForceDisconnectReason.Banned, DateTimeOffset.Now + duration)); SendTo(user, new ForceDisconnectPacket(ForceDisconnectReason.Banned, DateTimeOffset.Now + duration));
else else
user.Send(new ForceDisconnectPacket(ForceDisconnectReason.Kicked)); SendTo(user, new ForceDisconnectPacket(ForceDisconnectReason.Kicked));
lock(ConnectionsAccess) {
foreach(ChatConnection conn in Connections)
if(conn.User == user)
conn.Dispose();
Connections.RemoveWhere(conn => conn.IsDisposed);
}
user.Close();
UserLeave(user.Channel, user, reason); UserLeave(user.Channel, user, reason);
} }
public void HandleJoin(ChatUser user, ChatChannel chan, ChatConnection conn, int maxMsgLength) { public void HandleJoin(ChatUser user, ChatChannel chan, ChatConnection conn, int maxMsgLength) {
lock(EventsAccess) { lock(EventsAccess) {
if(!chan.HasUser(user)) { if(!chan.HasUser(user)) {
chan.Send(new UserConnectPacket(DateTimeOffset.Now, user)); SendTo(chan, new UserConnectPacket(DateTimeOffset.Now, user));
Events.AddEvent(new UserConnectEvent(DateTimeOffset.Now, user, chan)); Events.AddEvent(new UserConnectEvent(DateTimeOffset.Now, user, chan));
} }
@ -94,28 +104,27 @@ namespace SharpChat {
lock(EventsAccess) { lock(EventsAccess) {
chan.UserLeave(user); chan.UserLeave(user);
chan.Send(new UserDisconnectPacket(DateTimeOffset.Now, user, reason)); SendTo(chan, new UserDisconnectPacket(DateTimeOffset.Now, user, reason));
Events.AddEvent(new UserDisconnectEvent(DateTimeOffset.Now, user, chan, reason)); Events.AddEvent(new UserDisconnectEvent(DateTimeOffset.Now, user, chan, reason));
} }
} }
public void SwitchChannel(ChatUser user, ChatChannel chan, string password) { public void SwitchChannel(ChatUser user, ChatChannel chan, string password) {
if(user.CurrentChannel == chan) { if(user.CurrentChannel == chan) {
//user.Send(true, "samechan", chan.Name); ForceChannel(user);
user.ForceChannel();
return; return;
} }
if(!user.Can(ChatUserPermissions.JoinAnyChannel) && chan.Owner != user) { if(!user.Can(ChatUserPermissions.JoinAnyChannel) && chan.Owner != user) {
if(chan.Rank > user.Rank) { if(chan.Rank > user.Rank) {
user.Send(new LegacyCommandResponse(LCR.CHANNEL_INSUFFICIENT_HIERARCHY, true, chan.Name)); SendTo(user, new LegacyCommandResponse(LCR.CHANNEL_INSUFFICIENT_HIERARCHY, true, chan.Name));
user.ForceChannel(); ForceChannel(user);
return; return;
} }
if(chan.Password != password) { if(chan.Password != password) {
user.Send(new LegacyCommandResponse(LCR.CHANNEL_INVALID_PASSWORD, true, chan.Name)); SendTo(user, new LegacyCommandResponse(LCR.CHANNEL_INVALID_PASSWORD, true, chan.Name));
user.ForceChannel(); ForceChannel(user);
return; return;
} }
} }
@ -131,18 +140,18 @@ namespace SharpChat {
ChatChannel oldChan = user.CurrentChannel; ChatChannel oldChan = user.CurrentChannel;
lock(EventsAccess) { lock(EventsAccess) {
oldChan.Send(new UserChannelLeavePacket(user)); SendTo(oldChan, new UserChannelLeavePacket(user));
Events.AddEvent(new UserChannelLeaveEvent(DateTimeOffset.Now, user, oldChan)); Events.AddEvent(new UserChannelLeaveEvent(DateTimeOffset.Now, user, oldChan));
chan.Send(new UserChannelJoinPacket(user)); SendTo(chan, new UserChannelJoinPacket(user));
Events.AddEvent(new UserChannelJoinEvent(DateTimeOffset.Now, user, chan)); Events.AddEvent(new UserChannelJoinEvent(DateTimeOffset.Now, user, chan));
user.Send(new ContextClearPacket(chan, ContextClearMode.MessagesUsers)); SendTo(user, new ContextClearPacket(chan, ContextClearMode.MessagesUsers));
user.Send(new ContextUsersPacket(chan.GetUsers(new[] { user }))); SendTo(user, new ContextUsersPacket(chan.GetUsers(new[] { user })));
foreach(IChatEvent msg in Events.GetTargetEventLog(chan.Name)) foreach(IChatEvent msg in Events.GetTargetEventLog(chan.Name))
user.Send(new ContextMessagePacket(msg)); SendTo(user, new ContextMessagePacket(msg));
user.ForceChannel(chan); ForceChannel(user, chan);
oldChan.UserLeave(user); oldChan.UserLeave(user);
chan.UserJoin(user); chan.UserJoin(user);
} }
@ -153,9 +162,50 @@ namespace SharpChat {
} }
public void Send(IServerPacket packet) { public void Send(IServerPacket packet) {
lock(UsersAccess) if(packet == null)
foreach(ChatUser user in Users) throw new ArgumentNullException(nameof(packet));
user.Send(packet);
lock(ConnectionsAccess)
foreach(ChatConnection conn in Connections)
if(conn.IsAuthed)
conn.Send(packet);
}
public void SendTo(ChatUser user, IServerPacket packet) {
if(user == null)
throw new ArgumentNullException(nameof(user));
if(packet == null)
throw new ArgumentNullException(nameof(packet));
lock(ConnectionsAccess)
foreach(ChatConnection conn in Connections)
if(conn.IsAlive && conn.User == user)
conn.Send(packet);
}
public void SendTo(ChatChannel channel, IServerPacket packet) {
if(channel == null)
throw new ArgumentNullException(nameof(channel));
if(packet == null)
throw new ArgumentNullException(nameof(packet));
lock(ConnectionsAccess) {
IEnumerable<ChatConnection> conns = Connections.Where(c => c.IsAuthed && channel.HasUser(c.User));
foreach(ChatConnection conn in conns)
conn.Send(packet);
}
}
public IPAddress[] GetRemoteAddresses(ChatUser user) {
lock(ConnectionsAccess)
return Connections.Where(c => c.IsAlive && c.User == user).Select(c => c.RemoteAddress).Distinct().ToArray();
}
public void ForceChannel(ChatUser user, ChatChannel chan = null) {
if(user == null)
throw new ArgumentNullException(nameof(user));
SendTo(user, new UserChannelForceJoinPacket(chan ?? user.CurrentChannel));
} }
public void UpdateChannel(ChatChannel channel, string name = null, bool? temporary = null, int? hierarchy = null, string password = null) { public void UpdateChannel(ChatChannel channel, string name = null, bool? temporary = null, int? hierarchy = null, string password = null) {
@ -187,10 +237,10 @@ namespace SharpChat {
// 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 // 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
lock(UsersAccess) lock(UsersAccess)
foreach(ChatUser user in Users.Where(u => u.Rank >= channel.Rank)) { foreach(ChatUser user in Users.Where(u => u.Rank >= channel.Rank)) {
user.Send(new ChannelUpdatePacket(prevName, channel)); SendTo(user, new ChannelUpdatePacket(prevName, channel));
if(nameUpdated) if(nameUpdated)
user.ForceChannel(); ForceChannel(user);
} }
} }
@ -213,7 +263,7 @@ namespace SharpChat {
// Broadcast deletion of channel // Broadcast deletion of channel
lock(UsersAccess) lock(UsersAccess)
foreach(ChatUser user in Users.Where(u => u.Rank >= channel.Rank)) foreach(ChatUser user in Users.Where(u => u.Rank >= channel.Rank))
user.Send(new ChannelDeletePacket(channel)); SendTo(user, new ChannelDeletePacket(channel));
} }
} }
} }

View file

@ -1,9 +1,7 @@
using SharpChat.Misuzu; using SharpChat.Misuzu;
using SharpChat.Packet;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net;
using System.Text; using System.Text;
namespace SharpChat { namespace SharpChat {
@ -83,7 +81,6 @@ namespace SharpChat {
public class ChatUser : BasicUser, IPacketTarget { public class ChatUser : BasicUser, IPacketTarget {
public DateTimeOffset SilencedUntil { get; set; } public DateTimeOffset SilencedUntil { get; set; }
private readonly List<ChatConnection> Connections = new();
private readonly List<ChatChannel> Channels = new(); private readonly List<ChatChannel> Channels = new();
public readonly ChatRateLimiter RateLimiter = new(); public readonly ChatRateLimiter RateLimiter = new();
@ -98,12 +95,6 @@ namespace SharpChat {
public bool IsSilenced public bool IsSilenced
=> DateTimeOffset.UtcNow - SilencedUntil <= TimeSpan.Zero; => DateTimeOffset.UtcNow - SilencedUntil <= TimeSpan.Zero;
public bool HasConnections => Connections.Where(c => !c.HasTimedOut && !c.IsDisposed).Any();
public int ConnectionCount => Connections.Where(c => !c.HasTimedOut && !c.IsDisposed).Count();
public IEnumerable<IPAddress> RemoteAddresses => Connections.Select(c => c.RemoteAddress);
public ChatUser() {} public ChatUser() {}
public ChatUser(MisuzuAuthInfo auth) { public ChatUser(MisuzuAuthInfo auth) {
@ -125,26 +116,6 @@ namespace SharpChat {
SilencedUntil = auth.SilencedUntil; SilencedUntil = auth.SilencedUntil;
} }
public void Send(IServerPacket packet) {
foreach(ChatConnection conn in Connections)
conn.Send(packet);
}
public void Close() {
foreach(ChatConnection conn in Connections)
conn.Dispose();
Connections.Clear();
}
public void ForceChannel(ChatChannel chan = null) {
Send(new UserChannelForceJoinPacket(chan ?? CurrentChannel));
}
public void FocusChannel(ChatChannel chan) {
if(InChannel(chan))
CurrentChannel = chan;
}
public bool InChannel(ChatChannel chan) { public bool InChannel(ChatChannel chan) {
return Channels.Contains(chan); return Channels.Contains(chan);
} }
@ -165,27 +136,6 @@ namespace SharpChat {
return Channels.ToList(); return Channels.ToList();
} }
public void AddConnection(ChatConnection conn) {
if(conn == null)
return;
conn.User = this;
Connections.Add(conn);
}
public void RemoveConnection(ChatConnection conn) {
if(conn == null)
return;
if(!conn.IsDisposed) // this could be possible
conn.User = null;
Connections.Remove(conn);
}
public IEnumerable<ChatConnection> GetDeadConnections() {
return Connections.Where(x => x.HasTimedOut || x.IsDisposed).ToList();
}
public bool NameEquals(string name) { public bool NameEquals(string name) {
return string.Equals(name, Username, StringComparison.InvariantCultureIgnoreCase) return string.Equals(name, Username, StringComparison.InvariantCultureIgnoreCase)
|| string.Equals(name, Nickname, StringComparison.InvariantCultureIgnoreCase) || string.Equals(name, Nickname, StringComparison.InvariantCultureIgnoreCase)

View file

@ -22,7 +22,7 @@ namespace SharpChat.Commands {
ctx.User.Status = ChatUserStatus.Away; ctx.User.Status = ChatUserStatus.Away;
ctx.User.StatusMessage = statusText; ctx.User.StatusMessage = statusText;
ctx.Channel.Send(new UserUpdatePacket(ctx.User)); ctx.Chat.SendTo(ctx.Channel, new UserUpdatePacket(ctx.User));
} }
} }
} }

View file

@ -18,12 +18,12 @@ namespace SharpChat.Commands {
public void Dispatch(ChatCommandContext ctx) { public void Dispatch(ChatCommandContext ctx) {
if(!ctx.User.Can(ChatUserPermissions.BanUser | ChatUserPermissions.KickUser)) { if(!ctx.User.Can(ChatUserPermissions.BanUser | ChatUserPermissions.KickUser)) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
return; return;
} }
Task.Run(async () => { Task.Run(async () => {
ctx.User.Send(new BanListPacket( ctx.Chat.SendTo(ctx.User, new BanListPacket(
await Misuzu.GetBanListAsync() await Misuzu.GetBanListAsync()
)); ));
}).Wait(); }).Wait();

View file

@ -9,11 +9,11 @@ namespace SharpChat.Commands {
public void Dispatch(ChatCommandContext ctx) { public void Dispatch(ChatCommandContext ctx) {
if(!ctx.User.Can(ChatUserPermissions.Broadcast)) { if(!ctx.User.Can(ChatUserPermissions.Broadcast)) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
return; return;
} }
ctx.Chat.Send(new LegacyCommandResponse(LCR.BROADCAST, false, string.Join(' ', ctx.Args))); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.BROADCAST, false, string.Join(' ', ctx.Args)));
} }
} }
} }

View file

@ -9,7 +9,7 @@ namespace SharpChat.Commands {
public void Dispatch(ChatCommandContext ctx) { public void Dispatch(ChatCommandContext ctx) {
if(ctx.User.Can(ChatUserPermissions.CreateChannel)) { if(ctx.User.Can(ChatUserPermissions.CreateChannel)) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
return; return;
} }
@ -17,7 +17,7 @@ namespace SharpChat.Commands {
bool createChanHasHierarchy; bool createChanHasHierarchy;
if(!ctx.Args.Any() || (createChanHasHierarchy = firstArg.All(char.IsDigit) && ctx.Args.Length < 2)) { if(!ctx.Args.Any() || (createChanHasHierarchy = firstArg.All(char.IsDigit) && ctx.Args.Length < 2)) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR));
return; return;
} }
@ -27,20 +27,20 @@ namespace SharpChat.Commands {
createChanHierarchy = 0; createChanHierarchy = 0;
if(createChanHierarchy > ctx.User.Rank) { if(createChanHierarchy > ctx.User.Rank) {
ctx.User.Send(new LegacyCommandResponse(LCR.INSUFFICIENT_HIERARCHY)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.INSUFFICIENT_HIERARCHY));
return; return;
} }
string createChanName = string.Join('_', ctx.Args.Skip(createChanHasHierarchy ? 1 : 0)); string createChanName = string.Join('_', ctx.Args.Skip(createChanHasHierarchy ? 1 : 0));
if(!ChatChannel.CheckName(createChanName)) { if(!ChatChannel.CheckName(createChanName)) {
ctx.User.Send(new LegacyCommandResponse(LCR.CHANNEL_NAME_INVALID)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_NAME_INVALID));
return; return;
} }
lock(ctx.Chat.ChannelsAccess) { lock(ctx.Chat.ChannelsAccess) {
if(ctx.Chat.Channels.Any(c => c.NameEquals(createChanName))) { if(ctx.Chat.Channels.Any(c => c.NameEquals(createChanName))) {
ctx.User.Send(new LegacyCommandResponse(LCR.CHANNEL_ALREADY_EXISTS, true, createChanName)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_ALREADY_EXISTS, true, createChanName));
return; return;
} }
@ -54,11 +54,11 @@ namespace SharpChat.Commands {
ctx.Chat.Channels.Add(createChan); ctx.Chat.Channels.Add(createChan);
lock(ctx.Chat.UsersAccess) { lock(ctx.Chat.UsersAccess) {
foreach(ChatUser ccu in ctx.Chat.Users.Where(u => u.Rank >= ctx.Channel.Rank)) foreach(ChatUser ccu in ctx.Chat.Users.Where(u => u.Rank >= ctx.Channel.Rank))
ccu.Send(new ChannelCreatePacket(ctx.Channel)); ctx.Chat.SendTo(ccu, new ChannelCreatePacket(ctx.Channel));
} }
ctx.Chat.SwitchChannel(ctx.User, createChan, createChan.Password); ctx.Chat.SwitchChannel(ctx.User, createChan, createChan.Password);
ctx.User.Send(new LegacyCommandResponse(LCR.CHANNEL_CREATED, false, createChan.Name)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_CREATED, false, createChan.Name));
} }
} }
} }

View file

@ -12,7 +12,7 @@ namespace SharpChat.Commands {
public void Dispatch(ChatCommandContext ctx) { public void Dispatch(ChatCommandContext ctx) {
if(!ctx.Args.Any() || string.IsNullOrWhiteSpace(ctx.Args.FirstOrDefault())) { if(!ctx.Args.Any() || string.IsNullOrWhiteSpace(ctx.Args.FirstOrDefault())) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR));
return; return;
} }
@ -22,18 +22,18 @@ namespace SharpChat.Commands {
delChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(delChanName)); delChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(delChanName));
if(delChan == null) { if(delChan == null) {
ctx.User.Send(new LegacyCommandResponse(LCR.CHANNEL_NOT_FOUND, true, delChanName)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_NOT_FOUND, true, delChanName));
return; return;
} }
if(!ctx.User.Can(ChatUserPermissions.DeleteChannel) && delChan.Owner != ctx.User) { if(!ctx.User.Can(ChatUserPermissions.DeleteChannel) && delChan.Owner != ctx.User) {
ctx.User.Send(new LegacyCommandResponse(LCR.CHANNEL_DELETE_FAILED, true, delChan.Name)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_DELETE_FAILED, true, delChan.Name));
return; return;
} }
lock(ctx.Chat.ChannelsAccess) lock(ctx.Chat.ChannelsAccess)
ctx.Chat.RemoveChannel(delChan); ctx.Chat.RemoveChannel(delChan);
ctx.User.Send(new LegacyCommandResponse(LCR.CHANNEL_DELETED, false, delChan.Name)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_DELETED, false, delChan.Name));
} }
} }
} }

View file

@ -15,14 +15,14 @@ namespace SharpChat.Commands {
bool deleteAnyMessage = ctx.User.Can(ChatUserPermissions.DeleteAnyMessage); bool deleteAnyMessage = ctx.User.Can(ChatUserPermissions.DeleteAnyMessage);
if(!deleteAnyMessage && !ctx.User.Can(ChatUserPermissions.DeleteOwnMessage)) { if(!deleteAnyMessage && !ctx.User.Can(ChatUserPermissions.DeleteOwnMessage)) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
return; return;
} }
string firstArg = ctx.Args.FirstOrDefault(); string firstArg = ctx.Args.FirstOrDefault();
if(string.IsNullOrWhiteSpace(firstArg) || !firstArg.All(char.IsDigit) || !long.TryParse(firstArg, out long delSeqId)) { if(string.IsNullOrWhiteSpace(firstArg) || !firstArg.All(char.IsDigit) || !long.TryParse(firstArg, out long delSeqId)) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR));
return; return;
} }
@ -30,7 +30,7 @@ namespace SharpChat.Commands {
IChatEvent delMsg = ctx.Chat.Events.GetEvent(delSeqId); IChatEvent delMsg = ctx.Chat.Events.GetEvent(delSeqId);
if(delMsg == null || delMsg.Sender.Rank > ctx.User.Rank || (!deleteAnyMessage && delMsg.Sender.UserId != ctx.User.UserId)) { if(delMsg == null || delMsg.Sender.Rank > ctx.User.Rank || (!deleteAnyMessage && delMsg.Sender.UserId != ctx.User.UserId)) {
ctx.User.Send(new LegacyCommandResponse(LCR.MESSAGE_DELETE_ERROR)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.MESSAGE_DELETE_ERROR));
return; return;
} }

View file

@ -14,8 +14,8 @@ namespace SharpChat.Commands {
joinChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(joinChanStr)); joinChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(joinChanStr));
if(joinChan == null) { if(joinChan == null) {
ctx.User.Send(new LegacyCommandResponse(LCR.CHANNEL_NOT_FOUND, true, joinChanStr)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_NOT_FOUND, true, joinChanStr));
ctx.User.ForceChannel(); ctx.Chat.ForceChannel(ctx.User);
return; return;
} }

View file

@ -21,7 +21,7 @@ namespace SharpChat.Commands {
bool isBanning = ctx.NameEquals("ban"); bool isBanning = ctx.NameEquals("ban");
if(!ctx.User.Can(isBanning ? ChatUserPermissions.BanUser : ChatUserPermissions.KickUser)) { if(!ctx.User.Can(isBanning ? ChatUserPermissions.BanUser : ChatUserPermissions.KickUser)) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
return; return;
} }
@ -32,19 +32,19 @@ namespace SharpChat.Commands {
lock(ctx.Chat.UsersAccess) lock(ctx.Chat.UsersAccess)
if(banUserTarget == null || (banUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(banUserTarget))) == null) { if(banUserTarget == null || (banUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(banUserTarget))) == null) {
ctx.User.Send(new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, banUser == null ? "User" : banUserTarget)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, banUser == null ? "User" : banUserTarget));
return; return;
} }
if(banUser == ctx.User || banUser.Rank >= ctx.User.Rank) { if(banUser == ctx.User || banUser.Rank >= ctx.User.Rank) {
ctx.User.Send(new LegacyCommandResponse(LCR.KICK_NOT_ALLOWED, true, banUser.DisplayName)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.KICK_NOT_ALLOWED, true, banUser.DisplayName));
return; return;
} }
TimeSpan duration = isBanning ? TimeSpan.MaxValue : TimeSpan.Zero; TimeSpan duration = isBanning ? TimeSpan.MaxValue : TimeSpan.Zero;
if(!string.IsNullOrWhiteSpace(banDurationStr) && double.TryParse(banDurationStr, out double durationSeconds)) { if(!string.IsNullOrWhiteSpace(banDurationStr) && double.TryParse(banDurationStr, out double durationSeconds)) {
if(durationSeconds < 0) { if(durationSeconds < 0) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR));
return; return;
} }
@ -60,18 +60,19 @@ namespace SharpChat.Commands {
string banReason = string.Join(' ', ctx.Args.Skip(banReasonIndex)); string banReason = string.Join(' ', ctx.Args.Skip(banReasonIndex));
Task.Run(async () => { Task.Run(async () => {
string userId = banUser.UserId.ToString();
string userIp = ctx.Chat.GetRemoteAddresses(banUser).FirstOrDefault()?.ToString() ?? string.Empty;
// obviously it makes no sense to only check for one ip address but that's current misuzu limitations // obviously it makes no sense to only check for one ip address but that's current misuzu limitations
MisuzuBanInfo fbi = await Misuzu.CheckBanAsync( MisuzuBanInfo fbi = await Misuzu.CheckBanAsync(userId, userIp);
banUser.UserId.ToString(), banUser.RemoteAddresses.First().ToString()
);
if(fbi.IsBanned && !fbi.HasExpired) { if(fbi.IsBanned && !fbi.HasExpired) {
ctx.User.Send(new LegacyCommandResponse(LCR.KICK_NOT_ALLOWED, true, banUser.DisplayName)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.KICK_NOT_ALLOWED, true, banUser.DisplayName));
return; return;
} }
await Misuzu.CreateBanAsync( await Misuzu.CreateBanAsync(
banUser.UserId.ToString(), banUser.RemoteAddresses.First().ToString(), userId, userIp,
ctx.User.UserId.ToString(), ctx.Connection.RemoteAddress.ToString(), ctx.User.UserId.ToString(), ctx.Connection.RemoteAddress.ToString(),
duration, banReason duration, banReason
); );

View file

@ -11,7 +11,7 @@ namespace SharpChat.Commands {
bool setOthersNick = ctx.User.Can(ChatUserPermissions.SetOthersNickname); bool setOthersNick = ctx.User.Can(ChatUserPermissions.SetOthersNickname);
if(!setOthersNick && !ctx.User.Can(ChatUserPermissions.SetOwnNickname)) { if(!setOthersNick && !ctx.User.Can(ChatUserPermissions.SetOwnNickname)) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
return; return;
} }
ChatUser targetUser = null; ChatUser targetUser = null;
@ -26,7 +26,7 @@ namespace SharpChat.Commands {
targetUser ??= ctx.User; targetUser ??= ctx.User;
if(ctx.Args.Length < offset) { if(ctx.Args.Length < offset) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR));
return; return;
} }
@ -44,13 +44,13 @@ namespace SharpChat.Commands {
lock(ctx.Chat.UsersAccess) lock(ctx.Chat.UsersAccess)
if(!string.IsNullOrWhiteSpace(nickStr) && ctx.Chat.Users.Any(u => u.NameEquals(nickStr))) { if(!string.IsNullOrWhiteSpace(nickStr) && ctx.Chat.Users.Any(u => u.NameEquals(nickStr))) {
ctx.User.Send(new LegacyCommandResponse(LCR.NAME_IN_USE, true, nickStr)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.NAME_IN_USE, true, nickStr));
return; return;
} }
string previousName = targetUser == ctx.User ? (targetUser.Nickname ?? targetUser.Username) : null; string previousName = targetUser == ctx.User ? (targetUser.Nickname ?? targetUser.Username) : null;
targetUser.Nickname = nickStr; targetUser.Nickname = nickStr;
ctx.Channel.Send(new UserUpdatePacket(targetUser, previousName)); ctx.Chat.SendTo(ctx.Channel, new UserUpdatePacket(targetUser, previousName));
} }
} }
} }

View file

@ -20,13 +20,13 @@ namespace SharpChat.Commands {
public void Dispatch(ChatCommandContext ctx) { public void Dispatch(ChatCommandContext ctx) {
if(!ctx.User.Can(ChatUserPermissions.BanUser | ChatUserPermissions.KickUser)) { if(!ctx.User.Can(ChatUserPermissions.BanUser | ChatUserPermissions.KickUser)) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
return; return;
} }
string unbanAddrTarget = ctx.Args.FirstOrDefault(); string unbanAddrTarget = ctx.Args.FirstOrDefault();
if(string.IsNullOrWhiteSpace(unbanAddrTarget) || !IPAddress.TryParse(unbanAddrTarget, out IPAddress unbanAddr)) { if(string.IsNullOrWhiteSpace(unbanAddrTarget) || !IPAddress.TryParse(unbanAddrTarget, out IPAddress unbanAddr)) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR));
return; return;
} }
@ -36,15 +36,15 @@ namespace SharpChat.Commands {
MisuzuBanInfo banInfo = await Misuzu.CheckBanAsync(ipAddr: unbanAddrTarget); MisuzuBanInfo banInfo = await Misuzu.CheckBanAsync(ipAddr: unbanAddrTarget);
if(!banInfo.IsBanned || banInfo.HasExpired) { if(!banInfo.IsBanned || banInfo.HasExpired) {
ctx.User.Send(new LegacyCommandResponse(LCR.USER_NOT_BANNED, true, unbanAddrTarget)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_BANNED, true, unbanAddrTarget));
return; return;
} }
bool wasBanned = await Misuzu.RevokeBanAsync(banInfo, MisuzuClient.BanRevokeKind.RemoteAddress); bool wasBanned = await Misuzu.RevokeBanAsync(banInfo, MisuzuClient.BanRevokeKind.RemoteAddress);
if(wasBanned) if(wasBanned)
ctx.User.Send(new LegacyCommandResponse(LCR.USER_UNBANNED, false, unbanAddrTarget)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_UNBANNED, false, unbanAddrTarget));
else else
ctx.User.Send(new LegacyCommandResponse(LCR.USER_NOT_BANNED, true, unbanAddrTarget)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_BANNED, true, unbanAddrTarget));
}).Wait(); }).Wait();
} }
} }

View file

@ -19,14 +19,14 @@ namespace SharpChat.Commands {
public void Dispatch(ChatCommandContext ctx) { public void Dispatch(ChatCommandContext ctx) {
if(!ctx.User.Can(ChatUserPermissions.BanUser | ChatUserPermissions.KickUser)) { if(!ctx.User.Can(ChatUserPermissions.BanUser | ChatUserPermissions.KickUser)) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
return; return;
} }
bool unbanUserTargetIsName = true; bool unbanUserTargetIsName = true;
string unbanUserTarget = ctx.Args.FirstOrDefault(); string unbanUserTarget = ctx.Args.FirstOrDefault();
if(string.IsNullOrWhiteSpace(unbanUserTarget)) { if(string.IsNullOrWhiteSpace(unbanUserTarget)) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR));
return; return;
} }
@ -46,15 +46,15 @@ namespace SharpChat.Commands {
MisuzuBanInfo banInfo = await Misuzu.CheckBanAsync(unbanUserTarget, userIdIsName: unbanUserTargetIsName); MisuzuBanInfo banInfo = await Misuzu.CheckBanAsync(unbanUserTarget, userIdIsName: unbanUserTargetIsName);
if(!banInfo.IsBanned || banInfo.HasExpired) { if(!banInfo.IsBanned || banInfo.HasExpired) {
ctx.User.Send(new LegacyCommandResponse(LCR.USER_NOT_BANNED, true, unbanUserTarget)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_BANNED, true, unbanUserTarget));
return; return;
} }
bool wasBanned = await Misuzu.RevokeBanAsync(banInfo, MisuzuClient.BanRevokeKind.UserId); bool wasBanned = await Misuzu.RevokeBanAsync(banInfo, MisuzuClient.BanRevokeKind.UserId);
if(wasBanned) if(wasBanned)
ctx.User.Send(new LegacyCommandResponse(LCR.USER_UNBANNED, false, unbanUserTarget)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_UNBANNED, false, unbanUserTarget));
else else
ctx.User.Send(new LegacyCommandResponse(LCR.USER_NOT_BANNED, true, unbanUserTarget)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_BANNED, true, unbanUserTarget));
}).Wait(); }).Wait();
} }
} }

View file

@ -9,7 +9,7 @@ namespace SharpChat.Commands {
public void Dispatch(ChatCommandContext ctx) { public void Dispatch(ChatCommandContext ctx) {
if(!ctx.User.Can(ChatUserPermissions.SetChannelPassword) || ctx.Channel.Owner != ctx.User) { if(!ctx.User.Can(ChatUserPermissions.SetChannelPassword) || ctx.Channel.Owner != ctx.User) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
return; return;
} }
@ -20,7 +20,7 @@ namespace SharpChat.Commands {
lock(ctx.Chat.ChannelsAccess) lock(ctx.Chat.ChannelsAccess)
ctx.Chat.UpdateChannel(ctx.Channel, password: chanPass); ctx.Chat.UpdateChannel(ctx.Channel, password: chanPass);
ctx.User.Send(new LegacyCommandResponse(LCR.CHANNEL_PASSWORD_CHANGED, false)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_PASSWORD_CHANGED, false));
} }
} }
} }

View file

@ -11,18 +11,18 @@ namespace SharpChat.Commands {
public void Dispatch(ChatCommandContext ctx) { public void Dispatch(ChatCommandContext ctx) {
if(!ctx.User.Can(ChatUserPermissions.SetChannelHierarchy) || ctx.Channel.Owner != ctx.User) { if(!ctx.User.Can(ChatUserPermissions.SetChannelHierarchy) || ctx.Channel.Owner != ctx.User) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
return; return;
} }
if(!ctx.Args.Any() || !int.TryParse(ctx.Args.First(), out int chanHierarchy) || chanHierarchy > ctx.User.Rank) { if(!ctx.Args.Any() || !int.TryParse(ctx.Args.First(), out int chanHierarchy) || chanHierarchy > ctx.User.Rank) {
ctx.User.Send(new LegacyCommandResponse(LCR.INSUFFICIENT_HIERARCHY)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.INSUFFICIENT_HIERARCHY));
return; return;
} }
lock(ctx.Chat.ChannelsAccess) lock(ctx.Chat.ChannelsAccess)
ctx.Chat.UpdateChannel(ctx.Channel, hierarchy: chanHierarchy); ctx.Chat.UpdateChannel(ctx.Channel, hierarchy: chanHierarchy);
ctx.User.Send(new LegacyCommandResponse(LCR.CHANNEL_HIERARCHY_CHANGED, false)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_HIERARCHY_CHANGED, false));
} }
} }
} }

View file

@ -11,7 +11,7 @@ namespace SharpChat.Commands {
public void Dispatch(ChatCommandContext ctx) { public void Dispatch(ChatCommandContext ctx) {
if(!ctx.User.Can(ChatUserPermissions.SeeIPAddress)) { if(!ctx.User.Can(ChatUserPermissions.SeeIPAddress)) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, "/ip")); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, "/ip"));
return; return;
} }
@ -20,12 +20,12 @@ namespace SharpChat.Commands {
lock(ctx.Chat.UsersAccess) lock(ctx.Chat.UsersAccess)
if(string.IsNullOrWhiteSpace(ipUserStr) || (ipUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(ipUserStr))) == null) { if(string.IsNullOrWhiteSpace(ipUserStr) || (ipUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(ipUserStr))) == null) {
ctx.User.Send(new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, ipUserStr ?? "User")); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, ipUserStr ?? "User"));
return; return;
} }
foreach(IPAddress ip in ipUser.RemoteAddresses.Distinct().ToArray()) foreach(IPAddress ip in ctx.Chat.GetRemoteAddresses(ipUser))
ctx.User.Send(new LegacyCommandResponse(LCR.IP_ADDRESS, false, ipUser.Username, ip)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.IP_ADDRESS, false, ipUser.Username, ip));
} }
} }
} }

View file

@ -19,7 +19,7 @@ namespace SharpChat.Commands {
public void Dispatch(ChatCommandContext ctx) { public void Dispatch(ChatCommandContext ctx) {
if(ctx.User.UserId != 1) { if(ctx.User.UserId != 1) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
return; return;
} }

View file

@ -10,7 +10,7 @@ namespace SharpChat.Commands {
public void Dispatch(ChatCommandContext ctx) { public void Dispatch(ChatCommandContext ctx) {
if(!ctx.User.Can(ChatUserPermissions.SilenceUser)) { if(!ctx.User.Can(ChatUserPermissions.SilenceUser)) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
return; return;
} }
@ -19,22 +19,22 @@ namespace SharpChat.Commands {
lock(ctx.Chat.UsersAccess) lock(ctx.Chat.UsersAccess)
if(string.IsNullOrWhiteSpace(silUserStr) || (silUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(silUserStr))) == null) { if(string.IsNullOrWhiteSpace(silUserStr) || (silUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(silUserStr))) == null) {
ctx.User.Send(new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, silUserStr ?? "User")); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, silUserStr ?? "User"));
return; return;
} }
if(silUser == ctx.User) { if(silUser == ctx.User) {
ctx.User.Send(new LegacyCommandResponse(LCR.SILENCE_SELF)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.SILENCE_SELF));
return; return;
} }
if(silUser.Rank >= ctx.User.Rank) { if(silUser.Rank >= ctx.User.Rank) {
ctx.User.Send(new LegacyCommandResponse(LCR.SILENCE_HIERARCHY)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.SILENCE_HIERARCHY));
return; return;
} }
if(silUser.IsSilenced) { if(silUser.IsSilenced) {
ctx.User.Send(new LegacyCommandResponse(LCR.SILENCE_ALREADY)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.SILENCE_ALREADY));
return; return;
} }
@ -42,7 +42,7 @@ namespace SharpChat.Commands {
if(ctx.Args.Length > 1) { if(ctx.Args.Length > 1) {
if(!double.TryParse(ctx.Args.ElementAt(1), out double silenceSeconds)) { if(!double.TryParse(ctx.Args.ElementAt(1), out double silenceSeconds)) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR));
return; return;
} }
@ -50,8 +50,8 @@ namespace SharpChat.Commands {
} }
silUser.SilencedUntil = silenceUntil; silUser.SilencedUntil = silenceUntil;
silUser.Send(new LegacyCommandResponse(LCR.SILENCED, false)); ctx.Chat.SendTo(silUser, new LegacyCommandResponse(LCR.SILENCED, false));
ctx.User.Send(new LegacyCommandResponse(LCR.TARGET_SILENCED, false, silUser.DisplayName)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.TARGET_SILENCED, false, silUser.DisplayName));
} }
} }
} }

View file

@ -10,7 +10,7 @@ namespace SharpChat.Commands {
public void Dispatch(ChatCommandContext ctx) { public void Dispatch(ChatCommandContext ctx) {
if(!ctx.User.Can(ChatUserPermissions.SilenceUser)) { if(!ctx.User.Can(ChatUserPermissions.SilenceUser)) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
return; return;
} }
@ -19,23 +19,23 @@ namespace SharpChat.Commands {
lock(ctx.Chat.UsersAccess) lock(ctx.Chat.UsersAccess)
if(string.IsNullOrWhiteSpace(unsilUserStr) || (unsilUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(unsilUserStr))) == null) { if(string.IsNullOrWhiteSpace(unsilUserStr) || (unsilUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(unsilUserStr))) == null) {
ctx.User.Send(new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, unsilUserStr ?? "User")); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, unsilUserStr ?? "User"));
return; return;
} }
if(unsilUser.Rank >= ctx.User.Rank) { if(unsilUser.Rank >= ctx.User.Rank) {
ctx.User.Send(new LegacyCommandResponse(LCR.UNSILENCE_HIERARCHY)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.UNSILENCE_HIERARCHY));
return; return;
} }
if(!unsilUser.IsSilenced) { if(!unsilUser.IsSilenced) {
ctx.User.Send(new LegacyCommandResponse(LCR.NOT_SILENCED)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.NOT_SILENCED));
return; return;
} }
unsilUser.SilencedUntil = DateTimeOffset.MinValue; unsilUser.SilencedUntil = DateTimeOffset.MinValue;
unsilUser.Send(new LegacyCommandResponse(LCR.UNSILENCED, false)); ctx.Chat.SendTo(unsilUser, new LegacyCommandResponse(LCR.UNSILENCED, false));
ctx.User.Send(new LegacyCommandResponse(LCR.TARGET_UNSILENCED, false, unsilUser.DisplayName)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.TARGET_UNSILENCED, false, unsilUser.DisplayName));
} }
} }
} }

View file

@ -12,7 +12,7 @@ namespace SharpChat.Commands {
public void Dispatch(ChatCommandContext ctx) { public void Dispatch(ChatCommandContext ctx) {
if(ctx.Args.Length < 2) { if(ctx.Args.Length < 2) {
ctx.User.Send(new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR));
return; return;
} }
@ -22,7 +22,7 @@ namespace SharpChat.Commands {
whisperUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(whisperUserStr)); whisperUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(whisperUserStr));
if(whisperUser == null) { if(whisperUser == null) {
ctx.User.Send(new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, whisperUserStr)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, whisperUserStr));
return; return;
} }
if(whisperUser == ctx.User) if(whisperUser == ctx.User)
@ -30,7 +30,7 @@ namespace SharpChat.Commands {
string whisperStr = string.Join(' ', ctx.Args.Skip(1)); string whisperStr = string.Join(' ', ctx.Args.Skip(1));
whisperUser.Send(new ChatMessageAddPacket(new ChatMessage { ctx.Chat.SendTo(whisperUser, new ChatMessageAddPacket(new ChatMessage {
DateTime = DateTimeOffset.Now, DateTime = DateTimeOffset.Now,
Target = whisperUser, Target = whisperUser,
TargetName = whisperUser.TargetName, TargetName = whisperUser.TargetName,
@ -38,7 +38,7 @@ namespace SharpChat.Commands {
Text = whisperStr, Text = whisperStr,
Flags = ChatMessageFlags.Private, Flags = ChatMessageFlags.Private,
})); }));
ctx.User.Send(new ChatMessageAddPacket(new ChatMessage { ctx.Chat.SendTo(ctx.User, new ChatMessageAddPacket(new ChatMessage {
DateTime = DateTimeOffset.Now, DateTime = DateTimeOffset.Now,
Target = whisperUser, Target = whisperUser,
TargetName = whisperUser.TargetName, TargetName = whisperUser.TargetName,

View file

@ -28,19 +28,19 @@ namespace SharpChat.Commands {
if(whoChanSB.Length > 2) if(whoChanSB.Length > 2)
whoChanSB.Length -= 2; whoChanSB.Length -= 2;
ctx.User.Send(new LegacyCommandResponse(LCR.USERS_LISTING_SERVER, false, whoChanSB)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USERS_LISTING_SERVER, false, whoChanSB));
} else { } else {
ChatChannel whoChan; ChatChannel whoChan;
lock(ctx.Chat.ChannelsAccess) lock(ctx.Chat.ChannelsAccess)
whoChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(whoChanStr)); whoChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(whoChanStr));
if(whoChan == null) { if(whoChan == null) {
ctx.User.Send(new LegacyCommandResponse(LCR.CHANNEL_NOT_FOUND, true, whoChanStr)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_NOT_FOUND, true, whoChanStr));
return; return;
} }
if(whoChan.Rank > ctx.User.Rank || (whoChan.HasPassword && !ctx.User.Can(ChatUserPermissions.JoinAnyChannel))) { if(whoChan.Rank > ctx.User.Rank || (whoChan.HasPassword && !ctx.User.Can(ChatUserPermissions.JoinAnyChannel))) {
ctx.User.Send(new LegacyCommandResponse(LCR.USERS_LISTING_ERROR, true, whoChanStr)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USERS_LISTING_ERROR, true, whoChanStr));
return; return;
} }
@ -58,7 +58,7 @@ namespace SharpChat.Commands {
if(whoChanSB.Length > 2) if(whoChanSB.Length > 2)
whoChanSB.Length -= 2; whoChanSB.Length -= 2;
ctx.User.Send(new LegacyCommandResponse(LCR.USERS_LISTING_CHANNEL, false, whoChan.Name, whoChanSB)); ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USERS_LISTING_CHANNEL, false, whoChan.Name, whoChanSB));
} }
} }
} }

View file

@ -1,6 +1,5 @@
namespace SharpChat { namespace SharpChat {
public interface IPacketTarget { public interface IPacketTarget {
string TargetName { get; } string TargetName { get; }
void Send(IServerPacket packet);
} }
} }

View file

@ -99,28 +99,27 @@ namespace SharpChat.PacketHandlers {
} }
lock(ctx.Chat.UsersAccess) { lock(ctx.Chat.UsersAccess) {
ChatUser aUser = ctx.Chat.Users.FirstOrDefault(u => u.UserId == fai.UserId); ChatUser user = ctx.Chat.Users.FirstOrDefault(u => u.UserId == fai.UserId);
if(aUser == null) if(user == null)
aUser = new ChatUser(fai); user = new ChatUser(fai);
else { else {
aUser.ApplyAuth(fai); user.ApplyAuth(fai);
aUser.Channel?.Send(new UserUpdatePacket(aUser)); if(user.Channel != null)
ctx.Chat.SendTo(user.Channel, new UserUpdatePacket(user));
} }
// Enforce a maximum amount of connections per user // Enforce a maximum amount of connections per user
if(aUser.ConnectionCount >= MaxConnections) { lock(ctx.Chat.ConnectionsAccess)
ctx.Connection.Send(new AuthFailPacket(AuthFailReason.MaxSessions)); if(ctx.Chat.Connections.Count(conn => conn.User == user) >= MaxConnections) {
ctx.Connection.Dispose(); ctx.Connection.Send(new AuthFailPacket(AuthFailReason.MaxSessions));
return; ctx.Connection.Dispose();
} return;
}
// Bumping the ping to prevent upgrading
ctx.Connection.BumpPing(); ctx.Connection.BumpPing();
ctx.Connection.User = user;
aUser.AddConnection(ctx.Connection); ctx.Connection.Send(new LegacyCommandResponse(LCR.WELCOME, false, $"Welcome to Flashii Chat, {user.Username}!"));
ctx.Connection.Send(new LegacyCommandResponse(LCR.WELCOME, false, $"Welcome to Flashii Chat, {aUser.Username}!"));
if(File.Exists("welcome.txt")) { if(File.Exists("welcome.txt")) {
IEnumerable<string> lines = File.ReadAllLines("welcome.txt").Where(x => !string.IsNullOrWhiteSpace(x)); IEnumerable<string> lines = File.ReadAllLines("welcome.txt").Where(x => !string.IsNullOrWhiteSpace(x));
@ -130,7 +129,7 @@ namespace SharpChat.PacketHandlers {
ctx.Connection.Send(new LegacyCommandResponse(LCR.WELCOME, false, line)); ctx.Connection.Send(new LegacyCommandResponse(LCR.WELCOME, false, line));
} }
ctx.Chat.HandleJoin(aUser, DefaultChannel, ctx.Connection, MaxMessageLength); ctx.Chat.HandleJoin(user, DefaultChannel, ctx.Connection, MaxMessageLength);
} }
}).Wait(); }).Wait();
} }

View file

@ -1,6 +1,7 @@
using SharpChat.Misuzu; using SharpChat.Misuzu;
using SharpChat.Packet; using SharpChat.Packet;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -32,11 +33,16 @@ namespace SharpChat.PacketHandlers {
lock(BumpAccess) { lock(BumpAccess) {
if(LastBump < DateTimeOffset.UtcNow - BumpInterval) { if(LastBump < DateTimeOffset.UtcNow - BumpInterval) {
(string, string)[] bumpList; (string, string)[] bumpList;
lock(ctx.Chat.UsersAccess) lock(ctx.Chat.UsersAccess) {
bumpList = ctx.Chat.Users IEnumerable<ChatUser> filtered = ctx.Chat.Users.Where(u => u.Status == ChatUserStatus.Online);
.Where(u => u.HasConnections && u.Status == ChatUserStatus.Online)
.Select(u => (u.UserId.ToString(), u.RemoteAddresses.FirstOrDefault()?.ToString() ?? string.Empty)) lock(ctx.Chat.ConnectionsAccess)
filtered = filtered.Where(u => ctx.Chat.Connections.Any(c => c.User == u));
bumpList = filtered
.Select(u => (u.UserId.ToString(), ctx.Chat.GetRemoteAddresses(u).FirstOrDefault()?.ToString() ?? string.Empty))
.ToArray(); .ToArray();
}
if(bumpList.Any()) if(bumpList.Any())
Task.Run(async () => { Task.Run(async () => {

View file

@ -51,7 +51,7 @@ namespace SharpChat.PacketHandlers {
if(user.Status != ChatUserStatus.Online) { if(user.Status != ChatUserStatus.Online) {
user.Status = ChatUserStatus.Online; user.Status = ChatUserStatus.Online;
channel.Send(new UserUpdatePacket(user)); ctx.Chat.SendTo(channel, new UserUpdatePacket(user));
} }
int maxMsgLength = MaxMessageLength; int maxMsgLength = MaxMessageLength;
@ -97,7 +97,7 @@ namespace SharpChat.PacketHandlers {
lock(ctx.Chat.EventsAccess) { lock(ctx.Chat.EventsAccess) {
ctx.Chat.Events.AddEvent(message); ctx.Chat.Events.AddEvent(message);
channel.Send(new ChatMessageAddPacket(message)); ctx.Chat.SendTo(channel, new ChatMessageAddPacket(message));
} }
} }
} }

View file

@ -143,29 +143,18 @@ namespace SharpChat {
private void OnClose(IWebSocketConnection sock) { private void OnClose(IWebSocketConnection sock) {
Logger.Write($"Connection closed from {sock.ConnectionInfo.ClientIpAddress}:{sock.ConnectionInfo.ClientPort}"); Logger.Write($"Connection closed from {sock.ConnectionInfo.ClientIpAddress}:{sock.ConnectionInfo.ClientPort}");
ChatConnection conn; lock(Context.ConnectionsAccess) {
lock(Context.ConnectionsAccess) ChatConnection conn = Context.GetConnection(sock);
conn = Context.GetConnection(sock); if(conn == null)
return;
// Remove connection from user
if(conn?.User != null) {
// RemoveConnection sets conn.User to null so we must grab a local copy.
ChatUser user = conn.User;
user.RemoveConnection(conn);
if(!user.HasConnections)
Context.UserLeave(null, user);
}
// Update context
Context.Update();
// Remove connection from server
lock(Context.ConnectionsAccess)
Context.Connections.Remove(conn); Context.Connections.Remove(conn);
conn?.Dispose(); if(conn.User != null && !Context.Connections.Any(c => c.User == conn.User))
Context.UserLeave(null, conn.User);
}
Context.Update();
} }
private void OnError(IWebSocketConnection sock, Exception ex) { private void OnError(IWebSocketConnection sock, Exception ex) {
@ -210,7 +199,7 @@ namespace SharpChat {
}).Wait(); }).Wait();
return; return;
} else if(conn.User.RateLimiter.State == ChatRateLimitState.Warning) } else if(conn.User.RateLimiter.State == ChatRateLimitState.Warning)
conn.User.Send(new FloodWarningPacket()); Context.SendTo(conn.User, new FloodWarningPacket());
} }
ChatPacketHandlerContext context = new(msg, Context, conn); ChatPacketHandlerContext context = new(msg, Context, conn);