Code style updates.
This commit is contained in:
parent
c9cc5ff23a
commit
4104e40843
42 changed files with 292 additions and 317 deletions
|
@ -26,7 +26,9 @@ namespace SharpChat {
|
||||||
Username = fb.Username;
|
Username = fb.Username;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() => Username;
|
public override string ToString() {
|
||||||
|
return Username;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BannedIPAddress : IBan {
|
public class BannedIPAddress : IBan {
|
||||||
|
@ -41,7 +43,9 @@ namespace SharpChat {
|
||||||
Expires = fb.Expires;
|
Expires = fb.Expires;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() => Address.ToString();
|
public override string ToString() {
|
||||||
|
return Address.ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BanManager : IDisposable {
|
public class BanManager : IDisposable {
|
||||||
|
@ -168,8 +172,9 @@ namespace SharpChat {
|
||||||
return BanList.ToList();
|
return BanList.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
~BanManager()
|
~BanManager() {
|
||||||
=> DoDispose();
|
DoDispose();
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
DoDispose();
|
DoDispose();
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace SharpChat {
|
||||||
public class ChannelInvalidNameException : ChannelException { }
|
public class ChannelInvalidNameException : ChannelException { }
|
||||||
|
|
||||||
public class ChannelManager : IDisposable {
|
public class ChannelManager : IDisposable {
|
||||||
private readonly List<ChatChannel> Channels = new List<ChatChannel>();
|
private readonly List<ChatChannel> Channels = new();
|
||||||
|
|
||||||
public readonly ChatContext Context;
|
public readonly ChatContext Context;
|
||||||
|
|
||||||
|
@ -23,43 +23,43 @@ namespace SharpChat {
|
||||||
|
|
||||||
public ChatChannel DefaultChannel {
|
public ChatChannel DefaultChannel {
|
||||||
get {
|
get {
|
||||||
if (_DefaultChannel == null)
|
if(_DefaultChannel == null)
|
||||||
_DefaultChannel = Channels.FirstOrDefault();
|
_DefaultChannel = Channels.FirstOrDefault();
|
||||||
|
|
||||||
return _DefaultChannel;
|
return _DefaultChannel;
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
if (value == null)
|
if(value == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (Channels.Contains(value))
|
if(Channels.Contains(value))
|
||||||
_DefaultChannel = value;
|
_DefaultChannel = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void Add(ChatChannel channel) {
|
public void Add(ChatChannel channel) {
|
||||||
if (channel == null)
|
if(channel == null)
|
||||||
throw new ArgumentNullException(nameof(channel));
|
throw new ArgumentNullException(nameof(channel));
|
||||||
if (!channel.Name.All(c => char.IsLetter(c) || char.IsNumber(c) || c == '-'))
|
if(!channel.Name.All(c => char.IsLetter(c) || char.IsNumber(c) || c == '-'))
|
||||||
throw new ChannelInvalidNameException();
|
throw new ChannelInvalidNameException();
|
||||||
if (Get(channel.Name) != null)
|
if(Get(channel.Name) != null)
|
||||||
throw new ChannelExistException();
|
throw new ChannelExistException();
|
||||||
|
|
||||||
// Add channel to the listing
|
// Add channel to the listing
|
||||||
Channels.Add(channel);
|
Channels.Add(channel);
|
||||||
|
|
||||||
// Set as default if there's none yet
|
// Set as default if there's none yet
|
||||||
if (_DefaultChannel == null)
|
if(_DefaultChannel == null)
|
||||||
_DefaultChannel = channel;
|
_DefaultChannel = channel;
|
||||||
|
|
||||||
// Broadcast creation of channel
|
// Broadcast creation of channel
|
||||||
foreach (ChatUser user in Context.Users.OfHierarchy(channel.Rank))
|
foreach(ChatUser user in Context.Users.OfHierarchy(channel.Rank))
|
||||||
user.Send(new ChannelCreatePacket(channel));
|
user.Send(new ChannelCreatePacket(channel));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Remove(ChatChannel channel) {
|
public void Remove(ChatChannel channel) {
|
||||||
if (channel == null || channel == DefaultChannel)
|
if(channel == null || channel == DefaultChannel)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Remove channel from the listing
|
// Remove channel from the listing
|
||||||
|
@ -67,94 +67,94 @@ namespace SharpChat {
|
||||||
|
|
||||||
// Move all users back to the main channel
|
// 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.
|
// TODO: Replace this with a kick. SCv2 supports being in 0 channels, SCv1 should force the user back to DefaultChannel.
|
||||||
foreach (ChatUser user in channel.GetUsers()) {
|
foreach(ChatUser user in channel.GetUsers()) {
|
||||||
Context.SwitchChannel(user, DefaultChannel, string.Empty);
|
Context.SwitchChannel(user, DefaultChannel, string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Broadcast deletion of channel
|
// Broadcast deletion of channel
|
||||||
foreach (ChatUser user in Context.Users.OfHierarchy(channel.Rank))
|
foreach(ChatUser user in Context.Users.OfHierarchy(channel.Rank))
|
||||||
user.Send(new ChannelDeletePacket(channel));
|
user.Send(new ChannelDeletePacket(channel));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Contains(ChatChannel chan) {
|
public bool Contains(ChatChannel chan) {
|
||||||
if (chan == null)
|
if(chan == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
lock (Channels)
|
lock(Channels)
|
||||||
return Channels.Contains(chan) || Channels.Any(c => c.Name.ToLowerInvariant() == chan.Name.ToLowerInvariant());
|
return Channels.Contains(chan) || Channels.Any(c => c.Name.ToLowerInvariant() == chan.Name.ToLowerInvariant());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(ChatChannel channel, string name = null, bool? temporary = null, int? hierarchy = null, string password = null) {
|
public void Update(ChatChannel channel, string name = null, bool? temporary = null, int? hierarchy = null, string password = null) {
|
||||||
if (channel == null)
|
if(channel == null)
|
||||||
throw new ArgumentNullException(nameof(channel));
|
throw new ArgumentNullException(nameof(channel));
|
||||||
if (!Channels.Contains(channel))
|
if(!Channels.Contains(channel))
|
||||||
throw new ArgumentException(@"Provided channel is not registered with this manager.", nameof(channel));
|
throw new ArgumentException(@"Provided channel is not registered with this manager.", nameof(channel));
|
||||||
|
|
||||||
string prevName = channel.Name;
|
string prevName = channel.Name;
|
||||||
int prevHierarchy = channel.Rank;
|
int prevHierarchy = channel.Rank;
|
||||||
bool nameUpdated = !string.IsNullOrWhiteSpace(name) && name != prevName;
|
bool nameUpdated = !string.IsNullOrWhiteSpace(name) && name != prevName;
|
||||||
|
|
||||||
if (nameUpdated) {
|
if(nameUpdated) {
|
||||||
if (!name.All(c => char.IsLetter(c) || char.IsNumber(c) || c == '-'))
|
if(!name.All(c => char.IsLetter(c) || char.IsNumber(c) || c == '-'))
|
||||||
throw new ChannelInvalidNameException();
|
throw new ChannelInvalidNameException();
|
||||||
if (Get(name) != null)
|
if(Get(name) != null)
|
||||||
throw new ChannelExistException();
|
throw new ChannelExistException();
|
||||||
|
|
||||||
channel.Name = name;
|
channel.Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (temporary.HasValue)
|
if(temporary.HasValue)
|
||||||
channel.IsTemporary = temporary.Value;
|
channel.IsTemporary = temporary.Value;
|
||||||
|
|
||||||
if (hierarchy.HasValue)
|
if(hierarchy.HasValue)
|
||||||
channel.Rank = hierarchy.Value;
|
channel.Rank = hierarchy.Value;
|
||||||
|
|
||||||
if (password != null)
|
if(password != null)
|
||||||
channel.Password = password;
|
channel.Password = password;
|
||||||
|
|
||||||
// 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
|
||||||
foreach (ChatUser user in Context.Users.OfHierarchy(channel.Rank)) {
|
foreach(ChatUser user in Context.Users.OfHierarchy(channel.Rank)) {
|
||||||
user.Send(new ChannelUpdatePacket(prevName, channel));
|
user.Send(new ChannelUpdatePacket(prevName, channel));
|
||||||
|
|
||||||
if (nameUpdated)
|
if(nameUpdated)
|
||||||
user.ForceChannel();
|
user.ForceChannel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChatChannel Get(string name) {
|
public ChatChannel Get(string name) {
|
||||||
if (string.IsNullOrWhiteSpace(name))
|
if(string.IsNullOrWhiteSpace(name))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return Channels.FirstOrDefault(x => x.Name.ToLowerInvariant() == name.ToLowerInvariant());
|
return Channels.FirstOrDefault(x => x.Name.ToLowerInvariant() == name.ToLowerInvariant());
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<ChatChannel> GetUser(ChatUser user) {
|
public IEnumerable<ChatChannel> GetUser(ChatUser user) {
|
||||||
if (user == null)
|
if(user == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return Channels.Where(x => x.HasUser(user));
|
return Channels.Where(x => x.HasUser(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<ChatChannel> OfHierarchy(int hierarchy) {
|
public IEnumerable<ChatChannel> OfHierarchy(int hierarchy) {
|
||||||
lock (Channels)
|
lock(Channels)
|
||||||
return Channels.Where(c => c.Rank <= hierarchy).ToList();
|
return Channels.Where(c => c.Rank <= hierarchy).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
~ChannelManager()
|
~ChannelManager() {
|
||||||
=> Dispose(false);
|
DoDispose();
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose() {
|
||||||
=> Dispose(true);
|
DoDispose();
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
private void Dispose(bool disposing) {
|
private void DoDispose() {
|
||||||
if (IsDisposed)
|
if(IsDisposed)
|
||||||
return;
|
return;
|
||||||
IsDisposed = true;
|
IsDisposed = true;
|
||||||
|
|
||||||
Channels.Clear();
|
Channels.Clear();
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,7 @@ namespace SharpChat {
|
||||||
public int Rank { get; set; } = 0;
|
public int Rank { get; set; } = 0;
|
||||||
public ChatUser Owner { get; set; } = null;
|
public ChatUser Owner { get; set; } = null;
|
||||||
|
|
||||||
private List<ChatUser> Users { get; } = new List<ChatUser>();
|
private List<ChatUser> Users { get; } = new();
|
||||||
private List<ChatChannelTyping> Typing { get; } = new List<ChatChannelTyping>();
|
|
||||||
|
|
||||||
public bool HasPassword
|
public bool HasPassword
|
||||||
=> !string.IsNullOrWhiteSpace(Password);
|
=> !string.IsNullOrWhiteSpace(Password);
|
||||||
|
@ -26,73 +25,51 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasUser(ChatUser user) {
|
public bool HasUser(ChatUser user) {
|
||||||
lock (Users)
|
lock(Users)
|
||||||
return Users.Contains(user);
|
return Users.Contains(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UserJoin(ChatUser user) {
|
public void UserJoin(ChatUser user) {
|
||||||
if (!user.InChannel(this)) {
|
if(!user.InChannel(this)) {
|
||||||
// Remove this, a different means for this should be established for V1 compat.
|
// Remove this, a different means for this should be established for V1 compat.
|
||||||
user.Channel?.UserLeave(user);
|
user.Channel?.UserLeave(user);
|
||||||
user.JoinChannel(this);
|
user.JoinChannel(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (Users) {
|
lock(Users) {
|
||||||
if (!HasUser(user))
|
if(!HasUser(user))
|
||||||
Users.Add(user);
|
Users.Add(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UserLeave(ChatUser user) {
|
public void UserLeave(ChatUser user) {
|
||||||
lock (Users)
|
lock(Users)
|
||||||
Users.Remove(user);
|
Users.Remove(user);
|
||||||
|
|
||||||
if (user.InChannel(this))
|
if(user.InChannel(this))
|
||||||
user.LeaveChannel(this);
|
user.LeaveChannel(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Send(IServerPacket packet) {
|
public void Send(IServerPacket packet) {
|
||||||
lock (Users) {
|
lock(Users) {
|
||||||
foreach (ChatUser user in Users)
|
foreach(ChatUser user in Users)
|
||||||
user.Send(packet);
|
user.Send(packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<ChatUser> GetUsers(IEnumerable<ChatUser> exclude = null) {
|
public IEnumerable<ChatUser> GetUsers(IEnumerable<ChatUser> exclude = null) {
|
||||||
lock (Users) {
|
lock(Users) {
|
||||||
IEnumerable<ChatUser> users = Users.OrderByDescending(x => x.Rank);
|
IEnumerable<ChatUser> users = Users.OrderByDescending(x => x.Rank);
|
||||||
|
|
||||||
if (exclude != null)
|
if(exclude != null)
|
||||||
users = users.Except(exclude);
|
users = users.Except(exclude);
|
||||||
|
|
||||||
return users.ToList();
|
return users.ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsTyping(ChatUser user) {
|
|
||||||
if(user == null)
|
|
||||||
return false;
|
|
||||||
lock(Typing)
|
|
||||||
return Typing.Any(x => x.User == user && !x.HasExpired);
|
|
||||||
}
|
|
||||||
public bool CanType(ChatUser user) {
|
|
||||||
if(user == null || !HasUser(user))
|
|
||||||
return false;
|
|
||||||
return !IsTyping(user);
|
|
||||||
}
|
|
||||||
public ChatChannelTyping RegisterTyping(ChatUser user) {
|
|
||||||
if(user == null || !HasUser(user))
|
|
||||||
return null;
|
|
||||||
ChatChannelTyping typing = new ChatChannelTyping(user);
|
|
||||||
lock(Typing) {
|
|
||||||
Typing.RemoveAll(x => x.HasExpired);
|
|
||||||
Typing.Add(typing);
|
|
||||||
}
|
|
||||||
return typing;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Pack() {
|
public string Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append(Name);
|
sb.Append(Name);
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace SharpChat {
|
|
||||||
public class ChatChannelTyping {
|
|
||||||
public static TimeSpan Lifetime { get; } = TimeSpan.FromSeconds(5);
|
|
||||||
|
|
||||||
public ChatUser User { get; }
|
|
||||||
public DateTimeOffset Started { get; }
|
|
||||||
|
|
||||||
public bool HasExpired
|
|
||||||
=> DateTimeOffset.Now - Started > Lifetime;
|
|
||||||
|
|
||||||
public ChatChannelTyping(ChatUser user) {
|
|
||||||
User = user ?? throw new ArgumentNullException(nameof(user));
|
|
||||||
Started = DateTimeOffset.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,7 +15,7 @@
|
||||||
public bool Inherit {
|
public bool Inherit {
|
||||||
get => (Raw & INHERIT) > 0;
|
get => (Raw & INHERIT) > 0;
|
||||||
set {
|
set {
|
||||||
if (value)
|
if(value)
|
||||||
Raw |= INHERIT;
|
Raw |= INHERIT;
|
||||||
else
|
else
|
||||||
Raw &= ~INHERIT;
|
Raw &= ~INHERIT;
|
||||||
|
@ -47,7 +47,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() {
|
public override string ToString() {
|
||||||
if (Inherit)
|
if(Inherit)
|
||||||
return @"inherit";
|
return @"inherit";
|
||||||
return string.Format(@"#{0:X6}", Raw);
|
return string.Format(@"#{0:X6}", Raw);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,10 +28,10 @@ namespace SharpChat {
|
||||||
public ChatContext(HttpClient httpClient, SockChatServer server) {
|
public ChatContext(HttpClient httpClient, SockChatServer server) {
|
||||||
HttpClient = httpClient;
|
HttpClient = httpClient;
|
||||||
Server = server;
|
Server = server;
|
||||||
Bans = new BanManager(httpClient, this);
|
Bans = new(httpClient, this);
|
||||||
Users = new UserManager(this);
|
Users = new(this);
|
||||||
Channels = new ChannelManager(this);
|
Channels = new(this);
|
||||||
Events = new ChatEventManager(this);
|
Events = new(this);
|
||||||
|
|
||||||
BumpTimer = new Timer(e => FlashiiBump.SubmitAsync(HttpClient, Users.WithActiveConnections()).Wait(), null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
|
BumpTimer = new Timer(e => FlashiiBump.SubmitAsync(HttpClient, Users.WithActiveConnections()).Wait(), null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
|
||||||
}
|
}
|
||||||
|
@ -43,10 +43,10 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BanUser(ChatUser user, DateTimeOffset? until = null, bool banIPs = false, UserDisconnectReason reason = UserDisconnectReason.Kicked) {
|
public void BanUser(ChatUser user, DateTimeOffset? until = null, bool banIPs = false, UserDisconnectReason reason = UserDisconnectReason.Kicked) {
|
||||||
if (until.HasValue && until.Value <= DateTimeOffset.UtcNow)
|
if(until.HasValue && until.Value <= DateTimeOffset.UtcNow)
|
||||||
until = null;
|
until = null;
|
||||||
|
|
||||||
if (until.HasValue) {
|
if(until.HasValue) {
|
||||||
user.Send(new ForceDisconnectPacket(ForceDisconnectReason.Banned, until.Value));
|
user.Send(new ForceDisconnectPacket(ForceDisconnectReason.Banned, until.Value));
|
||||||
|
|
||||||
lock(BansAccess) {
|
lock(BansAccess) {
|
||||||
|
@ -64,7 +64,7 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleJoin(ChatUser user, ChatChannel chan, ChatUserSession sess) {
|
public void HandleJoin(ChatUser user, ChatChannel chan, ChatUserSession sess) {
|
||||||
if (!chan.HasUser(user)) {
|
if(!chan.HasUser(user)) {
|
||||||
chan.Send(new UserConnectPacket(DateTimeOffset.Now, user));
|
chan.Send(new UserConnectPacket(DateTimeOffset.Now, user));
|
||||||
Events.Add(new UserConnectEvent(DateTimeOffset.Now, user, chan));
|
Events.Add(new UserConnectEvent(DateTimeOffset.Now, user, chan));
|
||||||
}
|
}
|
||||||
|
@ -79,24 +79,24 @@ namespace SharpChat {
|
||||||
|
|
||||||
sess.Send(new ContextChannelsPacket(Channels.OfHierarchy(user.Rank)));
|
sess.Send(new ContextChannelsPacket(Channels.OfHierarchy(user.Rank)));
|
||||||
|
|
||||||
if (!chan.HasUser(user))
|
if(!chan.HasUser(user))
|
||||||
chan.UserJoin(user);
|
chan.UserJoin(user);
|
||||||
|
|
||||||
if (!Users.Contains(user))
|
if(!Users.Contains(user))
|
||||||
Users.Add(user);
|
Users.Add(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UserLeave(ChatChannel chan, ChatUser user, UserDisconnectReason reason = UserDisconnectReason.Leave) {
|
public void UserLeave(ChatChannel chan, ChatUser user, UserDisconnectReason reason = UserDisconnectReason.Leave) {
|
||||||
user.Status = ChatUserStatus.Offline;
|
user.Status = ChatUserStatus.Offline;
|
||||||
|
|
||||||
if (chan == null) {
|
if(chan == null) {
|
||||||
foreach(ChatChannel channel in user.GetChannels()) {
|
foreach(ChatChannel channel in user.GetChannels()) {
|
||||||
UserLeave(channel, user, reason);
|
UserLeave(channel, user, reason);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chan.IsTemporary && chan.Owner == user)
|
if(chan.IsTemporary && chan.Owner == user)
|
||||||
Channels.Remove(chan);
|
Channels.Remove(chan);
|
||||||
|
|
||||||
chan.UserLeave(user);
|
chan.UserLeave(user);
|
||||||
|
@ -105,20 +105,20 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
//user.Send(true, @"samechan", chan.Name);
|
||||||
user.ForceChannel();
|
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));
|
user.Send(new LegacyCommandResponse(LCR.CHANNEL_INSUFFICIENT_HIERARCHY, true, chan.Name));
|
||||||
user.ForceChannel();
|
user.ForceChannel();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chan.Password != password) {
|
if(chan.Password != password) {
|
||||||
user.Send(new LegacyCommandResponse(LCR.CHANNEL_INVALID_PASSWORD, true, chan.Name));
|
user.Send(new LegacyCommandResponse(LCR.CHANNEL_INVALID_PASSWORD, true, chan.Name));
|
||||||
user.ForceChannel();
|
user.ForceChannel();
|
||||||
return;
|
return;
|
||||||
|
@ -129,7 +129,7 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ForceChannelSwitch(ChatUser user, ChatChannel chan) {
|
public void ForceChannelSwitch(ChatUser user, ChatChannel chan) {
|
||||||
if (!Channels.Contains(chan))
|
if(!Channels.Contains(chan))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ChatChannel oldChan = user.CurrentChannel;
|
ChatChannel oldChan = user.CurrentChannel;
|
||||||
|
@ -144,20 +144,20 @@ namespace SharpChat {
|
||||||
|
|
||||||
IEnumerable<IChatEvent> msgs = Events.GetTargetLog(chan);
|
IEnumerable<IChatEvent> msgs = Events.GetTargetLog(chan);
|
||||||
|
|
||||||
foreach (IChatEvent msg in msgs)
|
foreach(IChatEvent msg in msgs)
|
||||||
user.Send(new ContextMessagePacket(msg));
|
user.Send(new ContextMessagePacket(msg));
|
||||||
|
|
||||||
user.ForceChannel(chan);
|
user.ForceChannel(chan);
|
||||||
oldChan.UserLeave(user);
|
oldChan.UserLeave(user);
|
||||||
chan.UserJoin(user);
|
chan.UserJoin(user);
|
||||||
|
|
||||||
if (oldChan.IsTemporary && oldChan.Owner == user)
|
if(oldChan.IsTemporary && oldChan.Owner == user)
|
||||||
Channels.Remove(oldChan);
|
Channels.Remove(oldChan);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CheckPings() {
|
public void CheckPings() {
|
||||||
lock(Users)
|
lock(Users)
|
||||||
foreach (ChatUser user in Users.All()) {
|
foreach(ChatUser user in Users.All()) {
|
||||||
IEnumerable<ChatUserSession> timedOut = user.GetDeadSessions();
|
IEnumerable<ChatUserSession> timedOut = user.GetDeadSessions();
|
||||||
|
|
||||||
foreach(ChatUserSession sess in timedOut) {
|
foreach(ChatUserSession sess in timedOut) {
|
||||||
|
@ -172,7 +172,7 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Send(IServerPacket packet) {
|
public void Send(IServerPacket packet) {
|
||||||
foreach (ChatUser user in Users.All())
|
foreach(ChatUser user in Users.All())
|
||||||
user.Send(packet);
|
user.Send(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +186,7 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DoDispose() {
|
private void DoDispose() {
|
||||||
if (IsDisposed)
|
if(IsDisposed)
|
||||||
return;
|
return;
|
||||||
IsDisposed = true;
|
IsDisposed = true;
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,12 @@ namespace SharpChat {
|
||||||
public ChatEventManager(ChatContext context) {
|
public ChatEventManager(ChatContext context) {
|
||||||
Context = context;
|
Context = context;
|
||||||
|
|
||||||
if (!Database.HasDatabase)
|
if(!Database.HasDatabase)
|
||||||
Events = new List<IChatEvent>();
|
Events = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Add(IChatEvent evt) {
|
public void Add(IChatEvent evt) {
|
||||||
if (evt == null)
|
if(evt == null)
|
||||||
throw new ArgumentNullException(nameof(evt));
|
throw new ArgumentNullException(nameof(evt));
|
||||||
|
|
||||||
if(Events != null)
|
if(Events != null)
|
||||||
|
@ -32,39 +32,39 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Remove(IChatEvent evt) {
|
public void Remove(IChatEvent evt) {
|
||||||
if (evt == null)
|
if(evt == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (Events != null)
|
if(Events != null)
|
||||||
lock (Events)
|
lock(Events)
|
||||||
Events.Remove(evt);
|
Events.Remove(evt);
|
||||||
|
|
||||||
if (Database.HasDatabase)
|
if(Database.HasDatabase)
|
||||||
Database.DeleteEvent(evt);
|
Database.DeleteEvent(evt);
|
||||||
|
|
||||||
Context.Send(new ChatMessageDeletePacket(evt.SequenceId));
|
Context.Send(new ChatMessageDeletePacket(evt.SequenceId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IChatEvent Get(long seqId) {
|
public IChatEvent Get(long seqId) {
|
||||||
if (seqId < 1)
|
if(seqId < 1)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
if (Database.HasDatabase)
|
if(Database.HasDatabase)
|
||||||
return Database.GetEvent(seqId);
|
return Database.GetEvent(seqId);
|
||||||
|
|
||||||
if (Events != null)
|
if(Events != null)
|
||||||
lock (Events)
|
lock(Events)
|
||||||
return Events.FirstOrDefault(e => e.SequenceId == seqId);
|
return Events.FirstOrDefault(e => e.SequenceId == seqId);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<IChatEvent> GetTargetLog(IPacketTarget target, int amount = 20, int offset = 0) {
|
public IEnumerable<IChatEvent> GetTargetLog(IPacketTarget target, int amount = 20, int offset = 0) {
|
||||||
if (Database.HasDatabase)
|
if(Database.HasDatabase)
|
||||||
return Database.GetEvents(target, amount, offset).Reverse();
|
return Database.GetEvents(target, amount, offset).Reverse();
|
||||||
|
|
||||||
if (Events != null)
|
if(Events != null)
|
||||||
lock (Events) {
|
lock(Events) {
|
||||||
IEnumerable<IChatEvent> subset = Events.Where(e => e.Target == target || e.Target == null);
|
IEnumerable<IChatEvent> subset = Events.Where(e => e.Target == target || e.Target == null);
|
||||||
|
|
||||||
int start = subset.Count() - offset - amount;
|
int start = subset.Count() - offset - amount;
|
||||||
|
@ -80,21 +80,21 @@ namespace SharpChat {
|
||||||
return Enumerable.Empty<IChatEvent>();
|
return Enumerable.Empty<IChatEvent>();
|
||||||
}
|
}
|
||||||
|
|
||||||
~ChatEventManager()
|
~ChatEventManager() {
|
||||||
=> Dispose(false);
|
DoDispose();
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose() {
|
||||||
=> Dispose(true);
|
DoDispose();
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
private void Dispose(bool disposing) {
|
private void DoDispose() {
|
||||||
if (IsDisposed)
|
if(IsDisposed)
|
||||||
return;
|
return;
|
||||||
IsDisposed = true;
|
IsDisposed = true;
|
||||||
|
|
||||||
Events?.Clear();
|
Events?.Clear();
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,16 +13,16 @@ namespace SharpChat {
|
||||||
private const int FLOOD_PROTECTION_AMOUNT = 30;
|
private const int FLOOD_PROTECTION_AMOUNT = 30;
|
||||||
private const int FLOOD_PROTECTION_THRESHOLD = 10;
|
private const int FLOOD_PROTECTION_THRESHOLD = 10;
|
||||||
|
|
||||||
private readonly Queue<DateTimeOffset> TimePoints = new Queue<DateTimeOffset>();
|
private readonly Queue<DateTimeOffset> TimePoints = new();
|
||||||
|
|
||||||
public ChatRateLimitState State {
|
public ChatRateLimitState State {
|
||||||
get {
|
get {
|
||||||
lock (TimePoints) {
|
lock(TimePoints) {
|
||||||
if (TimePoints.Count == FLOOD_PROTECTION_AMOUNT) {
|
if(TimePoints.Count == FLOOD_PROTECTION_AMOUNT) {
|
||||||
if ((TimePoints.Last() - TimePoints.First()).TotalSeconds <= FLOOD_PROTECTION_THRESHOLD)
|
if((TimePoints.Last() - TimePoints.First()).TotalSeconds <= FLOOD_PROTECTION_THRESHOLD)
|
||||||
return ChatRateLimitState.Kick;
|
return ChatRateLimitState.Kick;
|
||||||
|
|
||||||
if ((TimePoints.Last() - TimePoints.Skip(5).First()).TotalSeconds <= FLOOD_PROTECTION_THRESHOLD)
|
if((TimePoints.Last() - TimePoints.Skip(5).First()).TotalSeconds <= FLOOD_PROTECTION_THRESHOLD)
|
||||||
return ChatRateLimitState.Warning;
|
return ChatRateLimitState.Warning;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,11 +32,11 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddTimePoint(DateTimeOffset? dto = null) {
|
public void AddTimePoint(DateTimeOffset? dto = null) {
|
||||||
if (!dto.HasValue)
|
if(!dto.HasValue)
|
||||||
dto = DateTimeOffset.Now;
|
dto = DateTimeOffset.Now;
|
||||||
|
|
||||||
lock (TimePoints) {
|
lock(TimePoints) {
|
||||||
if (TimePoints.Count >= FLOOD_PROTECTION_AMOUNT)
|
if(TimePoints.Count >= FLOOD_PROTECTION_AMOUNT)
|
||||||
TimePoints.Dequeue();
|
TimePoints.Dequeue();
|
||||||
|
|
||||||
TimePoints.Enqueue(dto.Value);
|
TimePoints.Enqueue(dto.Value);
|
||||||
|
|
|
@ -23,15 +23,16 @@ namespace SharpChat {
|
||||||
public bool HasFloodProtection
|
public bool HasFloodProtection
|
||||||
=> Rank < RANK_NO_FLOOD;
|
=> Rank < RANK_NO_FLOOD;
|
||||||
|
|
||||||
public bool Equals([AllowNull] BasicUser other)
|
public bool Equals([AllowNull] BasicUser other) {
|
||||||
=> UserId == other.UserId;
|
return UserId == other.UserId;
|
||||||
|
}
|
||||||
|
|
||||||
public string DisplayName {
|
public string DisplayName {
|
||||||
get {
|
get {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
if(Status == ChatUserStatus.Away)
|
if(Status == ChatUserStatus.Away)
|
||||||
sb.AppendFormat(@"<{0}>_", StatusMessage.Substring(0, Math.Min(StatusMessage.Length, 5)).ToUpperInvariant());
|
sb.AppendFormat(@"<{0}>_", StatusMessage[..Math.Min(StatusMessage.Length, 5)].ToUpperInvariant());
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(Nickname))
|
if(string.IsNullOrWhiteSpace(Nickname))
|
||||||
sb.Append(Username);
|
sb.Append(Username);
|
||||||
|
@ -50,7 +51,7 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Pack() {
|
public string Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append(UserId);
|
sb.Append(UserId);
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
@ -61,7 +62,7 @@ namespace SharpChat {
|
||||||
sb.Append(Rank);
|
sb.Append(Rank);
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(Can(ChatUserPermissions.KickUser) ? '1' : '0');
|
sb.Append(Can(ChatUserPermissions.KickUser) ? '1' : '0');
|
||||||
sb.Append(@" 0 ");
|
sb.Append(@" 0 "); // view logs
|
||||||
sb.Append(Can(ChatUserPermissions.SetOwnNickname) ? '1' : '0');
|
sb.Append(Can(ChatUserPermissions.SetOwnNickname) ? '1' : '0');
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(Can(ChatUserPermissions.CreateChannel | ChatUserPermissions.SetChannelPermanent, true) ? 2 : (
|
sb.Append(Can(ChatUserPermissions.CreateChannel | ChatUserPermissions.SetChannelPermanent, true) ? 2 : (
|
||||||
|
@ -75,10 +76,10 @@ 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<ChatUserSession> Sessions = new List<ChatUserSession>();
|
private readonly List<ChatUserSession> Sessions = new();
|
||||||
private readonly List<ChatChannel> Channels = new List<ChatChannel>();
|
private readonly List<ChatChannel> Channels = new();
|
||||||
|
|
||||||
public readonly ChatRateLimiter RateLimiter = new ChatRateLimiter();
|
public readonly ChatRateLimiter RateLimiter = new();
|
||||||
|
|
||||||
public string TargetName => @"@log";
|
public string TargetName => @"@log";
|
||||||
|
|
||||||
|
@ -104,7 +105,7 @@ namespace SharpChat {
|
||||||
|
|
||||||
public int SessionCount {
|
public int SessionCount {
|
||||||
get {
|
get {
|
||||||
lock (Sessions)
|
lock(Sessions)
|
||||||
return Sessions.Where(c => !c.HasTimedOut && !c.IsDisposed).Count();
|
return Sessions.Where(c => !c.HasTimedOut && !c.IsDisposed).Count();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,33 +128,34 @@ namespace SharpChat {
|
||||||
public void ApplyAuth(FlashiiAuth auth, bool invalidateRestrictions = false) {
|
public void ApplyAuth(FlashiiAuth auth, bool invalidateRestrictions = false) {
|
||||||
Username = auth.Username;
|
Username = auth.Username;
|
||||||
|
|
||||||
if (Status == ChatUserStatus.Offline)
|
if(Status == ChatUserStatus.Offline)
|
||||||
Status = ChatUserStatus.Online;
|
Status = ChatUserStatus.Online;
|
||||||
|
|
||||||
Colour = new ChatColour(auth.ColourRaw);
|
Colour = new(auth.ColourRaw);
|
||||||
Rank = auth.Rank;
|
Rank = auth.Rank;
|
||||||
Permissions = auth.Permissions;
|
Permissions = auth.Permissions;
|
||||||
|
|
||||||
if (invalidateRestrictions || !IsSilenced)
|
if(invalidateRestrictions || !IsSilenced)
|
||||||
SilencedUntil = auth.SilencedUntil;
|
SilencedUntil = auth.SilencedUntil;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Send(IServerPacket packet) {
|
public void Send(IServerPacket packet) {
|
||||||
lock(Sessions)
|
lock(Sessions)
|
||||||
foreach (ChatUserSession conn in Sessions)
|
foreach(ChatUserSession conn in Sessions)
|
||||||
conn.Send(packet);
|
conn.Send(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Close() {
|
public void Close() {
|
||||||
lock (Sessions) {
|
lock(Sessions) {
|
||||||
foreach (ChatUserSession conn in Sessions)
|
foreach(ChatUserSession conn in Sessions)
|
||||||
conn.Dispose();
|
conn.Dispose();
|
||||||
Sessions.Clear();
|
Sessions.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ForceChannel(ChatChannel chan = null)
|
public void ForceChannel(ChatChannel chan = null) {
|
||||||
=> Send(new UserChannelForceJoinPacket(chan ?? CurrentChannel));
|
Send(new UserChannelForceJoinPacket(chan ?? CurrentChannel));
|
||||||
|
}
|
||||||
|
|
||||||
public void FocusChannel(ChatChannel chan) {
|
public void FocusChannel(ChatChannel chan) {
|
||||||
lock(Channels) {
|
lock(Channels) {
|
||||||
|
@ -163,12 +165,12 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool InChannel(ChatChannel chan) {
|
public bool InChannel(ChatChannel chan) {
|
||||||
lock (Channels)
|
lock(Channels)
|
||||||
return Channels.Contains(chan);
|
return Channels.Contains(chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void JoinChannel(ChatChannel chan) {
|
public void JoinChannel(ChatChannel chan) {
|
||||||
lock (Channels) {
|
lock(Channels) {
|
||||||
if(!InChannel(chan)) {
|
if(!InChannel(chan)) {
|
||||||
Channels.Add(chan);
|
Channels.Add(chan);
|
||||||
CurrentChannel = chan;
|
CurrentChannel = chan;
|
||||||
|
@ -184,21 +186,21 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<ChatChannel> GetChannels() {
|
public IEnumerable<ChatChannel> GetChannels() {
|
||||||
lock (Channels)
|
lock(Channels)
|
||||||
return Channels.ToList();
|
return Channels.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddSession(ChatUserSession sess) {
|
public void AddSession(ChatUserSession sess) {
|
||||||
if (sess == null)
|
if(sess == null)
|
||||||
return;
|
return;
|
||||||
sess.User = this;
|
sess.User = this;
|
||||||
|
|
||||||
lock (Sessions)
|
lock(Sessions)
|
||||||
Sessions.Add(sess);
|
Sessions.Add(sess);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveSession(ChatUserSession sess) {
|
public void RemoveSession(ChatUserSession sess) {
|
||||||
if (sess == null)
|
if(sess == null)
|
||||||
return;
|
return;
|
||||||
if(!sess.IsDisposed) // this could be possible
|
if(!sess.IsDisposed) // this could be possible
|
||||||
sess.User = null;
|
sess.User = null;
|
||||||
|
@ -208,7 +210,7 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<ChatUserSession> GetDeadSessions() {
|
public IEnumerable<ChatUserSession> GetDeadSessions() {
|
||||||
lock (Sessions)
|
lock(Sessions)
|
||||||
return Sessions.Where(x => x.HasTimedOut || x.IsDisposed).ToList();
|
return Sessions.Where(x => x.HasTimedOut || x.IsDisposed).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace SharpChat {
|
||||||
public DateTimeOffset LastPing { get; set; } = DateTimeOffset.MinValue;
|
public DateTimeOffset LastPing { get; set; } = DateTimeOffset.MinValue;
|
||||||
public ChatUser User { get; set; }
|
public ChatUser User { get; set; }
|
||||||
|
|
||||||
private static int CloseCode { get; set; } = 1000;
|
private int CloseCode { get; set; } = 1000;
|
||||||
|
|
||||||
public string TargetName => @"@log";
|
public string TargetName => @"@log";
|
||||||
|
|
||||||
|
@ -65,30 +65,32 @@ namespace SharpChat {
|
||||||
Connection.Send(line);
|
Connection.Send(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BumpPing()
|
public void BumpPing() {
|
||||||
=> LastPing = DateTimeOffset.Now;
|
LastPing = DateTimeOffset.Now;
|
||||||
|
}
|
||||||
|
|
||||||
public bool HasTimedOut
|
public bool HasTimedOut
|
||||||
=> DateTimeOffset.Now - LastPing > SessionTimeOut;
|
=> DateTimeOffset.Now - LastPing > SessionTimeOut;
|
||||||
|
|
||||||
public void PrepareForRestart()
|
public void PrepareForRestart() {
|
||||||
=> CloseCode = 1012;
|
CloseCode = 1012;
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
~ChatUserSession() {
|
||||||
=> Dispose(true);
|
DoDispose();
|
||||||
|
}
|
||||||
|
|
||||||
~ChatUserSession()
|
public void Dispose() {
|
||||||
=> Dispose(false);
|
DoDispose();
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
private void Dispose(bool disposing) {
|
private void DoDispose() {
|
||||||
if(IsDisposed)
|
if(IsDisposed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
IsDisposed = true;
|
IsDisposed = true;
|
||||||
Connection.Close(CloseCode);
|
Connection.Close(CloseCode);
|
||||||
|
|
||||||
if(disposing)
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace SharpChat.Commands {
|
||||||
else {
|
else {
|
||||||
statusText = statusText.Trim();
|
statusText = statusText.Trim();
|
||||||
if(statusText.Length > MAX_LENGTH)
|
if(statusText.Length > MAX_LENGTH)
|
||||||
statusText = statusText.Substring(0, MAX_LENGTH).Trim();
|
statusText = statusText[..MAX_LENGTH].Trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
context.User.Status = ChatUserStatus.Away;
|
context.User.Status = ChatUserStatus.Away;
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace SharpChat {
|
||||||
if(!File.Exists(@"mariadb.txt"))
|
if(!File.Exists(@"mariadb.txt"))
|
||||||
return;
|
return;
|
||||||
string[] config = File.ReadAllLines(@"mariadb.txt");
|
string[] config = File.ReadAllLines(@"mariadb.txt");
|
||||||
if (config.Length < 4)
|
if(config.Length < 4)
|
||||||
return;
|
return;
|
||||||
Init(config[0], config[1], config[2], config[3]);
|
Init(config[0], config[1], config[2], config[3]);
|
||||||
}
|
}
|
||||||
|
@ -43,27 +43,27 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MySqlConnection GetConnection() {
|
private static MySqlConnection GetConnection() {
|
||||||
if (!HasDatabase)
|
if(!HasDatabase)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
MySqlConnection conn = new MySqlConnection(ConnectionString);
|
MySqlConnection conn = new(ConnectionString);
|
||||||
conn.Open();
|
conn.Open();
|
||||||
|
|
||||||
return conn;
|
return conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int RunCommand(string command, params MySqlParameter[] parameters) {
|
private static int RunCommand(string command, params MySqlParameter[] parameters) {
|
||||||
if (!HasDatabase)
|
if(!HasDatabase)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
using MySqlConnection conn = GetConnection();
|
using MySqlConnection conn = GetConnection();
|
||||||
using MySqlCommand cmd = conn.CreateCommand();
|
using MySqlCommand cmd = conn.CreateCommand();
|
||||||
if (parameters?.Length > 0)
|
if(parameters?.Length > 0)
|
||||||
cmd.Parameters.AddRange(parameters);
|
cmd.Parameters.AddRange(parameters);
|
||||||
cmd.CommandText = command;
|
cmd.CommandText = command;
|
||||||
return cmd.ExecuteNonQuery();
|
return cmd.ExecuteNonQuery();
|
||||||
} catch (MySqlException ex) {
|
} catch(MySqlException ex) {
|
||||||
Logger.Write(ex);
|
Logger.Write(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,13 +71,13 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MySqlDataReader RunQuery(string command, params MySqlParameter[] parameters) {
|
private static MySqlDataReader RunQuery(string command, params MySqlParameter[] parameters) {
|
||||||
if (!HasDatabase)
|
if(!HasDatabase)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
MySqlConnection conn = GetConnection();
|
MySqlConnection conn = GetConnection();
|
||||||
MySqlCommand cmd = conn.CreateCommand();
|
MySqlCommand cmd = conn.CreateCommand();
|
||||||
if (parameters?.Length > 0)
|
if(parameters?.Length > 0)
|
||||||
cmd.Parameters.AddRange(parameters);
|
cmd.Parameters.AddRange(parameters);
|
||||||
cmd.CommandText = command;
|
cmd.CommandText = command;
|
||||||
return cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
|
return cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
|
||||||
|
@ -89,13 +89,13 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static object RunQueryValue(string command, params MySqlParameter[] parameters) {
|
private static object RunQueryValue(string command, params MySqlParameter[] parameters) {
|
||||||
if (!HasDatabase)
|
if(!HasDatabase)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
using MySqlConnection conn = GetConnection();
|
using MySqlConnection conn = GetConnection();
|
||||||
using MySqlCommand cmd = conn.CreateCommand();
|
using MySqlCommand cmd = conn.CreateCommand();
|
||||||
if (parameters?.Length > 0)
|
if(parameters?.Length > 0)
|
||||||
cmd.Parameters.AddRange(parameters);
|
cmd.Parameters.AddRange(parameters);
|
||||||
cmd.CommandText = command;
|
cmd.CommandText = command;
|
||||||
cmd.Prepare();
|
cmd.Prepare();
|
||||||
|
@ -111,7 +111,7 @@ namespace SharpChat {
|
||||||
private static int IdCounter = 0;
|
private static int IdCounter = 0;
|
||||||
|
|
||||||
public static long GenerateId() {
|
public static long GenerateId() {
|
||||||
if (IdCounter > 200)
|
if(IdCounter > 200)
|
||||||
IdCounter = 0;
|
IdCounter = 0;
|
||||||
|
|
||||||
long id = 0;
|
long id = 0;
|
||||||
|
@ -160,7 +160,7 @@ namespace SharpChat {
|
||||||
evt.Flags = (ChatMessageFlags)reader.GetByte(@"event_flags");
|
evt.Flags = (ChatMessageFlags)reader.GetByte(@"event_flags");
|
||||||
evt.DateTime = DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32(@"event_created"));
|
evt.DateTime = DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32(@"event_created"));
|
||||||
|
|
||||||
if (!reader.IsDBNull(reader.GetOrdinal(@"event_sender"))) {
|
if(!reader.IsDBNull(reader.GetOrdinal(@"event_sender"))) {
|
||||||
evt.Sender = new BasicUser {
|
evt.Sender = new BasicUser {
|
||||||
UserId = reader.GetInt64(@"event_sender"),
|
UserId = reader.GetInt64(@"event_sender"),
|
||||||
Username = reader.GetString(@"event_sender_name"),
|
Username = reader.GetString(@"event_sender_name"),
|
||||||
|
@ -175,7 +175,7 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<IChatEvent> GetEvents(IPacketTarget target, int amount, int offset) {
|
public static IEnumerable<IChatEvent> GetEvents(IPacketTarget target, int amount, int offset) {
|
||||||
List<IChatEvent> events = new List<IChatEvent>();
|
List<IChatEvent> events = new();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
using MySqlDataReader reader = RunQuery(
|
using MySqlDataReader reader = RunQuery(
|
||||||
|
@ -192,9 +192,9 @@ namespace SharpChat {
|
||||||
new MySqlParameter(@"offset", offset)
|
new MySqlParameter(@"offset", offset)
|
||||||
);
|
);
|
||||||
|
|
||||||
while (reader.Read()) {
|
while(reader.Read()) {
|
||||||
IChatEvent evt = ReadEvent(reader, target);
|
IChatEvent evt = ReadEvent(reader, target);
|
||||||
if (evt != null)
|
if(evt != null)
|
||||||
events.Add(evt);
|
events.Add(evt);
|
||||||
}
|
}
|
||||||
} catch(MySqlException ex) {
|
} catch(MySqlException ex) {
|
||||||
|
@ -215,9 +215,9 @@ namespace SharpChat {
|
||||||
new MySqlParameter(@"id", seqId)
|
new MySqlParameter(@"id", seqId)
|
||||||
);
|
);
|
||||||
|
|
||||||
while (reader.Read()) {
|
while(reader.Read()) {
|
||||||
IChatEvent evt = ReadEvent(reader);
|
IChatEvent evt = ReadEvent(reader);
|
||||||
if (evt != null)
|
if(evt != null)
|
||||||
return evt;
|
return evt;
|
||||||
}
|
}
|
||||||
} catch(MySqlException ex) {
|
} catch(MySqlException ex) {
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace SharpChat {
|
||||||
@"SELECT COUNT(*) FROM `sqc_migrations` WHERE `migration_name` = @name",
|
@"SELECT COUNT(*) FROM `sqc_migrations` WHERE `migration_name` = @name",
|
||||||
new MySqlParameter(@"name", name)
|
new MySqlParameter(@"name", name)
|
||||||
) > 0;
|
) > 0;
|
||||||
if (!done) {
|
if(!done) {
|
||||||
Logger.Write($@"Running migration '{name}'...");
|
Logger.Write($@"Running migration '{name}'...");
|
||||||
action();
|
action();
|
||||||
RunCommand(
|
RunCommand(
|
||||||
|
|
|
@ -1,23 +1,22 @@
|
||||||
using System;
|
using System.IO;
|
||||||
using System.IO;
|
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace SharpChat {
|
namespace SharpChat {
|
||||||
public static class Extensions {
|
public static class Extensions {
|
||||||
public static string GetSignedHash(this string str, string key = null)
|
public static string GetSignedHash(this string str, string key = null) {
|
||||||
=> Encoding.UTF8.GetBytes(str).GetSignedHash(key);
|
return Encoding.UTF8.GetBytes(str).GetSignedHash(key);
|
||||||
|
}
|
||||||
|
|
||||||
public static string GetSignedHash(this byte[] bytes, string key = null) {
|
public static string GetSignedHash(this byte[] bytes, string key = null) {
|
||||||
if (key == null)
|
key ??= File.Exists(@"login_key.txt") ? File.ReadAllText(@"login_key.txt") : @"woomy";
|
||||||
key = File.Exists(@"login_key.txt") ? File.ReadAllText(@"login_key.txt") : @"woomy";
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
using (HMACSHA256 algo = new HMACSHA256(Encoding.UTF8.GetBytes(key))) {
|
using(HMACSHA256 algo = new(Encoding.UTF8.GetBytes(key))) {
|
||||||
byte[] hash = algo.ComputeHash(bytes);
|
byte[] hash = algo.ComputeHash(bytes);
|
||||||
|
|
||||||
foreach (byte b in hash)
|
foreach(byte b in hash)
|
||||||
sb.AppendFormat(@"{0:x2}", b);
|
sb.AppendFormat(@"{0:x2}", b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +25,7 @@ namespace SharpChat {
|
||||||
|
|
||||||
public static string GetIdString(this byte[] buffer) {
|
public static string GetIdString(this byte[] buffer) {
|
||||||
const string id_chars = @"abcdefghijklmnopqrstuvwxyz0123456789-_ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
const string id_chars = @"abcdefghijklmnopqrstuvwxyz0123456789-_ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
foreach(byte b in buffer)
|
foreach(byte b in buffer)
|
||||||
sb.Append(id_chars[b % id_chars.Length]);
|
sb.Append(id_chars[b % id_chars.Length]);
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
|
|
|
@ -19,8 +19,9 @@ namespace SharpChat.Flashii {
|
||||||
public string Hash
|
public string Hash
|
||||||
=> string.Join(@"#", UserId, Token, IPAddress).GetSignedHash();
|
=> string.Join(@"#", UserId, Token, IPAddress).GetSignedHash();
|
||||||
|
|
||||||
public byte[] GetJSON()
|
public byte[] GetJSON() {
|
||||||
=> JsonSerializer.SerializeToUtf8Bytes(this);
|
return JsonSerializer.SerializeToUtf8Bytes(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FlashiiAuth {
|
public class FlashiiAuth {
|
||||||
|
@ -55,7 +56,7 @@ namespace SharpChat.Flashii {
|
||||||
throw new ArgumentNullException(nameof(authRequest));
|
throw new ArgumentNullException(nameof(authRequest));
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
if (authRequest.UserId >= 10000)
|
if(authRequest.UserId >= 10000)
|
||||||
return new FlashiiAuth {
|
return new FlashiiAuth {
|
||||||
Success = true,
|
Success = true,
|
||||||
UserId = authRequest.UserId,
|
UserId = authRequest.UserId,
|
||||||
|
|
|
@ -4,25 +4,31 @@ using System.Text;
|
||||||
|
|
||||||
namespace SharpChat {
|
namespace SharpChat {
|
||||||
public static class Logger {
|
public static class Logger {
|
||||||
public static void Write(string str)
|
public static void Write(string str) {
|
||||||
=> Console.WriteLine(string.Format(@"[{1}] {0}", str, DateTime.Now));
|
Console.WriteLine(string.Format(@"[{1}] {0}", str, DateTime.Now));
|
||||||
|
}
|
||||||
|
|
||||||
public static void Write(byte[] bytes)
|
public static void Write(byte[] bytes) {
|
||||||
=> Write(Encoding.UTF8.GetString(bytes));
|
Write(Encoding.UTF8.GetString(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
public static void Write(object obj)
|
public static void Write(object obj) {
|
||||||
=> Write(obj?.ToString() ?? string.Empty);
|
Write(obj?.ToString() ?? string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
[Conditional(@"DEBUG")]
|
[Conditional(@"DEBUG")]
|
||||||
public static void Debug(string str)
|
public static void Debug(string str) {
|
||||||
=> Write(str);
|
Write(str);
|
||||||
|
}
|
||||||
|
|
||||||
[Conditional(@"DEBUG")]
|
[Conditional(@"DEBUG")]
|
||||||
public static void Debug(byte[] bytes)
|
public static void Debug(byte[] bytes) {
|
||||||
=> Write(bytes);
|
Write(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
[Conditional(@"DEBUG")]
|
[Conditional(@"DEBUG")]
|
||||||
public static void Debug(object obj)
|
public static void Debug(object obj) {
|
||||||
=> Write(obj);
|
Write(obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,20 +16,20 @@ namespace SharpChat.Packet {
|
||||||
public AuthFailPacket(AuthFailReason reason, DateTimeOffset? expires = null) {
|
public AuthFailPacket(AuthFailReason reason, DateTimeOffset? expires = null) {
|
||||||
Reason = reason;
|
Reason = reason;
|
||||||
|
|
||||||
if (reason == AuthFailReason.Banned) {
|
if(reason == AuthFailReason.Banned) {
|
||||||
if (!expires.HasValue)
|
if(!expires.HasValue)
|
||||||
throw new ArgumentNullException(nameof(expires));
|
throw new ArgumentNullException(nameof(expires));
|
||||||
Expires = expires.Value;
|
Expires = expires.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append('1');
|
sb.Append('1');
|
||||||
sb.Append("\tn\t");
|
sb.Append("\tn\t");
|
||||||
|
|
||||||
switch (Reason) {
|
switch(Reason) {
|
||||||
case AuthFailReason.AuthInvalid:
|
case AuthFailReason.AuthInvalid:
|
||||||
default:
|
default:
|
||||||
sb.Append(@"authfail");
|
sb.Append(@"authfail");
|
||||||
|
@ -42,10 +42,10 @@ namespace SharpChat.Packet {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Reason == AuthFailReason.Banned) {
|
if(Reason == AuthFailReason.Banned) {
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
||||||
if (Expires == DateTimeOffset.MaxValue)
|
if(Expires == DateTimeOffset.MaxValue)
|
||||||
sb.Append(@"-1");
|
sb.Append(@"-1");
|
||||||
else
|
else
|
||||||
sb.Append(Expires.ToUnixTimeSeconds());
|
sb.Append(Expires.ToUnixTimeSeconds());
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append('1');
|
sb.Append('1');
|
||||||
sb.Append("\ty\t");
|
sb.Append("\ty\t");
|
||||||
|
|
|
@ -12,17 +12,17 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append('2');
|
sb.Append('2');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append(DateTimeOffset.Now.ToUnixTimeSeconds());
|
sb.Append(DateTimeOffset.Now.ToUnixTimeSeconds());
|
||||||
sb.Append("\t-1\t0\fbanlist\f");
|
sb.Append("\t-1\t0\fbanlist\f");
|
||||||
|
|
||||||
foreach (IBan ban in Bans)
|
foreach(IBan ban in Bans)
|
||||||
sb.AppendFormat(@"<a href=""javascript:void(0);"" onclick=""Chat.SendMessageWrapper('/unban '+ this.innerHTML);"">{0}</a>, ", ban);
|
sb.AppendFormat(@"<a href=""javascript:void(0);"" onclick=""Chat.SendMessageWrapper('/unban '+ this.innerHTML);"">{0}</a>, ", ban);
|
||||||
|
|
||||||
if (Bans.Any())
|
if(Bans.Any())
|
||||||
sb.Length -= 2;
|
sb.Length -= 2;
|
||||||
|
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append('4');
|
sb.Append('4');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append('4');
|
sb.Append('4');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
|
@ -12,7 +12,7 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append('4');
|
sb.Append('4');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
|
@ -10,12 +10,12 @@ namespace SharpChat.Packet {
|
||||||
public ChatMessageAddPacket(IChatMessage message) : base(message?.SequenceId ?? 0) {
|
public ChatMessageAddPacket(IChatMessage message) : base(message?.SequenceId ?? 0) {
|
||||||
Message = message ?? throw new ArgumentNullException(nameof(message));
|
Message = message ?? throw new ArgumentNullException(nameof(message));
|
||||||
|
|
||||||
if (Message.SequenceId < 1)
|
if(Message.SequenceId < 1)
|
||||||
Message.SequenceId = SequenceId;
|
Message.SequenceId = SequenceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append('2');
|
sb.Append('2');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
@ -26,7 +26,7 @@ namespace SharpChat.Packet {
|
||||||
sb.Append(Message.Sender?.UserId ?? -1);
|
sb.Append(Message.Sender?.UserId ?? -1);
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
||||||
if (Message.Flags.HasFlag(ChatMessageFlags.Action))
|
if(Message.Flags.HasFlag(ChatMessageFlags.Action))
|
||||||
sb.Append(@"<i>");
|
sb.Append(@"<i>");
|
||||||
|
|
||||||
sb.Append(
|
sb.Append(
|
||||||
|
@ -37,7 +37,7 @@ namespace SharpChat.Packet {
|
||||||
.Replace("\t", @" ")
|
.Replace("\t", @" ")
|
||||||
);
|
);
|
||||||
|
|
||||||
if (Message.Flags.HasFlag(ChatMessageFlags.Action))
|
if(Message.Flags.HasFlag(ChatMessageFlags.Action))
|
||||||
sb.Append(@"</i>");
|
sb.Append(@"</i>");
|
||||||
|
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append('6');
|
sb.Append('6');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
|
@ -12,7 +12,7 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append('7');
|
sb.Append('7');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
@ -20,7 +20,7 @@ namespace SharpChat.Packet {
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append(Channels.Count());
|
sb.Append(Channels.Count());
|
||||||
|
|
||||||
foreach (ChatChannel channel in Channels) {
|
foreach(ChatChannel channel in Channels) {
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append(channel.Pack());
|
sb.Append(channel.Pack());
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append('8');
|
sb.Append('8');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace SharpChat.Packet {
|
||||||
private const string V1_CHATBOT = "-1\tChatBot\tinherit\t\t";
|
private const string V1_CHATBOT = "-1\tChatBot\tinherit\t\t";
|
||||||
|
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append('7');
|
sb.Append('7');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
@ -25,7 +25,7 @@ namespace SharpChat.Packet {
|
||||||
sb.Append(Event.DateTime.ToUnixTimeSeconds());
|
sb.Append(Event.DateTime.ToUnixTimeSeconds());
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
||||||
switch (Event) {
|
switch(Event) {
|
||||||
case IChatMessage msg:
|
case IChatMessage msg:
|
||||||
sb.Append(Event.Sender.Pack());
|
sb.Append(Event.Sender.Pack());
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
@ -60,7 +60,7 @@ namespace SharpChat.Packet {
|
||||||
sb.Append(V1_CHATBOT);
|
sb.Append(V1_CHATBOT);
|
||||||
sb.Append("0\f");
|
sb.Append("0\f");
|
||||||
|
|
||||||
switch (ude.Reason) {
|
switch(ude.Reason) {
|
||||||
case UserDisconnectReason.Flood:
|
case UserDisconnectReason.Flood:
|
||||||
sb.Append(@"flood");
|
sb.Append(@"flood");
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -12,7 +12,7 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append('7');
|
sb.Append('7');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
@ -20,7 +20,7 @@ namespace SharpChat.Packet {
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append(Users.Count());
|
sb.Append(Users.Count());
|
||||||
|
|
||||||
foreach (ChatUser user in Users) {
|
foreach(ChatUser user in Users) {
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append(user.Pack());
|
sb.Append(user.Pack());
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
|
@ -5,7 +5,7 @@ using System.Text;
|
||||||
namespace SharpChat.Packet {
|
namespace SharpChat.Packet {
|
||||||
public class FloodWarningPacket : ServerPacket {
|
public class FloodWarningPacket : ServerPacket {
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append('2');
|
sb.Append('2');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
|
@ -15,21 +15,21 @@ namespace SharpChat.Packet {
|
||||||
public ForceDisconnectPacket(ForceDisconnectReason reason, DateTimeOffset? expires = null) {
|
public ForceDisconnectPacket(ForceDisconnectReason reason, DateTimeOffset? expires = null) {
|
||||||
Reason = reason;
|
Reason = reason;
|
||||||
|
|
||||||
if (reason == ForceDisconnectReason.Banned) {
|
if(reason == ForceDisconnectReason.Banned) {
|
||||||
if (!expires.HasValue)
|
if(!expires.HasValue)
|
||||||
throw new ArgumentNullException(nameof(expires));
|
throw new ArgumentNullException(nameof(expires));
|
||||||
Expires = expires.Value;
|
Expires = expires.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append('9');
|
sb.Append('9');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append((int)Reason);
|
sb.Append((int)Reason);
|
||||||
|
|
||||||
if (Reason == ForceDisconnectReason.Banned) {
|
if(Reason == ForceDisconnectReason.Banned) {
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append(Expires.ToUnixTimeSeconds());
|
sb.Append(Expires.ToUnixTimeSeconds());
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,9 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
if (StringId == LCR.WELCOME) {
|
if(StringId == LCR.WELCOME) {
|
||||||
sb.Append('7');
|
sb.Append('7');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append('1');
|
sb.Append('1');
|
||||||
|
@ -40,15 +40,15 @@ namespace SharpChat.Packet {
|
||||||
sb.Append('\f');
|
sb.Append('\f');
|
||||||
sb.Append(StringId == LCR.WELCOME ? LCR.BROADCAST : StringId);
|
sb.Append(StringId == LCR.WELCOME ? LCR.BROADCAST : StringId);
|
||||||
|
|
||||||
if (Arguments?.Any() == true)
|
if(Arguments?.Any() == true)
|
||||||
foreach (object arg in Arguments) {
|
foreach(object arg in Arguments) {
|
||||||
sb.Append('\f');
|
sb.Append('\f');
|
||||||
sb.Append(arg);
|
sb.Append(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
||||||
if (StringId == LCR.WELCOME) {
|
if(StringId == LCR.WELCOME) {
|
||||||
sb.Append(StringId);
|
sb.Append(StringId);
|
||||||
sb.Append("\t0");
|
sb.Append("\t0");
|
||||||
} else
|
} else
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append('0');
|
sb.Append('0');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append('5');
|
sb.Append('5');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append('5');
|
sb.Append('5');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append('5');
|
sb.Append('5');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append('1');
|
sb.Append('1');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
|
@ -22,7 +22,7 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.Append('3');
|
sb.Append('3');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
@ -31,7 +31,7 @@ namespace SharpChat.Packet {
|
||||||
sb.Append(User.DisplayName);
|
sb.Append(User.DisplayName);
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
||||||
switch (Reason) {
|
switch(Reason) {
|
||||||
case UserDisconnectReason.Leave:
|
case UserDisconnectReason.Leave:
|
||||||
default:
|
default:
|
||||||
sb.Append(@"leave");
|
sb.Append(@"leave");
|
||||||
|
|
|
@ -13,11 +13,11 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> Pack() {
|
public override IEnumerable<string> Pack() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
bool isSilent = string.IsNullOrEmpty(PreviousName);
|
bool isSilent = string.IsNullOrEmpty(PreviousName);
|
||||||
|
|
||||||
if (!isSilent) {
|
if(!isSilent) {
|
||||||
sb.Append('2');
|
sb.Append('2');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append(DateTimeOffset.Now.ToUnixTimeSeconds());
|
sb.Append(DateTimeOffset.Now.ToUnixTimeSeconds());
|
||||||
|
|
|
@ -3,22 +3,22 @@ using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace SharpChat {
|
namespace SharpChat {
|
||||||
public static class RNG {
|
public static class RNG {
|
||||||
private static object Lock { get; } = new object();
|
private static object Lock { get; } = new();
|
||||||
private static Random NormalRandom { get; } = new Random();
|
private static Random NormalRandom { get; } = new();
|
||||||
private static RandomNumberGenerator SecureRandom { get; } = RandomNumberGenerator.Create();
|
private static RandomNumberGenerator SecureRandom { get; } = RandomNumberGenerator.Create();
|
||||||
|
|
||||||
public static int Next() {
|
public static int Next() {
|
||||||
lock (Lock)
|
lock(Lock)
|
||||||
return NormalRandom.Next();
|
return NormalRandom.Next();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int Next(int max) {
|
public static int Next(int max) {
|
||||||
lock (Lock)
|
lock(Lock)
|
||||||
return NormalRandom.Next(max);
|
return NormalRandom.Next(max);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int Next(int min, int max) {
|
public static int Next(int min, int max) {
|
||||||
lock (Lock)
|
lock(Lock)
|
||||||
return NormalRandom.Next(min, max);
|
return NormalRandom.Next(min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace SharpChat {
|
||||||
private Action<IWebSocketConnection> _config;
|
private Action<IWebSocketConnection> _config;
|
||||||
|
|
||||||
public SharpChatWebSocketServer(string location, bool supportDualStack = true) {
|
public SharpChatWebSocketServer(string location, bool supportDualStack = true) {
|
||||||
Uri uri = new Uri(location);
|
Uri uri = new(location);
|
||||||
|
|
||||||
Port = uri.Port;
|
Port = uri.Port;
|
||||||
Location = location;
|
Location = location;
|
||||||
|
@ -29,15 +29,15 @@ namespace SharpChat {
|
||||||
|
|
||||||
_locationIP = ParseIPAddress(uri);
|
_locationIP = ParseIPAddress(uri);
|
||||||
_scheme = uri.Scheme;
|
_scheme = uri.Scheme;
|
||||||
Socket socket = new Socket(_locationIP.AddressFamily, SocketType.Stream, ProtocolType.IP);
|
Socket socket = new(_locationIP.AddressFamily, SocketType.Stream, ProtocolType.IP);
|
||||||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
|
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
|
||||||
|
|
||||||
if (SupportDualStack && Type.GetType(@"Mono.Runtime") == null && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
|
if(SupportDualStack && Type.GetType(@"Mono.Runtime") == null && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
|
||||||
socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false);
|
socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
ListenerSocket = new SocketWrapper(socket);
|
ListenerSocket = new SocketWrapper(socket);
|
||||||
SupportedSubProtocols = new string[0];
|
SupportedSubProtocols = Array.Empty<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ISocket ListenerSocket { get; set; }
|
public ISocket ListenerSocket { get; set; }
|
||||||
|
@ -55,37 +55,38 @@ namespace SharpChat {
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
ListenerSocket.Dispose();
|
ListenerSocket.Dispose();
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IPAddress ParseIPAddress(Uri uri) {
|
private static IPAddress ParseIPAddress(Uri uri) {
|
||||||
string ipStr = uri.Host;
|
string ipStr = uri.Host;
|
||||||
|
|
||||||
if (ipStr == "0.0.0.0") {
|
if(ipStr == "0.0.0.0") {
|
||||||
return IPAddress.Any;
|
return IPAddress.Any;
|
||||||
} else if (ipStr == "[0000:0000:0000:0000:0000:0000:0000:0000]") {
|
} else if(ipStr == "[0000:0000:0000:0000:0000:0000:0000:0000]") {
|
||||||
return IPAddress.IPv6Any;
|
return IPAddress.IPv6Any;
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
return IPAddress.Parse(ipStr);
|
return IPAddress.Parse(ipStr);
|
||||||
} catch (Exception ex) {
|
} catch(Exception ex) {
|
||||||
throw new FormatException("Failed to parse the IP address part of the location. Please make sure you specify a valid IP address. Use 0.0.0.0 or [::] to listen on all interfaces.", ex);
|
throw new FormatException("Failed to parse the IP address part of the location. Please make sure you specify a valid IP address. Use 0.0.0.0 or [::] to listen on all interfaces.", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start(Action<IWebSocketConnection> config) {
|
public void Start(Action<IWebSocketConnection> config) {
|
||||||
IPEndPoint ipLocal = new IPEndPoint(_locationIP, Port);
|
IPEndPoint ipLocal = new(_locationIP, Port);
|
||||||
ListenerSocket.Bind(ipLocal);
|
ListenerSocket.Bind(ipLocal);
|
||||||
ListenerSocket.Listen(100);
|
ListenerSocket.Listen(100);
|
||||||
Port = ((IPEndPoint)ListenerSocket.LocalEndPoint).Port;
|
Port = ((IPEndPoint)ListenerSocket.LocalEndPoint).Port;
|
||||||
FleckLog.Info(string.Format("Server started at {0} (actual port {1})", Location, Port));
|
FleckLog.Info(string.Format("Server started at {0} (actual port {1})", Location, Port));
|
||||||
if (_scheme == "wss") {
|
if(_scheme == "wss") {
|
||||||
if (Certificate == null) {
|
if(Certificate == null) {
|
||||||
FleckLog.Error("Scheme cannot be 'wss' without a Certificate");
|
FleckLog.Error("Scheme cannot be 'wss' without a Certificate");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (EnabledSslProtocols == SslProtocols.None) {
|
if(EnabledSslProtocols == SslProtocols.None) {
|
||||||
EnabledSslProtocols = SslProtocols.Tls;
|
EnabledSslProtocols = SslProtocols.Tls;
|
||||||
FleckLog.Debug("Using default TLS 1.0 security protocol.");
|
FleckLog.Debug("Using default TLS 1.0 security protocol.");
|
||||||
}
|
}
|
||||||
|
@ -97,16 +98,16 @@ namespace SharpChat {
|
||||||
private void ListenForClients() {
|
private void ListenForClients() {
|
||||||
ListenerSocket.Accept(OnClientConnect, e => {
|
ListenerSocket.Accept(OnClientConnect, e => {
|
||||||
FleckLog.Error("Listener socket is closed", e);
|
FleckLog.Error("Listener socket is closed", e);
|
||||||
if (RestartAfterListenError) {
|
if(RestartAfterListenError) {
|
||||||
FleckLog.Info("Listener socket restarting");
|
FleckLog.Info("Listener socket restarting");
|
||||||
try {
|
try {
|
||||||
ListenerSocket.Dispose();
|
ListenerSocket.Dispose();
|
||||||
Socket socket = new Socket(_locationIP.AddressFamily, SocketType.Stream, ProtocolType.IP);
|
Socket socket = new(_locationIP.AddressFamily, SocketType.Stream, ProtocolType.IP);
|
||||||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
|
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
|
||||||
ListenerSocket = new SocketWrapper(socket);
|
ListenerSocket = new SocketWrapper(socket);
|
||||||
Start(_config);
|
Start(_config);
|
||||||
FleckLog.Info("Listener socket restarted");
|
FleckLog.Info("Listener socket restarted");
|
||||||
} catch (Exception ex) {
|
} catch(Exception ex) {
|
||||||
FleckLog.Error("Listener could not be restarted", ex);
|
FleckLog.Error("Listener could not be restarted", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,7 +115,7 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnClientConnect(ISocket clientSocket) {
|
private void OnClientConnect(ISocket clientSocket) {
|
||||||
if (clientSocket == null) return; // socket closed
|
if(clientSocket == null) return; // socket closed
|
||||||
|
|
||||||
FleckLog.Debug(string.Format("Client connected from {0}:{1}", clientSocket.RemoteIpAddress, clientSocket.RemotePort.ToString()));
|
FleckLog.Debug(string.Format("Client connected from {0}:{1}", clientSocket.RemoteIpAddress, clientSocket.RemotePort.ToString()));
|
||||||
ListenForClients();
|
ListenForClients();
|
||||||
|
@ -151,7 +152,7 @@ namespace SharpChat {
|
||||||
},
|
},
|
||||||
s => SubProtocolNegotiator.Negotiate(SupportedSubProtocols, s));
|
s => SubProtocolNegotiator.Negotiate(SupportedSubProtocols, s));
|
||||||
|
|
||||||
if (IsSecure) {
|
if(IsSecure) {
|
||||||
FleckLog.Debug("Authenticating Secure Connection");
|
FleckLog.Debug("Authenticating Secure Connection");
|
||||||
clientSocket
|
clientSocket
|
||||||
.Authenticate(Certificate,
|
.Authenticate(Certificate,
|
||||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
||||||
|
|
||||||
namespace SharpChat {
|
namespace SharpChat {
|
||||||
public class UserManager : IDisposable {
|
public class UserManager : IDisposable {
|
||||||
private readonly List<ChatUser> Users = new List<ChatUser>();
|
private readonly List<ChatUser> Users = new();
|
||||||
|
|
||||||
public readonly ChatContext Context;
|
public readonly ChatContext Context;
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Add(ChatUser user) {
|
public void Add(ChatUser user) {
|
||||||
if (user == null)
|
if(user == null)
|
||||||
throw new ArgumentNullException(nameof(user));
|
throw new ArgumentNullException(nameof(user));
|
||||||
|
|
||||||
lock(Users)
|
lock(Users)
|
||||||
|
@ -24,7 +24,7 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Remove(ChatUser user) {
|
public void Remove(ChatUser user) {
|
||||||
if (user == null)
|
if(user == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
lock(Users)
|
lock(Users)
|
||||||
|
@ -32,10 +32,10 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Contains(ChatUser user) {
|
public bool Contains(ChatUser user) {
|
||||||
if (user == null)
|
if(user == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
lock (Users)
|
lock(Users)
|
||||||
return Users.Contains(user) || Users.Any(x => x.UserId == user.UserId || x.Username.ToLowerInvariant() == user.Username.ToLowerInvariant());
|
return Users.Contains(user) || Users.Any(x => x.UserId == user.UserId || x.Username.ToLowerInvariant() == user.Username.ToLowerInvariant());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChatUser Get(string username, bool includeNickName = true, bool includeDisplayName = true) {
|
public ChatUser Get(string username, bool includeNickName = true, bool includeDisplayName = true) {
|
||||||
if (string.IsNullOrWhiteSpace(username))
|
if(string.IsNullOrWhiteSpace(username))
|
||||||
return null;
|
return null;
|
||||||
username = username.ToLowerInvariant();
|
username = username.ToLowerInvariant();
|
||||||
|
|
||||||
|
@ -56,35 +56,35 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<ChatUser> OfHierarchy(int hierarchy) {
|
public IEnumerable<ChatUser> OfHierarchy(int hierarchy) {
|
||||||
lock (Users)
|
lock(Users)
|
||||||
return Users.Where(u => u.Rank >= hierarchy).ToList();
|
return Users.Where(u => u.Rank >= hierarchy).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<ChatUser> WithActiveConnections() {
|
public IEnumerable<ChatUser> WithActiveConnections() {
|
||||||
lock (Users)
|
lock(Users)
|
||||||
return Users.Where(u => u.HasSessions).ToList();
|
return Users.Where(u => u.HasSessions).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<ChatUser> All() {
|
public IEnumerable<ChatUser> All() {
|
||||||
lock (Users)
|
lock(Users)
|
||||||
return Users.ToList();
|
return Users.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
~UserManager()
|
~UserManager() {
|
||||||
=> Dispose(false);
|
DoDispose();
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose() {
|
||||||
=> Dispose(true);
|
DoDispose();
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
private void Dispose(bool disposing) {
|
private void DoDispose() {
|
||||||
if (IsDisposed)
|
if(IsDisposed)
|
||||||
return;
|
return;
|
||||||
IsDisposed = true;
|
IsDisposed = true;
|
||||||
|
|
||||||
Users.Clear();
|
Users.Clear();
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue