Rewrote user and channel collections.
This commit is contained in:
parent
1a8c44a4ba
commit
549c80740d
50 changed files with 782 additions and 243 deletions
SharpChat
ChannelInfo.csChannelsContext.csChannelsUsersContext.csChatContext.cs
Commands
ChannelCreateCommand.csChannelDeleteCommand.csChannelJoinCommand.csKickBanCommand.csMessageWhisperCommand.csPardonAddressCommand.csPardonUserCommand.csUserAFKCommand.csUserNickCommand.csWhoCommand.csWhoisCommand.cs
Config
Events
Misuzu
Packet
AuthSuccessPacket.csChannelCreatePacket.csChannelCreateResponsePacket.csChannelDeleteNotAllowedErrorPacket.csChannelDeletePacket.csChannelDeleteResponsePacket.csChannelNameInUseErrorPacket.csChannelNotFoundErrorPacket.csChannelPasswordWrongErrorPacket.csChannelRankTooLowErrorPacket.csChannelUpdatePacket.csChannelsPopulatePacket.csCommandNotAllowedErrorPacket.csMOTDPacket.csMessageAddPacket.csMessageBroadcastPacket.csMessagePopulatePacket.csUserChannelForceJoinPacket.csUserChannelJoinPacket.csUserUpdatePacket.csUsersPopulatePacket.csWhoChannelNotFoundErrorPacket.csWhoChannelResponsePacket.cs
PacketHandlerContext.csPacketHandlers
SockChatServer.csSockChatUtility.csUserInfo.csUsersContext.csUtility.cs
|
@ -1,7 +1,4 @@
|
||||||
using System;
|
namespace SharpChat {
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace SharpChat {
|
|
||||||
public class ChannelInfo {
|
public class ChannelInfo {
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
|
@ -12,6 +9,9 @@ namespace SharpChat {
|
||||||
public bool HasPassword
|
public bool HasPassword
|
||||||
=> !string.IsNullOrWhiteSpace(Password);
|
=> !string.IsNullOrWhiteSpace(Password);
|
||||||
|
|
||||||
|
public bool IsPublic
|
||||||
|
=> !IsTemporary && Rank < 1 && !HasPassword;
|
||||||
|
|
||||||
public ChannelInfo(
|
public ChannelInfo(
|
||||||
string name,
|
string name,
|
||||||
string? password = null,
|
string? password = null,
|
||||||
|
@ -26,22 +26,10 @@ namespace SharpChat {
|
||||||
OwnerId = ownerId;
|
OwnerId = ownerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool NameEquals(string? name) {
|
|
||||||
return string.Equals(name, Name, StringComparison.InvariantCultureIgnoreCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsOwner(UserInfo user) {
|
public bool IsOwner(UserInfo user) {
|
||||||
return OwnerId > 0
|
return OwnerId > 0
|
||||||
&& user != null
|
&& user != null
|
||||||
&& OwnerId == user.UserId;
|
&& OwnerId == user.UserId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool CheckName(string name) {
|
|
||||||
return !string.IsNullOrWhiteSpace(name) && name.All(CheckNameChar);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool CheckNameChar(char c) {
|
|
||||||
return char.IsLetter(c) || char.IsNumber(c) || c == '-' || c == '_';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
123
SharpChat/ChannelsContext.cs
Normal file
123
SharpChat/ChannelsContext.cs
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace SharpChat {
|
||||||
|
public class ChannelsContext {
|
||||||
|
private readonly List<ChannelInfo> Channels = new();
|
||||||
|
|
||||||
|
public ChannelInfo? MainChannel { get; private set; }
|
||||||
|
public int TotalCount { get; private set; }
|
||||||
|
public int PublicCount { get; private set; }
|
||||||
|
|
||||||
|
public ChannelInfo[] All => Channels.ToArray();
|
||||||
|
|
||||||
|
public ChannelInfo? Get(
|
||||||
|
string? name,
|
||||||
|
Func<string, string>? sanitise = null
|
||||||
|
) {
|
||||||
|
if(string.IsNullOrWhiteSpace(name))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
foreach(ChannelInfo info in Channels) {
|
||||||
|
string chanName = info.Name;
|
||||||
|
if(sanitise != null)
|
||||||
|
chanName = sanitise(chanName);
|
||||||
|
if(!chanName.Equals(name, StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChannelInfo[] GetMany(
|
||||||
|
string[]? names = null,
|
||||||
|
Func<string, string>? sanitiseName = null,
|
||||||
|
int minRank = 0,
|
||||||
|
bool? isPublic = null
|
||||||
|
) {
|
||||||
|
List<ChannelInfo> chans = new();
|
||||||
|
|
||||||
|
names ??= Array.Empty<string>();
|
||||||
|
for(int i = 0; i < names.Length; ++i)
|
||||||
|
names[i] = names[i].ToLowerInvariant();
|
||||||
|
|
||||||
|
foreach(ChannelInfo info in Channels) {
|
||||||
|
if(info.Rank > minRank)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(isPublic != null && info.IsPublic != isPublic)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(names?.Length > 0) {
|
||||||
|
string chanName = info.Name;
|
||||||
|
if(sanitiseName != null)
|
||||||
|
chanName = sanitiseName(chanName);
|
||||||
|
|
||||||
|
bool match = false;
|
||||||
|
foreach(string name in names)
|
||||||
|
if(match = chanName.Equals(name, StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(!match)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
chans.Add(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
return chans.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(
|
||||||
|
ChannelInfo info,
|
||||||
|
bool forceMain = false,
|
||||||
|
Func<string, string>? sanitiseName = null
|
||||||
|
) {
|
||||||
|
if(Get(info.Name, sanitiseName) != null)
|
||||||
|
throw new ArgumentException("A channel with this name has already been registered.", nameof(info));
|
||||||
|
if(string.IsNullOrWhiteSpace(info.Name))
|
||||||
|
throw new ArgumentException("Channel names may not be blank.", nameof(info));
|
||||||
|
// todo: there should be more restrictions on channel names
|
||||||
|
|
||||||
|
Channels.Add(info);
|
||||||
|
|
||||||
|
++TotalCount;
|
||||||
|
if(info.IsPublic)
|
||||||
|
++PublicCount;
|
||||||
|
|
||||||
|
if(forceMain || MainChannel == null)
|
||||||
|
MainChannel = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(
|
||||||
|
ChannelInfo info,
|
||||||
|
Func<string, string>? sanitiseName = null
|
||||||
|
) {
|
||||||
|
Remove(info.Name, sanitiseName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(
|
||||||
|
string? name,
|
||||||
|
Func<string, string>? sanitise = null
|
||||||
|
) {
|
||||||
|
if(string.IsNullOrWhiteSpace(name))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ChannelInfo? info = Get(name, sanitise);
|
||||||
|
if(info == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Channels.Remove(info);
|
||||||
|
|
||||||
|
--TotalCount;
|
||||||
|
if(info.IsPublic)
|
||||||
|
--PublicCount;
|
||||||
|
|
||||||
|
if(MainChannel == info)
|
||||||
|
MainChannel = Channels.FirstOrDefault(c => !c.IsPublic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
257
SharpChat/ChannelsUsersContext.cs
Normal file
257
SharpChat/ChannelsUsersContext.cs
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace SharpChat {
|
||||||
|
public class ChannelsUsersContext {
|
||||||
|
private readonly Dictionary<string, HashSet<long>> ChannelUsers = new();
|
||||||
|
private readonly Dictionary<long, HashSet<string>> UserChannels = new();
|
||||||
|
private readonly Dictionary<long, string> UserLastChannel = new();
|
||||||
|
|
||||||
|
public string GetUserLastChannel(long userId) {
|
||||||
|
return UserLastChannel.ContainsKey(userId)
|
||||||
|
? UserLastChannel[userId]
|
||||||
|
: string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetUserLastChannel(UserInfo userInfo) {
|
||||||
|
return GetUserLastChannel(userInfo.UserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetUserLastChannel(long userId, string channelName) {
|
||||||
|
channelName = channelName.ToLowerInvariant();
|
||||||
|
if(UserLastChannel.ContainsKey(userId))
|
||||||
|
UserLastChannel[userId] = channelName;
|
||||||
|
else
|
||||||
|
UserLastChannel.Add(userId, channelName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetUserLastChannel(UserInfo userInfo, ChannelInfo channelInfo) {
|
||||||
|
SetUserLastChannel(userInfo.UserId, channelInfo.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteUserLastChannel(long userId) {
|
||||||
|
if(UserLastChannel.ContainsKey(userId))
|
||||||
|
UserLastChannel.Remove(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteUserLastChannel(UserInfo userInfo) {
|
||||||
|
DeleteUserLastChannel(userInfo.UserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsUserLastChannel(long userId, string channelName) {
|
||||||
|
return !string.IsNullOrWhiteSpace(channelName)
|
||||||
|
&& GetUserLastChannel(userId).Equals(channelName, StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsUserLastChannel(UserInfo userInfo, ChannelInfo channelInfo) {
|
||||||
|
return IsUserLastChannel(userInfo.UserId, channelInfo.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string[] GetUserChannelNames(long userId) {
|
||||||
|
if(!UserChannels.ContainsKey(userId))
|
||||||
|
return Array.Empty<string>();
|
||||||
|
|
||||||
|
return UserChannels[userId].ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string[] GetUserChannelNames(UserInfo userInfo) {
|
||||||
|
return GetUserChannelNames(userInfo.UserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long[] GetChannelUserIds(string channelName) {
|
||||||
|
channelName = channelName.ToLowerInvariant();
|
||||||
|
if(!ChannelUsers.ContainsKey(channelName))
|
||||||
|
return Array.Empty<long>();
|
||||||
|
|
||||||
|
return ChannelUsers[channelName].ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long[] GetChannelUserIds(string channelName, Func<string, string> sanitise) {
|
||||||
|
foreach(KeyValuePair<string, HashSet<long>> kvp in ChannelUsers)
|
||||||
|
if(sanitise(kvp.Key).Equals(channelName, StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
return kvp.Value.ToArray();
|
||||||
|
|
||||||
|
return Array.Empty<long>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long[] GetChannelUserIds(ChannelInfo channelInfo) {
|
||||||
|
return GetChannelUserIds(channelInfo.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Join(string channelName, long userId) {
|
||||||
|
channelName = channelName.ToLowerInvariant();
|
||||||
|
if(ChannelUsers.ContainsKey(channelName))
|
||||||
|
ChannelUsers[channelName].Add(userId);
|
||||||
|
else
|
||||||
|
ChannelUsers.Add(channelName, new HashSet<long> { userId });
|
||||||
|
|
||||||
|
if(UserChannels.ContainsKey(userId))
|
||||||
|
UserChannels[userId].Add(channelName);
|
||||||
|
else
|
||||||
|
UserChannels.Add(userId, new HashSet<string> { channelName });
|
||||||
|
|
||||||
|
SetUserLastChannel(userId, channelName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Join(ChannelInfo channelInfo, UserInfo userInfo) {
|
||||||
|
Join(channelInfo.Name, userInfo.UserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Leave(string channelName, long userId) {
|
||||||
|
channelName = channelName.ToLowerInvariant();
|
||||||
|
if(ChannelUsers.ContainsKey(channelName)) {
|
||||||
|
if(ChannelUsers[channelName].Count < 2)
|
||||||
|
ChannelUsers.Remove(channelName);
|
||||||
|
else
|
||||||
|
ChannelUsers[channelName].Remove(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(UserChannels.ContainsKey(userId)) {
|
||||||
|
if(UserChannels[userId].Count < 2)
|
||||||
|
UserChannels.Remove(userId);
|
||||||
|
else
|
||||||
|
UserChannels[userId].Remove(channelName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(IsUserLastChannel(userId, channelName))
|
||||||
|
DeleteUserLastChannel(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Leave(ChannelInfo channelInfo, UserInfo userInfo) {
|
||||||
|
Leave(channelInfo.Name, userInfo.UserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Has(string channelName, long userId) {
|
||||||
|
channelName = channelName.ToLowerInvariant();
|
||||||
|
return ChannelUsers.ContainsKey(channelName)
|
||||||
|
&& ChannelUsers[channelName].Contains(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Has(ChannelInfo channelInfo, UserInfo userInfo) {
|
||||||
|
return Has(channelInfo.Name, userInfo.UserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long[] FilterUsers(string channelName, long[] userIds) {
|
||||||
|
if(userIds.Length < 1)
|
||||||
|
return userIds;
|
||||||
|
|
||||||
|
channelName = channelName.ToLowerInvariant();
|
||||||
|
if(!ChannelUsers.ContainsKey(channelName))
|
||||||
|
return Array.Empty<long>();
|
||||||
|
|
||||||
|
List<long> filtered = new();
|
||||||
|
HashSet<long> channelUserIds = ChannelUsers[channelName];
|
||||||
|
foreach(long userId in userIds)
|
||||||
|
if(channelUserIds.Contains(userId))
|
||||||
|
filtered.Add(userId);
|
||||||
|
|
||||||
|
return filtered.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserInfo[] FilterUsers(ChannelInfo channelInfo, UserInfo[] userInfos) {
|
||||||
|
if(userInfos.Length < 1)
|
||||||
|
return userInfos;
|
||||||
|
|
||||||
|
long[] filteredIds = FilterUsers(channelInfo.Name, userInfos.Select(u => u.UserId).ToArray());
|
||||||
|
if(filteredIds.Length < 1)
|
||||||
|
return Array.Empty<UserInfo>();
|
||||||
|
|
||||||
|
return userInfos.Where(u => filteredIds.Contains(u.UserId)).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasUsers(string channelName, long[] userIds) {
|
||||||
|
return FilterUsers(channelName, userIds).SequenceEqual(userIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasUsers(ChannelInfo channelInfo, UserInfo[] userInfos) {
|
||||||
|
return HasUsers(channelInfo.Name, userInfos.Select(u => u.UserId).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public string[] FilterChannels(long userId, string[] channelNames) {
|
||||||
|
if(channelNames.Length < 1)
|
||||||
|
return channelNames;
|
||||||
|
|
||||||
|
if(!UserChannels.ContainsKey(userId))
|
||||||
|
return Array.Empty<string>();
|
||||||
|
|
||||||
|
List<string> filtered = new();
|
||||||
|
HashSet<string> userChannelNames = UserChannels[userId];
|
||||||
|
foreach(string channelName in userChannelNames)
|
||||||
|
if(userChannelNames.Contains(channelName))
|
||||||
|
filtered.Add(channelName);
|
||||||
|
|
||||||
|
return filtered.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChannelInfo[] FilterChannels(UserInfo userInfo, ChannelInfo[] channelInfos) {
|
||||||
|
if(channelInfos.Length < 1)
|
||||||
|
return channelInfos;
|
||||||
|
|
||||||
|
string[] filteredNames = FilterChannels(userInfo.UserId, channelInfos.Select(c => c.Name).ToArray());
|
||||||
|
if(filteredNames.Length < 1)
|
||||||
|
return Array.Empty<ChannelInfo>();
|
||||||
|
|
||||||
|
return channelInfos.Where(c => filteredNames.Contains(c.Name.ToLowerInvariant())).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasChannels(long userId, string[] channelNames) {
|
||||||
|
if(!UserChannels.ContainsKey(userId))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
HashSet<string> userChannelNames = UserChannels[userId];
|
||||||
|
foreach(string channelName in channelNames)
|
||||||
|
if(!userChannelNames.Contains(channelName.ToLowerInvariant()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasChannels(UserInfo userInfo, ChannelInfo[] channelInfos) {
|
||||||
|
return HasChannels(userInfo.UserId, channelInfos.Select(c => c.Name).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteUser(long userId) {
|
||||||
|
if(!UserChannels.ContainsKey(userId))
|
||||||
|
return;
|
||||||
|
|
||||||
|
HashSet<string> channelNames = UserChannels[userId];
|
||||||
|
UserChannels.Remove(userId);
|
||||||
|
DeleteUserLastChannel(userId);
|
||||||
|
|
||||||
|
foreach(string channelName in channelNames) {
|
||||||
|
if(!ChannelUsers.ContainsKey(channelName))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ChannelUsers[channelName].Remove(userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteUser(UserInfo userInfo) {
|
||||||
|
DeleteUser(userInfo.UserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteChannel(string channelName) {
|
||||||
|
channelName = channelName.ToLowerInvariant();
|
||||||
|
if(!ChannelUsers.ContainsKey(channelName))
|
||||||
|
return;
|
||||||
|
|
||||||
|
HashSet<long> userIds = ChannelUsers[channelName];
|
||||||
|
ChannelUsers.Remove(channelName);
|
||||||
|
|
||||||
|
foreach(long userId in userIds) {
|
||||||
|
if(!UserChannels.ContainsKey(userId))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
UserChannels[userId].Remove(channelName);
|
||||||
|
|
||||||
|
if(IsUserLastChannel(userId, channelName))
|
||||||
|
DeleteUserLastChannel(userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteChannel(ChannelInfo channelInfo) {
|
||||||
|
DeleteChannel(channelInfo.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,17 +9,14 @@ using System.Threading;
|
||||||
|
|
||||||
namespace SharpChat {
|
namespace SharpChat {
|
||||||
public class ChatContext {
|
public class ChatContext {
|
||||||
public record ChannelUserAssoc(long UserId, string ChannelName);
|
|
||||||
|
|
||||||
public readonly SemaphoreSlim ContextAccess = new(1, 1);
|
public readonly SemaphoreSlim ContextAccess = new(1, 1);
|
||||||
|
|
||||||
public Dictionary<string, ChannelInfo> Channels { get; } = new();
|
public ChannelsContext Channels { get; } = new();
|
||||||
public List<ConnectionInfo> Connections { get; } = new();
|
public List<ConnectionInfo> Connections { get; } = new();
|
||||||
public Dictionary<long, UserInfo> Users { get; } = new();
|
public UsersContext Users { get; } = new();
|
||||||
public IEventStorage Events { get; }
|
public IEventStorage Events { get; }
|
||||||
public HashSet<ChannelUserAssoc> ChannelUsers { get; } = new();
|
public ChannelsUsersContext ChannelsUsers { get; } = new();
|
||||||
public Dictionary<long, RateLimiter> UserRateLimiters { get; } = new();
|
public Dictionary<long, RateLimiter> UserRateLimiters { get; } = new();
|
||||||
public Dictionary<long, ChannelInfo> UserLastChannel { get; } = new();
|
|
||||||
|
|
||||||
public ChatContext(IEventStorage evtStore) {
|
public ChatContext(IEventStorage evtStore) {
|
||||||
Events = evtStore;
|
Events = evtStore;
|
||||||
|
@ -41,7 +38,7 @@ namespace SharpChat {
|
||||||
if(targetIds.Length != 2)
|
if(targetIds.Length != 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
UserInfo[] users = Users.Where(kvp => targetIds.Contains(kvp.Key)).Select(kvp => kvp.Value).ToArray();
|
UserInfo[] users = Users.GetMany(targetIds);
|
||||||
UserInfo? target = users.FirstOrDefault(u => u.UserId != mce.SenderId);
|
UserInfo? target = users.FirstOrDefault(u => u.UserId != mce.SenderId);
|
||||||
if(target == null)
|
if(target == null)
|
||||||
return;
|
return;
|
||||||
|
@ -51,12 +48,12 @@ namespace SharpChat {
|
||||||
mce.MessageId,
|
mce.MessageId,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
mce.SenderId,
|
mce.SenderId,
|
||||||
mce.SenderId == user.UserId ? $"{target.LegacyName} {mce.MessageText}" : mce.MessageText,
|
mce.SenderId == user.UserId ? $"{SockChatUtility.GetUserName(target)} {mce.MessageText}" : mce.MessageText,
|
||||||
mce.IsAction,
|
mce.IsAction,
|
||||||
true
|
true
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
ChannelInfo? channel = Channels.Values.FirstOrDefault(c => c.NameEquals(mce.ChannelName));
|
ChannelInfo? channel = Channels.Get(mce.ChannelName, SockChatUtility.SanitiseChannelName);
|
||||||
if(channel != null)
|
if(channel != null)
|
||||||
SendTo(channel, new MessageAddPacket(
|
SendTo(channel, new MessageAddPacket(
|
||||||
mce.MessageId,
|
mce.MessageId,
|
||||||
|
@ -92,7 +89,7 @@ namespace SharpChat {
|
||||||
if(removed > 0)
|
if(removed > 0)
|
||||||
Logger.Write($"Removed {removed} nuked connections from the list.");
|
Logger.Write($"Removed {removed} nuked connections from the list.");
|
||||||
|
|
||||||
foreach(UserInfo user in Users.Values)
|
foreach(UserInfo user in Users.All)
|
||||||
if(!Connections.Any(conn => conn.User == user)) {
|
if(!Connections.Any(conn => conn.User == user)) {
|
||||||
HandleDisconnect(user, UserDisconnectReason.TimeOut);
|
HandleDisconnect(user, UserDisconnectReason.TimeOut);
|
||||||
Logger.Write($"Timed out {user} (no more connections).");
|
Logger.Write($"Timed out {user} (no more connections).");
|
||||||
|
@ -108,28 +105,12 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsInChannel(UserInfo? user, ChannelInfo? channel) {
|
|
||||||
return user != null
|
|
||||||
&& channel != null
|
|
||||||
&& ChannelUsers.Contains(new ChannelUserAssoc(user.UserId, channel.Name));
|
|
||||||
}
|
|
||||||
|
|
||||||
public string[] GetUserChannelNames(UserInfo user) {
|
|
||||||
return ChannelUsers.Where(cu => cu.UserId == user.UserId).Select(cu => cu.ChannelName).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ChannelInfo[] GetUserChannels(UserInfo user) {
|
public ChannelInfo[] GetUserChannels(UserInfo user) {
|
||||||
string[] names = GetUserChannelNames(user);
|
return Channels.GetMany(ChannelsUsers.GetUserChannelNames(user));
|
||||||
return Channels.Values.Where(c => names.Any(n => c.NameEquals(n))).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long[] GetChannelUserIds(ChannelInfo channel) {
|
|
||||||
return ChannelUsers.Where(cu => channel.NameEquals(cu.ChannelName)).Select(cu => cu.UserId).ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserInfo[] GetChannelUsers(ChannelInfo channel) {
|
public UserInfo[] GetChannelUsers(ChannelInfo channel) {
|
||||||
long[] targetIds = GetChannelUserIds(channel);
|
return Users.GetMany(ChannelsUsers.GetChannelUserIds(channel));
|
||||||
return Users.Values.Where(u => targetIds.Contains(u.UserId)).ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateUser(
|
public void UpdateUser(
|
||||||
|
@ -192,11 +173,11 @@ namespace SharpChat {
|
||||||
|
|
||||||
if(hasChanged) {
|
if(hasChanged) {
|
||||||
if(previousName != null)
|
if(previousName != null)
|
||||||
SendToUserChannels(user, new UserUpdateNotificationPacket(previousName, user.LegacyNameWithStatus));
|
SendToUserChannels(user, new UserUpdateNotificationPacket(previousName, SockChatUtility.GetUserNameWithStatus(user)));
|
||||||
|
|
||||||
SendToUserChannels(user, new UserUpdatePacket(
|
SendToUserChannels(user, new UserUpdatePacket(
|
||||||
user.UserId,
|
user.UserId,
|
||||||
user.LegacyNameWithStatus,
|
SockChatUtility.GetUserNameWithStatus(user),
|
||||||
user.Colour,
|
user.Colour,
|
||||||
user.Rank,
|
user.Rank,
|
||||||
user.Permissions
|
user.Permissions
|
||||||
|
@ -222,10 +203,10 @@ namespace SharpChat {
|
||||||
public void HandleChannelEventLog(string channelName, Action<IServerPacket> handler) {
|
public void HandleChannelEventLog(string channelName, Action<IServerPacket> handler) {
|
||||||
foreach(StoredEventInfo msg in Events.GetChannelEventLog(channelName))
|
foreach(StoredEventInfo msg in Events.GetChannelEventLog(channelName))
|
||||||
handler(msg.Type switch {
|
handler(msg.Type switch {
|
||||||
"user:connect" => new UserConnectLogPacket(msg.Created, msg.Sender?.LegacyName ?? string.Empty),
|
"user:connect" => new UserConnectLogPacket(msg.Created, msg.Sender == null ? string.Empty : SockChatUtility.GetUserName(msg.Sender)),
|
||||||
"user:disconnect" => new UserDisconnectLogPacket(
|
"user:disconnect" => new UserDisconnectLogPacket(
|
||||||
msg.Created,
|
msg.Created,
|
||||||
msg.Sender?.LegacyNameWithStatus ?? string.Empty,
|
msg.Sender == null ? string.Empty : SockChatUtility.GetUserNameWithStatus(msg.Sender),
|
||||||
(UserDisconnectReason)msg.Data.RootElement.GetProperty("reason").GetByte()
|
(UserDisconnectReason)msg.Data.RootElement.GetProperty("reason").GetByte()
|
||||||
),
|
),
|
||||||
_ => new MessagePopulatePacket(msg),
|
_ => new MessagePopulatePacket(msg),
|
||||||
|
@ -233,11 +214,11 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleJoin(UserInfo user, ChannelInfo chan, ConnectionInfo conn, int maxMsgLength) {
|
public void HandleJoin(UserInfo user, ChannelInfo chan, ConnectionInfo conn, int maxMsgLength) {
|
||||||
if(!IsInChannel(user, chan)) {
|
if(!ChannelsUsers.Has(chan, user)) {
|
||||||
SendTo(chan, new UserConnectPacket(
|
SendTo(chan, new UserConnectPacket(
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
user.UserId,
|
user.UserId,
|
||||||
user.LegacyNameWithStatus,
|
SockChatUtility.GetUserNameWithStatus(user),
|
||||||
user.Colour,
|
user.Colour,
|
||||||
user.Rank,
|
user.Rank,
|
||||||
user.Permissions
|
user.Permissions
|
||||||
|
@ -247,7 +228,7 @@ namespace SharpChat {
|
||||||
|
|
||||||
conn.Send(new AuthSuccessPacket(
|
conn.Send(new AuthSuccessPacket(
|
||||||
user.UserId,
|
user.UserId,
|
||||||
user.LegacyNameWithStatus,
|
SockChatUtility.GetUserNameWithStatus(user),
|
||||||
user.Colour,
|
user.Colour,
|
||||||
user.Rank,
|
user.Rank,
|
||||||
user.Permissions,
|
user.Permissions,
|
||||||
|
@ -255,32 +236,42 @@ namespace SharpChat {
|
||||||
maxMsgLength
|
maxMsgLength
|
||||||
));
|
));
|
||||||
conn.Send(new UsersPopulatePacket(GetChannelUsers(chan).Except(new[] { user }).Select(
|
conn.Send(new UsersPopulatePacket(GetChannelUsers(chan).Except(new[] { user }).Select(
|
||||||
user => new UsersPopulatePacket.ListEntry(user.UserId, user.LegacyNameWithStatus, user.Colour, user.Rank, user.Permissions, true)
|
user => new UsersPopulatePacket.ListEntry(
|
||||||
|
user.UserId,
|
||||||
|
SockChatUtility.GetUserNameWithStatus(user),
|
||||||
|
user.Colour,
|
||||||
|
user.Rank,
|
||||||
|
user.Permissions,
|
||||||
|
true
|
||||||
|
)
|
||||||
).OrderByDescending(user => user.Rank).ToArray()));
|
).OrderByDescending(user => user.Rank).ToArray()));
|
||||||
|
|
||||||
HandleChannelEventLog(chan.Name, p => conn.Send(p));
|
HandleChannelEventLog(chan.Name, p => conn.Send(p));
|
||||||
|
|
||||||
conn.Send(new ChannelsPopulatePacket(Channels.Values.Where(c => c.Rank <= user.Rank).Select(
|
conn.Send(new ChannelsPopulatePacket(Channels.GetMany(isPublic: true, minRank: user.Rank).Select(
|
||||||
channel => new ChannelsPopulatePacket.ListEntry(channel.Name, channel.HasPassword, channel.IsTemporary)
|
channel => new ChannelsPopulatePacket.ListEntry(channel.Name, channel.HasPassword, channel.IsTemporary)
|
||||||
).ToArray()));
|
).ToArray()));
|
||||||
|
|
||||||
Users.Add(user.UserId, user);
|
if(Users.Get(userId: user.UserId) == null)
|
||||||
|
Users.Add(user);
|
||||||
|
|
||||||
ChannelUsers.Add(new ChannelUserAssoc(user.UserId, chan.Name));
|
ChannelsUsers.Join(chan.Name, user.UserId);
|
||||||
UserLastChannel[user.UserId] = chan;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleDisconnect(UserInfo user, UserDisconnectReason reason = UserDisconnectReason.Leave) {
|
public void HandleDisconnect(UserInfo user, UserDisconnectReason reason = UserDisconnectReason.Leave) {
|
||||||
UpdateUser(user, status: UserStatus.Offline);
|
UpdateUser(user, status: UserStatus.Offline);
|
||||||
Users.Remove(user.UserId);
|
Users.Remove(user.UserId);
|
||||||
UserLastChannel.Remove(user.UserId);
|
|
||||||
|
|
||||||
ChannelInfo[] channels = GetUserChannels(user);
|
ChannelInfo[] channels = GetUserChannels(user);
|
||||||
|
ChannelsUsers.DeleteUser(user);
|
||||||
|
|
||||||
foreach(ChannelInfo chan in channels) {
|
foreach(ChannelInfo chan in channels) {
|
||||||
ChannelUsers.Remove(new ChannelUserAssoc(user.UserId, chan.Name));
|
SendTo(chan, new UserDisconnectPacket(
|
||||||
|
DateTimeOffset.Now,
|
||||||
SendTo(chan, new UserDisconnectPacket(DateTimeOffset.Now, user.UserId, user.LegacyNameWithStatus, reason));
|
user.UserId,
|
||||||
|
SockChatUtility.GetUserNameWithStatus(user),
|
||||||
|
reason
|
||||||
|
));
|
||||||
Events.AddEvent("user:disconnect", user, chan, new { reason = (int)reason }, StoredEventFlags.Log);
|
Events.AddEvent("user:disconnect", user, chan, new { reason = (int)reason }, StoredEventFlags.Log);
|
||||||
|
|
||||||
if(chan.IsTemporary && chan.IsOwner(user))
|
if(chan.IsTemporary && chan.IsOwner(user))
|
||||||
|
@ -289,7 +280,7 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SwitchChannel(UserInfo user, ChannelInfo chan, string password) {
|
public void SwitchChannel(UserInfo user, ChannelInfo chan, string password) {
|
||||||
if(UserLastChannel.TryGetValue(user.UserId, out ChannelInfo? ulc) && chan == ulc) {
|
if(ChannelsUsers.IsUserLastChannel(user, chan)) {
|
||||||
ForceChannel(user);
|
ForceChannel(user);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -312,29 +303,44 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ForceChannelSwitch(UserInfo user, ChannelInfo chan) {
|
public void ForceChannelSwitch(UserInfo user, ChannelInfo chan) {
|
||||||
if(!Channels.ContainsValue(chan))
|
ChannelInfo? oldChan = Channels.Get(ChannelsUsers.GetUserLastChannel(user));
|
||||||
return;
|
|
||||||
|
|
||||||
ChannelInfo oldChan = UserLastChannel[user.UserId];
|
if(oldChan != null) {
|
||||||
|
SendTo(oldChan, new UserChannelLeavePacket(user.UserId));
|
||||||
|
Events.AddEvent("chan:leave", user, oldChan, flags: StoredEventFlags.Log);
|
||||||
|
}
|
||||||
|
|
||||||
SendTo(oldChan, new UserChannelLeavePacket(user.UserId));
|
SendTo(chan, new UserChannelJoinPacket(
|
||||||
Events.AddEvent("chan:leave", user, oldChan, flags: StoredEventFlags.Log);
|
user.UserId,
|
||||||
SendTo(chan, new UserChannelJoinPacket(user.UserId, user.LegacyNameWithStatus, user.Colour, user.Rank, user.Permissions));
|
SockChatUtility.GetUserNameWithStatus(user),
|
||||||
Events.AddEvent("chan:join", user, oldChan, flags: StoredEventFlags.Log);
|
user.Colour,
|
||||||
|
user.Rank,
|
||||||
|
user.Permissions
|
||||||
|
));
|
||||||
|
|
||||||
|
if(oldChan != null)
|
||||||
|
Events.AddEvent("chan:join", user, oldChan, flags: StoredEventFlags.Log);
|
||||||
|
|
||||||
SendTo(user, new ContextClearPacket(ContextClearPacket.ClearMode.MessagesUsers));
|
SendTo(user, new ContextClearPacket(ContextClearPacket.ClearMode.MessagesUsers));
|
||||||
SendTo(user, new UsersPopulatePacket(GetChannelUsers(chan).Except(new[] { user }).Select(
|
SendTo(user, new UsersPopulatePacket(GetChannelUsers(chan).Except(new[] { user }).Select(
|
||||||
user => new UsersPopulatePacket.ListEntry(user.UserId, user.LegacyNameWithStatus, user.Colour, user.Rank, user.Permissions, true)
|
user => new UsersPopulatePacket.ListEntry(
|
||||||
|
user.UserId,
|
||||||
|
SockChatUtility.GetUserNameWithStatus(user),
|
||||||
|
user.Colour,
|
||||||
|
user.Rank,
|
||||||
|
user.Permissions,
|
||||||
|
true
|
||||||
|
)
|
||||||
).OrderByDescending(u => u.Rank).ToArray()));
|
).OrderByDescending(u => u.Rank).ToArray()));
|
||||||
|
|
||||||
HandleChannelEventLog(chan.Name, p => SendTo(user, p));
|
HandleChannelEventLog(chan.Name, p => SendTo(user, p));
|
||||||
ForceChannel(user, chan);
|
ForceChannel(user, chan);
|
||||||
|
|
||||||
ChannelUsers.Remove(new ChannelUserAssoc(user.UserId, oldChan.Name));
|
if(oldChan != null)
|
||||||
ChannelUsers.Add(new ChannelUserAssoc(user.UserId, chan.Name));
|
ChannelsUsers.Leave(oldChan, user);
|
||||||
UserLastChannel[user.UserId] = chan;
|
ChannelsUsers.Join(chan, user);
|
||||||
|
|
||||||
if(oldChan.IsTemporary && oldChan.IsOwner(user))
|
if(oldChan != null && oldChan.IsTemporary && oldChan.IsOwner(user))
|
||||||
RemoveChannel(oldChan);
|
RemoveChannel(oldChan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,22 +352,21 @@ namespace SharpChat {
|
||||||
|
|
||||||
public void SendTo(UserInfo user, IServerPacket packet) {
|
public void SendTo(UserInfo user, IServerPacket packet) {
|
||||||
foreach(ConnectionInfo conn in Connections)
|
foreach(ConnectionInfo conn in Connections)
|
||||||
if(conn.IsAlive && conn.User == user)
|
if(conn.IsAuthed && conn.User!.UserId == user.UserId)
|
||||||
conn.Send(packet);
|
conn.Send(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendTo(ChannelInfo channel, IServerPacket packet) {
|
public void SendTo(ChannelInfo channel, IServerPacket packet) {
|
||||||
// might be faster to grab the users first and then cascade into that SendTo
|
long[] userIds = ChannelsUsers.GetChannelUserIds(channel);
|
||||||
IEnumerable<ConnectionInfo> conns = Connections.Where(c => c.IsAuthed && IsInChannel(c.User, channel));
|
foreach(ConnectionInfo conn in Connections)
|
||||||
foreach(ConnectionInfo conn in conns)
|
if(conn.IsAuthed && userIds.Contains(conn.User!.UserId))
|
||||||
conn.Send(packet);
|
conn.Send(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendToUserChannels(UserInfo user, IServerPacket packet) {
|
public void SendToUserChannels(UserInfo user, IServerPacket packet) {
|
||||||
IEnumerable<ChannelInfo> chans = Channels.Values.Where(c => IsInChannel(user, c));
|
ChannelInfo[] chans = GetUserChannels(user);
|
||||||
IEnumerable<ConnectionInfo> conns = Connections.Where(conn => conn.IsAuthed && ChannelUsers.Any(cu => cu.UserId == conn.User?.UserId && chans.Any(chan => chan.NameEquals(cu.ChannelName))));
|
foreach(ChannelInfo chan in chans)
|
||||||
foreach(ConnectionInfo conn in conns)
|
SendTo(chan, packet);
|
||||||
conn.Send(packet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IPAddress[] GetRemoteAddresses(UserInfo user) {
|
public IPAddress[] GetRemoteAddresses(UserInfo user) {
|
||||||
|
@ -369,10 +374,9 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ForceChannel(UserInfo user, ChannelInfo? chan = null) {
|
public void ForceChannel(UserInfo user, ChannelInfo? chan = null) {
|
||||||
if(chan == null && !UserLastChannel.TryGetValue(user.UserId, out chan))
|
chan ??= Channels.Get(ChannelsUsers.GetUserLastChannel(user));
|
||||||
throw new ArgumentException("no channel???");
|
if(chan != null)
|
||||||
|
SendTo(user, new UserChannelForceJoinPacket(chan.Name));
|
||||||
SendTo(user, new UserChannelForceJoinPacket(chan.Name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateChannel(
|
public void UpdateChannel(
|
||||||
|
@ -381,9 +385,6 @@ namespace SharpChat {
|
||||||
int? minRank = null,
|
int? minRank = null,
|
||||||
string? password = null
|
string? password = null
|
||||||
) {
|
) {
|
||||||
if(!Channels.ContainsValue(channel))
|
|
||||||
throw new ArgumentException("Provided channel is not registered with this manager.", nameof(channel));
|
|
||||||
|
|
||||||
string prevName = channel.Name;
|
string prevName = channel.Name;
|
||||||
|
|
||||||
if(temporary.HasValue)
|
if(temporary.HasValue)
|
||||||
|
@ -396,21 +397,21 @@ namespace SharpChat {
|
||||||
channel.Password = password;
|
channel.Password = password;
|
||||||
|
|
||||||
// TODO: Users that no longer have access to the channel/gained access to the channel by the rank change should receive delete and create packets respectively
|
// TODO: Users that no longer have access to the channel/gained access to the channel by the rank change should receive delete and create packets respectively
|
||||||
foreach(UserInfo user in Users.Values.Where(u => u.Rank >= channel.Rank)) {
|
// the server currently doesn't keep track of what channels a user is already aware of so can't really simulate this yet.
|
||||||
|
foreach(UserInfo user in Users.GetMany(minRank: channel.Rank))
|
||||||
SendTo(user, new ChannelUpdatePacket(prevName, channel.Name, channel.HasPassword, channel.IsTemporary));
|
SendTo(user, new ChannelUpdatePacket(prevName, channel.Name, channel.HasPassword, channel.IsTemporary));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveChannel(ChannelInfo channel) {
|
public void RemoveChannel(ChannelInfo channel) {
|
||||||
if(channel == null || !Channels.Any())
|
if(channel == null || Channels.PublicCount > 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ChannelInfo? defaultChannel = Channels.Values.FirstOrDefault();
|
ChannelInfo? defaultChannel = Channels.MainChannel;
|
||||||
if(defaultChannel == null)
|
if(defaultChannel == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Remove channel from the listing
|
// Remove channel from the listing
|
||||||
Channels.Remove(channel.Name);
|
Channels.Remove(channel);
|
||||||
|
|
||||||
// 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.
|
||||||
|
@ -418,7 +419,7 @@ namespace SharpChat {
|
||||||
SwitchChannel(user, defaultChannel, string.Empty);
|
SwitchChannel(user, defaultChannel, string.Empty);
|
||||||
|
|
||||||
// Broadcast deletion of channel
|
// Broadcast deletion of channel
|
||||||
foreach(UserInfo user in Users.Values.Where(u => u.Rank >= channel.Rank))
|
foreach(UserInfo user in Users.GetMany(minRank: channel.Rank))
|
||||||
SendTo(user, new ChannelDeletePacket(channel.Name));
|
SendTo(user, new ChannelDeletePacket(channel.Name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,12 +33,12 @@ namespace SharpChat.Commands {
|
||||||
|
|
||||||
string createChanName = string.Join('_', ctx.Args.Skip(createChanHasHierarchy ? 1 : 0));
|
string createChanName = string.Join('_', ctx.Args.Skip(createChanHasHierarchy ? 1 : 0));
|
||||||
|
|
||||||
if(!ChannelInfo.CheckName(createChanName)) {
|
if(!SockChatUtility.CheckChannelName(createChanName)) {
|
||||||
ctx.Chat.SendTo(ctx.User, new ChannelNameFormatErrorPacket());
|
ctx.Chat.SendTo(ctx.User, new ChannelNameFormatErrorPacket());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ctx.Chat.Channels.Values.Any(c => c.NameEquals(createChanName))) {
|
if(ctx.Chat.Channels.Get(createChanName, SockChatUtility.SanitiseChannelName) != null) {
|
||||||
ctx.Chat.SendTo(ctx.User, new ChannelNameInUseErrorPacket(createChanName));
|
ctx.Chat.SendTo(ctx.User, new ChannelNameInUseErrorPacket(createChanName));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -50,8 +50,8 @@ namespace SharpChat.Commands {
|
||||||
ownerId: ctx.User.UserId
|
ownerId: ctx.User.UserId
|
||||||
);
|
);
|
||||||
|
|
||||||
ctx.Chat.Channels.Add(createChan.Name, createChan);
|
ctx.Chat.Channels.Add(createChan, sanitiseName: SockChatUtility.SanitiseChannelName);
|
||||||
foreach(UserInfo ccu in ctx.Chat.Users.Values.Where(u => u.Rank >= ctx.Channel.Rank))
|
foreach(UserInfo ccu in ctx.Chat.Users.GetMany(minRank: ctx.Channel.Rank))
|
||||||
ctx.Chat.SendTo(ccu, new ChannelCreatePacket(
|
ctx.Chat.SendTo(ccu, new ChannelCreatePacket(
|
||||||
ctx.Channel.Name,
|
ctx.Channel.Name,
|
||||||
ctx.Channel.HasPassword,
|
ctx.Channel.HasPassword,
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace SharpChat.Commands {
|
||||||
}
|
}
|
||||||
|
|
||||||
string delChanName = string.Join('_', ctx.Args);
|
string delChanName = string.Join('_', ctx.Args);
|
||||||
ChannelInfo? delChan = ctx.Chat.Channels.Values.FirstOrDefault(c => c.NameEquals(delChanName));
|
ChannelInfo? delChan = ctx.Chat.Channels.Get(delChanName, SockChatUtility.SanitiseChannelName);
|
||||||
|
|
||||||
if(delChan == null) {
|
if(delChan == null) {
|
||||||
ctx.Chat.SendTo(ctx.User, new ChannelNotFoundErrorPacket(delChanName));
|
ctx.Chat.SendTo(ctx.User, new ChannelNotFoundErrorPacket(delChanName));
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace SharpChat.Commands {
|
||||||
|
|
||||||
public void Dispatch(UserCommandContext ctx) {
|
public void Dispatch(UserCommandContext ctx) {
|
||||||
string joinChanStr = ctx.Args.FirstOrDefault() ?? string.Empty;
|
string joinChanStr = ctx.Args.FirstOrDefault() ?? string.Empty;
|
||||||
ChannelInfo? joinChan = ctx.Chat.Channels.Values.FirstOrDefault(c => c.NameEquals(joinChanStr));
|
ChannelInfo? joinChan = ctx.Chat.Channels.Get(joinChanStr, SockChatUtility.SanitiseChannelName);
|
||||||
|
|
||||||
if(joinChan == null) {
|
if(joinChan == null) {
|
||||||
ctx.Chat.SendTo(ctx.User, new ChannelNotFoundErrorPacket(joinChanStr));
|
ctx.Chat.SendTo(ctx.User, new ChannelNotFoundErrorPacket(joinChanStr));
|
||||||
|
|
|
@ -30,13 +30,14 @@ namespace SharpChat.Commands {
|
||||||
int banReasonIndex = 1;
|
int banReasonIndex = 1;
|
||||||
UserInfo? banUser = null;
|
UserInfo? banUser = null;
|
||||||
|
|
||||||
if(string.IsNullOrEmpty(banUserTarget) || (banUser = ctx.Chat.Users.Values.FirstOrDefault(u => u.NameEquals(banUserTarget))) == null) {
|
(string name, UsersContext.NameTarget target) = SockChatUtility.ExplodeUserName(banUserTarget);
|
||||||
|
if(string.IsNullOrEmpty(name) || (banUser = ctx.Chat.Users.Get(name: name, nameTarget: target)) == null) {
|
||||||
ctx.Chat.SendTo(ctx.User, new UserNotFoundErrorPacket(banUserTarget));
|
ctx.Chat.SendTo(ctx.User, new UserNotFoundErrorPacket(banUserTarget));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!ctx.User.IsSuper && banUser.Rank >= ctx.User.Rank && banUser != ctx.User) {
|
if(!ctx.User.IsSuper && banUser.Rank >= ctx.User.Rank && banUser != ctx.User) {
|
||||||
ctx.Chat.SendTo(ctx.User, new KickBanNotAllowedErrorPacket(banUser.LegacyName));
|
ctx.Chat.SendTo(ctx.User, new KickBanNotAllowedErrorPacket(SockChatUtility.GetUserName(banUser)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +67,7 @@ namespace SharpChat.Commands {
|
||||||
MisuzuBanInfo? fbi = await Misuzu.CheckBanAsync(userId, userIp);
|
MisuzuBanInfo? fbi = await Misuzu.CheckBanAsync(userId, userIp);
|
||||||
|
|
||||||
if(fbi != null && fbi.IsBanned && !fbi.HasExpired) {
|
if(fbi != null && fbi.IsBanned && !fbi.HasExpired) {
|
||||||
ctx.Chat.SendTo(ctx.User, new KickBanNotAllowedErrorPacket(banUser.LegacyName));
|
ctx.Chat.SendTo(ctx.User, new KickBanNotAllowedErrorPacket(SockChatUtility.GetUserName(banUser)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,8 @@ namespace SharpChat.Commands {
|
||||||
}
|
}
|
||||||
|
|
||||||
string whisperUserStr = ctx.Args.FirstOrDefault() ?? string.Empty;
|
string whisperUserStr = ctx.Args.FirstOrDefault() ?? string.Empty;
|
||||||
UserInfo? whisperUser = ctx.Chat.Users.Values.FirstOrDefault(u => u.NameEquals(whisperUserStr));
|
(string name, UsersContext.NameTarget target) = SockChatUtility.ExplodeUserName(whisperUserStr);
|
||||||
|
UserInfo? whisperUser = ctx.Chat.Users.Get(name: name, nameTarget: target);
|
||||||
|
|
||||||
if(whisperUser == null) {
|
if(whisperUser == null) {
|
||||||
ctx.Chat.SendTo(ctx.User, new UserNotFoundErrorPacket(whisperUserStr));
|
ctx.Chat.SendTo(ctx.User, new UserNotFoundErrorPacket(whisperUserStr));
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using SharpChat.Misuzu;
|
using SharpChat.Misuzu;
|
||||||
using SharpChat.Packet;
|
using SharpChat.Packet;
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using SharpChat.Misuzu;
|
using SharpChat.Misuzu;
|
||||||
using SharpChat.Packet;
|
using SharpChat.Packet;
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
@ -31,10 +30,11 @@ namespace SharpChat.Commands {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UserInfo? unbanUser = ctx.Chat.Users.Values.FirstOrDefault(u => u.NameEquals(unbanUserTarget));
|
(string name, UsersContext.NameTarget target) = SockChatUtility.ExplodeUserName(unbanUserTarget);
|
||||||
|
UserInfo? unbanUser = ctx.Chat.Users.Get(name: name, nameTarget: target);
|
||||||
if(unbanUser == null && long.TryParse(unbanUserTarget, out long unbanUserId)) {
|
if(unbanUser == null && long.TryParse(unbanUserTarget, out long unbanUserId)) {
|
||||||
unbanUserTargetIsName = false;
|
unbanUserTargetIsName = false;
|
||||||
unbanUser = ctx.Chat.Users.Values.FirstOrDefault(u => u.UserId == unbanUserId);
|
unbanUser = ctx.Chat.Users.Get(unbanUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(unbanUser != null)
|
if(unbanUser != null)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using SharpChat.Packet;
|
using System.Linq;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
namespace SharpChat.Commands {
|
||||||
public class UserAFKCommand : IUserCommand {
|
public class UserAFKCommand : IUserCommand {
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace SharpChat.Commands {
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
|
||||||
if(setOthersNick && long.TryParse(ctx.Args.FirstOrDefault(), out long targetUserId) && targetUserId > 0) {
|
if(setOthersNick && long.TryParse(ctx.Args.FirstOrDefault(), out long targetUserId) && targetUserId > 0) {
|
||||||
targetUser = ctx.Chat.Users.Values.FirstOrDefault(u => u.UserId == targetUserId);
|
targetUser = ctx.Chat.Users.Get(targetUserId);
|
||||||
++offset;
|
++offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ namespace SharpChat.Commands {
|
||||||
else if(string.IsNullOrEmpty(nickStr))
|
else if(string.IsNullOrEmpty(nickStr))
|
||||||
nickStr = string.Empty;
|
nickStr = string.Empty;
|
||||||
|
|
||||||
if(!string.IsNullOrWhiteSpace(nickStr) && ctx.Chat.Users.Values.Any(u => u.NameEquals(nickStr))) {
|
if(!string.IsNullOrWhiteSpace(nickStr) && ctx.Chat.Users.Get(name: nickStr, nameTarget: UsersContext.NameTarget.UserAndNickName) != null) {
|
||||||
ctx.Chat.SendTo(ctx.User, new UserNameInUseErrorPacket(nickStr));
|
ctx.Chat.SendTo(ctx.User, new UserNameInUseErrorPacket(nickStr));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,13 +12,13 @@ namespace SharpChat.Commands {
|
||||||
|
|
||||||
if(string.IsNullOrEmpty(channelName)) {
|
if(string.IsNullOrEmpty(channelName)) {
|
||||||
ctx.Chat.SendTo(ctx.User, new WhoServerResponsePacket(
|
ctx.Chat.SendTo(ctx.User, new WhoServerResponsePacket(
|
||||||
ctx.Chat.Users.Values.Select(u => u.LegacyName).ToArray(),
|
ctx.Chat.Users.All.Select(u => SockChatUtility.GetUserNameWithStatus(u)).ToArray(),
|
||||||
ctx.User.LegacyName
|
SockChatUtility.GetUserName(ctx.User)
|
||||||
));
|
));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelInfo? channel = ctx.Chat.Channels.Values.FirstOrDefault(c => c.NameEquals(channelName));
|
ChannelInfo? channel = ctx.Chat.Channels.Get(channelName, SockChatUtility.SanitiseChannelName);
|
||||||
|
|
||||||
if(channel == null) {
|
if(channel == null) {
|
||||||
ctx.Chat.SendTo(ctx.User, new ChannelNotFoundErrorPacket(channelName));
|
ctx.Chat.SendTo(ctx.User, new ChannelNotFoundErrorPacket(channelName));
|
||||||
|
@ -32,8 +32,8 @@ namespace SharpChat.Commands {
|
||||||
|
|
||||||
ctx.Chat.SendTo(ctx.User, new WhoChannelResponsePacket(
|
ctx.Chat.SendTo(ctx.User, new WhoChannelResponsePacket(
|
||||||
channel.Name,
|
channel.Name,
|
||||||
ctx.Chat.GetChannelUsers(channel).Select(user => user.LegacyName).ToArray(),
|
ctx.Chat.GetChannelUsers(channel).Select(user => SockChatUtility.GetUserNameWithStatus(user)).ToArray(),
|
||||||
ctx.User.LegacyName
|
SockChatUtility.GetUserNameWithStatus(ctx.User)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,8 @@ namespace SharpChat.Commands {
|
||||||
string ipUserStr = ctx.Args.FirstOrDefault() ?? string.Empty;
|
string ipUserStr = ctx.Args.FirstOrDefault() ?? string.Empty;
|
||||||
UserInfo? ipUser;
|
UserInfo? ipUser;
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(ipUserStr) || (ipUser = ctx.Chat.Users.Values.FirstOrDefault(u => u.NameEquals(ipUserStr))) == null) {
|
(string name, UsersContext.NameTarget target) = SockChatUtility.ExplodeUserName(ipUserStr);
|
||||||
|
if(string.IsNullOrWhiteSpace(name) || (ipUser = ctx.Chat.Users.Get(name: name, nameTarget: target)) == null) {
|
||||||
ctx.Chat.SendTo(ctx.User, new UserNotFoundErrorPacket(ipUserStr));
|
ctx.Chat.SendTo(ctx.User, new UserNotFoundErrorPacket(ipUserStr));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace SharpChat.Config {
|
namespace SharpChat.Config {
|
||||||
public interface IConfig : IDisposable {
|
public interface IConfig : IDisposable {
|
||||||
|
|
|
@ -1,10 +1,4 @@
|
||||||
using System;
|
namespace SharpChat.Events {
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace SharpChat.Events {
|
|
||||||
public interface IChatEvent {
|
public interface IChatEvent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System.Text.Json.Serialization;
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace SharpChat.Misuzu {
|
namespace SharpChat.Misuzu {
|
||||||
public class MisuzuAuthInfo {
|
public class MisuzuAuthInfo {
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
using System;
|
namespace SharpChat.Packet {
|
||||||
|
|
||||||
namespace SharpChat.Packet {
|
|
||||||
public class AuthSuccessPacket : ServerPacket {
|
public class AuthSuccessPacket : ServerPacket {
|
||||||
private readonly long UserId;
|
private readonly long UserId;
|
||||||
private readonly string UserName;
|
private readonly string UserName;
|
||||||
|
@ -41,7 +39,7 @@ namespace SharpChat.Packet {
|
||||||
UserPerms.HasFlag(UserPermissions.CreateChannel) ? (
|
UserPerms.HasFlag(UserPermissions.CreateChannel) ? (
|
||||||
UserPerms.HasFlag(UserPermissions.SetChannelPermanent) ? 2 : 1
|
UserPerms.HasFlag(UserPermissions.SetChannelPermanent) ? 2 : 1
|
||||||
) : 0,
|
) : 0,
|
||||||
ChannelName,
|
SockChatUtility.SanitiseChannelName(ChannelName),
|
||||||
MaxMessageLength
|
MaxMessageLength
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
using System;
|
namespace SharpChat.Packet {
|
||||||
|
|
||||||
namespace SharpChat.Packet {
|
|
||||||
public class ChannelCreatePacket : ServerPacket {
|
public class ChannelCreatePacket : ServerPacket {
|
||||||
private readonly string ChannelName;
|
private readonly string ChannelName;
|
||||||
private readonly bool ChannelHasPassword;
|
private readonly bool ChannelHasPassword;
|
||||||
|
@ -19,7 +17,7 @@ namespace SharpChat.Packet {
|
||||||
public override string Pack() {
|
public override string Pack() {
|
||||||
return string.Format(
|
return string.Format(
|
||||||
"4\t0\t{0}\t{1}\t{2}",
|
"4\t0\t{0}\t{1}\t{2}",
|
||||||
ChannelName,
|
SockChatUtility.SanitiseChannelName(ChannelName),
|
||||||
ChannelHasPassword ? 1 : 0,
|
ChannelHasPassword ? 1 : 0,
|
||||||
ChannelIsTemporary ? 1 : 0
|
ChannelIsTemporary ? 1 : 0
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,7 +11,12 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Pack() {
|
public override string Pack() {
|
||||||
return string.Format("2\t{0}\t-1\t0\fcrchan\f{1}\t{2}\t10010", Timestamp, ChannelName, SequenceId);
|
return string.Format(
|
||||||
|
"2\t{0}\t-1\t0\fcrchan\f{1}\t{2}\t10010",
|
||||||
|
Timestamp,
|
||||||
|
SockChatUtility.SanitiseChannelName(ChannelName),
|
||||||
|
SequenceId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,12 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Pack() {
|
public override string Pack() {
|
||||||
return string.Format("2\t{0}\t-1\t1\fndchan\f{1}\t{2}\t10010", Timestamp, ChannelName, SequenceId);
|
return string.Format(
|
||||||
|
"2\t{0}\t-1\t1\fndchan\f{1}\t{2}\t10010",
|
||||||
|
Timestamp,
|
||||||
|
SockChatUtility.SanitiseChannelName(ChannelName),
|
||||||
|
SequenceId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
using System;
|
namespace SharpChat.Packet {
|
||||||
|
|
||||||
namespace SharpChat.Packet {
|
|
||||||
public class ChannelDeletePacket : ServerPacket {
|
public class ChannelDeletePacket : ServerPacket {
|
||||||
private readonly string ChannelName;
|
private readonly string ChannelName;
|
||||||
|
|
||||||
|
@ -9,7 +7,10 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Pack() {
|
public override string Pack() {
|
||||||
return string.Format("4\t2\t{0}", ChannelName);
|
return string.Format(
|
||||||
|
"4\t2\t{0}",
|
||||||
|
SockChatUtility.SanitiseChannelName(ChannelName)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,12 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Pack() {
|
public override string Pack() {
|
||||||
return string.Format("2\t{0}\t-1\t0\fdelchan\f{1}\t{2}\t10010", Timestamp, ChannelName, SequenceId);
|
return string.Format(
|
||||||
|
"2\t{0}\t-1\t0\fdelchan\f{1}\t{2}\t10010",
|
||||||
|
Timestamp,
|
||||||
|
SockChatUtility.SanitiseChannelName(ChannelName),
|
||||||
|
SequenceId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,12 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Pack() {
|
public override string Pack() {
|
||||||
return string.Format("2\t{0}\t-1\t1\fnischan\f{1}\t{2}\t10010", Timestamp, ChannelName, SequenceId);
|
return string.Format(
|
||||||
|
"2\t{0}\t-1\t1\fnischan\f{1}\t{2}\t10010",
|
||||||
|
Timestamp,
|
||||||
|
SockChatUtility.SanitiseChannelName(ChannelName),
|
||||||
|
SequenceId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,12 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Pack() {
|
public override string Pack() {
|
||||||
return string.Format("2\t{0}\t-1\t1\fnochan\f{1}\t{2}\t10010", Timestamp, ChannelName, SequenceId);
|
return string.Format(
|
||||||
|
"2\t{0}\t-1\t1\fnochan\f{1}\t{2}\t10010",
|
||||||
|
Timestamp,
|
||||||
|
SockChatUtility.SanitiseChannelName(ChannelName),
|
||||||
|
SequenceId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,12 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Pack() {
|
public override string Pack() {
|
||||||
return string.Format("2\t{0}\t-1\t1\fipwchan\f{1}\t{2}\t10010", Timestamp, ChannelName, SequenceId);
|
return string.Format(
|
||||||
|
"2\t{0}\t-1\t1\fipwchan\f{1}\t{2}\t10010",
|
||||||
|
Timestamp,
|
||||||
|
SockChatUtility.SanitiseChannelName(ChannelName),
|
||||||
|
SequenceId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,12 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Pack() {
|
public override string Pack() {
|
||||||
return string.Format("2\t{0}\t-1\t1\fipchan\f{1}\t{2}\t10010", Timestamp, ChannelName, SequenceId);
|
return string.Format(
|
||||||
|
"2\t{0}\t-1\t1\fipchan\f{1}\t{2}\t10010",
|
||||||
|
Timestamp,
|
||||||
|
SockChatUtility.SanitiseChannelName(ChannelName),
|
||||||
|
SequenceId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
using System;
|
namespace SharpChat.Packet {
|
||||||
|
|
||||||
namespace SharpChat.Packet {
|
|
||||||
public class ChannelUpdatePacket : ServerPacket {
|
public class ChannelUpdatePacket : ServerPacket {
|
||||||
private readonly string ChannelNamePrevious;
|
private readonly string ChannelNamePrevious;
|
||||||
private readonly string ChannelNameNew;
|
private readonly string ChannelNameNew;
|
||||||
|
@ -22,8 +20,8 @@ namespace SharpChat.Packet {
|
||||||
public override string Pack() {
|
public override string Pack() {
|
||||||
return string.Format(
|
return string.Format(
|
||||||
"4\t1\t{0}\t{1}\t{2}\t{3}",
|
"4\t1\t{0}\t{1}\t{2}\t{3}",
|
||||||
ChannelNamePrevious,
|
SockChatUtility.SanitiseChannelName(ChannelNamePrevious),
|
||||||
ChannelNameNew,
|
SockChatUtility.SanitiseChannelName(ChannelNameNew),
|
||||||
ChannelHasPassword ? 1 : 0,
|
ChannelHasPassword ? 1 : 0,
|
||||||
ChannelIsTemporary ? 1 : 0
|
ChannelIsTemporary ? 1 : 0
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System.Text;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace SharpChat.Packet {
|
namespace SharpChat.Packet {
|
||||||
public class ChannelsPopulatePacket : ServerPacket {
|
public class ChannelsPopulatePacket : ServerPacket {
|
||||||
|
@ -19,7 +18,7 @@ namespace SharpChat.Packet {
|
||||||
foreach(ListEntry entry in Entries)
|
foreach(ListEntry entry in Entries)
|
||||||
sb.AppendFormat(
|
sb.AppendFormat(
|
||||||
"\t{0}\t{1}\t{2}",
|
"\t{0}\t{1}\t{2}",
|
||||||
entry.Name,
|
SockChatUtility.SanitiseChannelName(entry.Name),
|
||||||
entry.HasPassword ? 1 : 0,
|
entry.HasPassword ? 1 : 0,
|
||||||
entry.IsTemporary ? 1 : 0
|
entry.IsTemporary ? 1 : 0
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,7 +11,12 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Pack() {
|
public override string Pack() {
|
||||||
return string.Format("2\t{0}\t-1\t1\fcmdna\f/{1}\t{2}\t10010", Timestamp, CommandName, SequenceId);
|
return string.Format(
|
||||||
|
"2\t{0}\t-1\t1\fcmdna\f/{1}\t{2}\t10010",
|
||||||
|
Timestamp,
|
||||||
|
CommandName,
|
||||||
|
SequenceId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace SharpChat.Packet {
|
||||||
public override string Pack() {
|
public override string Pack() {
|
||||||
return string.Format(
|
return string.Format(
|
||||||
"7\t1\t{0}\t-1\tChatBot\tinherit\t\t0\fsay\f{1}\twelcome\t0\t10010",
|
"7\t1\t{0}\t-1\tChatBot\tinherit\t\t0\fsay\f{1}\twelcome\t0\t10010",
|
||||||
Timestamp, Utility.Sanitise(Body)
|
Timestamp, SockChatUtility.SanitiseMessageBody(Body)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Pack() {
|
public override string Pack() {
|
||||||
string body = Utility.Sanitise(Body);
|
string body = SockChatUtility.SanitiseMessageBody(Body);
|
||||||
if(IsAction)
|
if(IsAction)
|
||||||
body = string.Format("<i>{0}</i>", body);
|
body = string.Format("<i>{0}</i>", body);
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace SharpChat.Packet {
|
||||||
return string.Format(
|
return string.Format(
|
||||||
"2\t{0}\t-1\t0\fsay\f{1}\t{2}\t10010",
|
"2\t{0}\t-1\t0\fsay\f{1}\t{2}\t10010",
|
||||||
Timestamp,
|
Timestamp,
|
||||||
Utility.Sanitise(Body),
|
SockChatUtility.SanitiseMessageBody(Body),
|
||||||
SequenceId
|
SequenceId
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using SharpChat.EventStorage;
|
using SharpChat.EventStorage;
|
||||||
using System;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace SharpChat.Packet {
|
namespace SharpChat.Packet {
|
||||||
|
@ -32,7 +31,7 @@ namespace SharpChat.Packet {
|
||||||
sb.AppendFormat(
|
sb.AppendFormat(
|
||||||
"{0}\t{1}\t{2}\t{3} {4} {5} {6} {7}",
|
"{0}\t{1}\t{2}\t{3} {4} {5} {6} {7}",
|
||||||
Event.Sender?.UserId,
|
Event.Sender?.UserId,
|
||||||
Event.Sender?.LegacyNameWithStatus,
|
Event.Sender == null ? string.Empty : SockChatUtility.GetUserNameWithStatus(Event.Sender),
|
||||||
Event.Sender?.Colour,
|
Event.Sender?.Colour,
|
||||||
Event.Sender?.Rank,
|
Event.Sender?.Rank,
|
||||||
Event.Sender?.Permissions.HasFlag(UserPermissions.KickUser) == true ? 1 : 0,
|
Event.Sender?.Permissions.HasFlag(UserPermissions.KickUser) == true ? 1 : 0,
|
||||||
|
@ -49,7 +48,7 @@ namespace SharpChat.Packet {
|
||||||
if(isBroadcast)
|
if(isBroadcast)
|
||||||
sb.Append("0\fsay\f");
|
sb.Append("0\fsay\f");
|
||||||
|
|
||||||
string body = Utility.Sanitise(Event.Data.RootElement.GetProperty("text").GetString());
|
string body = SockChatUtility.SanitiseMessageBody(Event.Data.RootElement.GetProperty("text").GetString());
|
||||||
if(isAction)
|
if(isAction)
|
||||||
body = string.Format("<i>{0}</i>", body);
|
body = string.Format("<i>{0}</i>", body);
|
||||||
|
|
||||||
|
@ -57,11 +56,19 @@ namespace SharpChat.Packet {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "chan:join":
|
case "chan:join":
|
||||||
sb.AppendFormat("{0}\t0\fjchan\f{1}", V1_CHATBOT, Event.Sender?.LegacyName);
|
sb.AppendFormat(
|
||||||
|
"{0}\t0\fjchan\f{1}",
|
||||||
|
V1_CHATBOT,
|
||||||
|
Event.Sender == null ? string.Empty : SockChatUtility.GetUserName(Event.Sender)
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "chan:leave":
|
case "chan:leave":
|
||||||
sb.AppendFormat("{0}\t0\flchan\f{1}", V1_CHATBOT, Event.Sender?.LegacyName);
|
sb.AppendFormat(
|
||||||
|
"{0}\t0\flchan\f{1}",
|
||||||
|
V1_CHATBOT,
|
||||||
|
Event.Sender == null ? string.Empty : SockChatUtility.GetUserName(Event.Sender)
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
using System;
|
namespace SharpChat.Packet {
|
||||||
|
|
||||||
namespace SharpChat.Packet {
|
|
||||||
public class UserChannelForceJoinPacket : ServerPacket {
|
public class UserChannelForceJoinPacket : ServerPacket {
|
||||||
private readonly string ChannelName;
|
private readonly string ChannelName;
|
||||||
|
|
||||||
|
@ -9,7 +7,10 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Pack() {
|
public override string Pack() {
|
||||||
return string.Format("5\t2\t{0}", ChannelName);
|
return string.Format(
|
||||||
|
"5\t2\t{0}",
|
||||||
|
SockChatUtility.SanitiseChannelName(ChannelName)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
using System;
|
namespace SharpChat.Packet {
|
||||||
|
|
||||||
namespace SharpChat.Packet {
|
|
||||||
public class UserChannelJoinPacket : ServerPacket {
|
public class UserChannelJoinPacket : ServerPacket {
|
||||||
private readonly long UserId;
|
private readonly long UserId;
|
||||||
private readonly string UserName;
|
private readonly string UserName;
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
using System;
|
namespace SharpChat.Packet {
|
||||||
|
|
||||||
namespace SharpChat.Packet {
|
|
||||||
public class UserUpdatePacket : ServerPacket {
|
public class UserUpdatePacket : ServerPacket {
|
||||||
private readonly long UserId;
|
private readonly long UserId;
|
||||||
private readonly string UserName;
|
private readonly string UserName;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System.Text;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace SharpChat.Packet {
|
namespace SharpChat.Packet {
|
||||||
public class UsersPopulatePacket : ServerPacket {
|
public class UsersPopulatePacket : ServerPacket {
|
||||||
|
|
|
@ -11,7 +11,12 @@ namespace SharpChat.Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Pack() {
|
public override string Pack() {
|
||||||
return string.Format("2\t{0}\t-1\t1\fwhoerr\f{1}\t{2}\t10010", Timestamp, ChannelName, SequenceId);
|
return string.Format(
|
||||||
|
"2\t{0}\t-1\t1\fwhoerr\f{1}\t{2}\t10010",
|
||||||
|
Timestamp,
|
||||||
|
SockChatUtility.SanitiseChannelName(ChannelName),
|
||||||
|
SequenceId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,11 @@ namespace SharpChat.Packet {
|
||||||
public override string Pack() {
|
public override string Pack() {
|
||||||
StringBuilder sb = new();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
sb.AppendFormat("2\t{0}\t-1\t0\fwhochan\f{1}\f", Timestamp, ChannelName);
|
sb.AppendFormat(
|
||||||
|
"2\t{0}\t-1\t0\fwhochan\f{1}\f",
|
||||||
|
Timestamp,
|
||||||
|
SockChatUtility.SanitiseChannelName(ChannelName)
|
||||||
|
);
|
||||||
|
|
||||||
foreach(string userName in Users) {
|
foreach(string userName in Users) {
|
||||||
sb.Append(@"<a href=""javascript:void(0);"" onclick=""UI.InsertChatText(this.innerHTML);""");
|
sb.Append(@"<a href=""javascript:void(0);"" onclick=""UI.InsertChatText(this.innerHTML);""");
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
using System;
|
namespace SharpChat {
|
||||||
|
|
||||||
namespace SharpChat {
|
|
||||||
public class PacketHandlerContext {
|
public class PacketHandlerContext {
|
||||||
public string Text { get; }
|
public string Text { get; }
|
||||||
public ChatContext Chat { get; }
|
public ChatContext Chat { get; }
|
||||||
|
|
|
@ -16,12 +16,12 @@ namespace SharpChat.PacketHandlers {
|
||||||
|
|
||||||
public AuthHandler(
|
public AuthHandler(
|
||||||
MisuzuClient msz,
|
MisuzuClient msz,
|
||||||
ChannelInfo defaultChannel,
|
ChannelInfo? defaultChannel,
|
||||||
CachedValue<int> maxMsgLength,
|
CachedValue<int> maxMsgLength,
|
||||||
CachedValue<int> maxConns
|
CachedValue<int> maxConns
|
||||||
) {
|
) {
|
||||||
Misuzu = msz;
|
Misuzu = msz;
|
||||||
DefaultChannel = defaultChannel;
|
DefaultChannel = defaultChannel ?? throw new ArgumentNullException(nameof(defaultChannel));
|
||||||
MaxMessageLength = maxMsgLength;
|
MaxMessageLength = maxMsgLength;
|
||||||
MaxConnections = maxConns;
|
MaxConnections = maxConns;
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,7 @@ namespace SharpChat.PacketHandlers {
|
||||||
|
|
||||||
await ctx.Chat.ContextAccess.WaitAsync();
|
await ctx.Chat.ContextAccess.WaitAsync();
|
||||||
try {
|
try {
|
||||||
UserInfo? user = ctx.Chat.Users.Values.FirstOrDefault(u => u.UserId == fai.UserId);
|
UserInfo? user = ctx.Chat.Users.Get(fai.UserId);
|
||||||
|
|
||||||
if(user == null)
|
if(user == null)
|
||||||
user = new UserInfo(
|
user = new UserInfo(
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace SharpChat.PacketHandlers {
|
||||||
ctx.Chat.ContextAccess.Wait();
|
ctx.Chat.ContextAccess.Wait();
|
||||||
try {
|
try {
|
||||||
if(LastBump < DateTimeOffset.UtcNow - BumpInterval) {
|
if(LastBump < DateTimeOffset.UtcNow - BumpInterval) {
|
||||||
(string, string)[] bumpList = ctx.Chat.Users.Values
|
(string, string)[] bumpList = ctx.Chat.Users.All
|
||||||
.Where(u => u.Status == UserStatus.Online && ctx.Chat.Connections.Any(c => c.User == u))
|
.Where(u => u.Status == UserStatus.Online && ctx.Chat.Connections.Any(c => c.User == u))
|
||||||
.Select(u => (u.UserId.ToString(), ctx.Chat.GetRemoteAddresses(u).FirstOrDefault()?.ToString() ?? string.Empty))
|
.Select(u => (u.UserId.ToString(), ctx.Chat.GetRemoteAddresses(u).FirstOrDefault()?.ToString() ?? string.Empty))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
using SharpChat.Commands;
|
using SharpChat.Config;
|
||||||
using SharpChat.Config;
|
|
||||||
using SharpChat.Events;
|
using SharpChat.Events;
|
||||||
using SharpChat.EventStorage;
|
|
||||||
using SharpChat.Packet;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace SharpChat.PacketHandlers
|
namespace SharpChat.PacketHandlers {
|
||||||
{
|
|
||||||
public class SendMessageHandler : IPacketHandler {
|
public class SendMessageHandler : IPacketHandler {
|
||||||
private readonly CachedValue<int> MaxMessageLength;
|
private readonly CachedValue<int> MaxMessageLength;
|
||||||
|
|
||||||
|
@ -47,8 +43,10 @@ namespace SharpChat.PacketHandlers
|
||||||
|
|
||||||
ctx.Chat.ContextAccess.Wait();
|
ctx.Chat.ContextAccess.Wait();
|
||||||
try {
|
try {
|
||||||
if(!ctx.Chat.UserLastChannel.TryGetValue(user.UserId, out ChannelInfo? channel)
|
ChannelInfo? channelInfo = ctx.Chat.Channels.Get(
|
||||||
|| !ctx.Chat.IsInChannel(user, channel))
|
ctx.Chat.ChannelsUsers.GetUserLastChannel(user)
|
||||||
|
);
|
||||||
|
if(channelInfo == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(user.Status != UserStatus.Online)
|
if(user.Status != UserStatus.Online)
|
||||||
|
@ -65,7 +63,7 @@ namespace SharpChat.PacketHandlers
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(messageText.StartsWith("/")) {
|
if(messageText.StartsWith("/")) {
|
||||||
UserCommandContext context = new(messageText, ctx.Chat, user, ctx.Connection, channel);
|
UserCommandContext context = new(messageText, ctx.Chat, user, ctx.Connection, channelInfo);
|
||||||
|
|
||||||
IUserCommand? command = null;
|
IUserCommand? command = null;
|
||||||
|
|
||||||
|
@ -83,7 +81,7 @@ namespace SharpChat.PacketHandlers
|
||||||
|
|
||||||
ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
||||||
SharpId.Next(),
|
SharpId.Next(),
|
||||||
channel,
|
channelInfo,
|
||||||
user,
|
user,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
messageText,
|
messageText,
|
||||||
|
|
|
@ -36,8 +36,6 @@ namespace SharpChat {
|
||||||
|
|
||||||
private bool IsShuttingDown = false;
|
private bool IsShuttingDown = false;
|
||||||
|
|
||||||
private ChannelInfo DefaultChannel { get; set; }
|
|
||||||
|
|
||||||
public SockChatServer(HttpClient httpClient, MisuzuClient msz, IEventStorage evtStore, IConfig config) {
|
public SockChatServer(HttpClient httpClient, MisuzuClient msz, IEventStorage evtStore, IConfig config) {
|
||||||
Logger.Write("Initialising Sock Chat server...");
|
Logger.Write("Initialising Sock Chat server...");
|
||||||
|
|
||||||
|
@ -67,15 +65,13 @@ namespace SharpChat {
|
||||||
rank: channelCfg.SafeReadValue("minRank", 0)
|
rank: channelCfg.SafeReadValue("minRank", 0)
|
||||||
);
|
);
|
||||||
|
|
||||||
Context.Channels.Add(channelInfo.Name, channelInfo);
|
Context.Channels.Add(channelInfo);
|
||||||
DefaultChannel ??= channelInfo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultChannel ??= new ChannelInfo("Default");
|
if(Context.Channels.PublicCount < 1)
|
||||||
if(Context.Channels.Count < 1)
|
Context.Channels.Add(new ChannelInfo("Default"));
|
||||||
Context.Channels.Add(DefaultChannel.Name, DefaultChannel);
|
|
||||||
|
|
||||||
GuestHandlers.Add(new AuthHandler(Misuzu, DefaultChannel, MaxMessageLength, MaxConnections));
|
GuestHandlers.Add(new AuthHandler(Misuzu, Context.Channels.MainChannel, MaxMessageLength, MaxConnections));
|
||||||
|
|
||||||
AuthedHandlers.AddRange(new IPacketHandler[] {
|
AuthedHandlers.AddRange(new IPacketHandler[] {
|
||||||
new PingHandler(Misuzu),
|
new PingHandler(Misuzu),
|
||||||
|
|
65
SharpChat/SockChatUtility.cs
Normal file
65
SharpChat/SockChatUtility.cs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
using System;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace SharpChat {
|
||||||
|
public static class SockChatUtility {
|
||||||
|
private static readonly Regex ChannelName = new(@"[^A-Za-z0-9\-_]", RegexOptions.CultureInvariant | RegexOptions.Compiled);
|
||||||
|
|
||||||
|
public static string SanitiseMessageBody(string? body) {
|
||||||
|
if(string.IsNullOrEmpty(body))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
return body.Replace("<", "<").Replace(">", ">").Replace("\n", " <br/> ").Replace("\t", " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string SanitiseChannelName(string name) {
|
||||||
|
return ChannelName.Replace(name.Replace(" ", "_"), "-");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool CheckChannelName(string name) {
|
||||||
|
return name.Length < 1 || ChannelName.IsMatch(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetUserName(UserInfo info) {
|
||||||
|
return string.IsNullOrWhiteSpace(info.NickName) ? info.UserName : $"~{info.NickName}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetUserNameWithStatus(UserInfo info) {
|
||||||
|
string name = GetUserName(info);
|
||||||
|
|
||||||
|
if(info.Status == UserStatus.Away)
|
||||||
|
name = string.Format(
|
||||||
|
"<{0}>_{1}",
|
||||||
|
info.StatusText[..Math.Min(info.StatusText.Length, 5)].ToUpperInvariant(),
|
||||||
|
name
|
||||||
|
);
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static (string, UsersContext.NameTarget) ExplodeUserName(string name) {
|
||||||
|
UsersContext.NameTarget target = UsersContext.NameTarget.UserName;
|
||||||
|
|
||||||
|
if(name.StartsWith("<")) {
|
||||||
|
int gt = name.IndexOf(">_");
|
||||||
|
if(gt > 0) {
|
||||||
|
gt += 2;
|
||||||
|
name = name[gt..];
|
||||||
|
}
|
||||||
|
} else if(name.StartsWith("<")) {
|
||||||
|
int gt = name.IndexOf(">_");
|
||||||
|
if(gt > 0) {
|
||||||
|
gt += 5;
|
||||||
|
name = name[gt..];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(name.StartsWith("~")) {
|
||||||
|
target = UsersContext.NameTarget.NickName;
|
||||||
|
name = name[1..];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (name, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,4 @@
|
||||||
using System;
|
namespace SharpChat {
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace SharpChat {
|
|
||||||
public class UserInfo {
|
public class UserInfo {
|
||||||
public const int DEFAULT_SIZE = 30;
|
public const int DEFAULT_SIZE = 30;
|
||||||
public const int DEFAULT_MINIMUM_DELAY = 10000;
|
public const int DEFAULT_MINIMUM_DELAY = 10000;
|
||||||
|
@ -17,21 +14,6 @@ namespace SharpChat {
|
||||||
public UserStatus Status { get; set; }
|
public UserStatus Status { get; set; }
|
||||||
public string StatusText { get; set; }
|
public string StatusText { get; set; }
|
||||||
|
|
||||||
public string LegacyName => string.IsNullOrWhiteSpace(NickName) ? UserName : $"~{NickName}";
|
|
||||||
|
|
||||||
public string LegacyNameWithStatus {
|
|
||||||
get {
|
|
||||||
StringBuilder sb = new();
|
|
||||||
|
|
||||||
if(Status == UserStatus.Away)
|
|
||||||
sb.AppendFormat("<{0}>_", StatusText[..Math.Min(StatusText.Length, 5)].ToUpperInvariant());
|
|
||||||
|
|
||||||
sb.Append(LegacyName);
|
|
||||||
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserInfo(
|
public UserInfo(
|
||||||
long userId,
|
long userId,
|
||||||
string userName,
|
string userName,
|
||||||
|
@ -54,13 +36,6 @@ namespace SharpChat {
|
||||||
IsSuper = isSuper;
|
IsSuper = isSuper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool NameEquals(string? name) {
|
|
||||||
return string.Equals(name, UserName, StringComparison.InvariantCultureIgnoreCase)
|
|
||||||
|| string.Equals(name, NickName, StringComparison.InvariantCultureIgnoreCase)
|
|
||||||
|| string.Equals(name, LegacyName, StringComparison.InvariantCultureIgnoreCase)
|
|
||||||
|| string.Equals(name, LegacyNameWithStatus, StringComparison.InvariantCultureIgnoreCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetDMChannelName(UserInfo user1, UserInfo user2) {
|
public static string GetDMChannelName(UserInfo user1, UserInfo user2) {
|
||||||
return user1.UserId < user2.UserId
|
return user1.UserId < user2.UserId
|
||||||
? $"@{user1.UserId}-{user2.UserId}"
|
? $"@{user1.UserId}-{user2.UserId}"
|
||||||
|
|
112
SharpChat/UsersContext.cs
Normal file
112
SharpChat/UsersContext.cs
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace SharpChat {
|
||||||
|
public class UsersContext {
|
||||||
|
[Flags]
|
||||||
|
public enum NameTarget {
|
||||||
|
None = 0,
|
||||||
|
UserName = 1,
|
||||||
|
NickName = 2,
|
||||||
|
UserAndNickName = UserName | NickName,
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly List<UserInfo> Users = new();
|
||||||
|
|
||||||
|
public int TotalCount { get; private set; }
|
||||||
|
|
||||||
|
public UserInfo[] All => Users.ToArray();
|
||||||
|
|
||||||
|
public UserInfo? Get(
|
||||||
|
long? userId = null,
|
||||||
|
string? name = null,
|
||||||
|
NameTarget nameTarget = NameTarget.UserName
|
||||||
|
) {
|
||||||
|
foreach(UserInfo info in Users) {
|
||||||
|
if(userId != null && info.UserId != userId)
|
||||||
|
continue;
|
||||||
|
if(name != null) {
|
||||||
|
// this could probably all fit in a single if condition, but it'd be massive and disgusting
|
||||||
|
// and require more thinking power than my goldfish brain can muster
|
||||||
|
bool match = false;
|
||||||
|
if(nameTarget.HasFlag(NameTarget.UserName) && info.UserName.Equals(name, StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
match = true;
|
||||||
|
else if(nameTarget.HasFlag(NameTarget.NickName) && info.NickName.Equals(name, StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
match = true;
|
||||||
|
if(!match)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserInfo[] GetMany(
|
||||||
|
long[]? userIds = null,
|
||||||
|
string[]? names = null,
|
||||||
|
NameTarget namesTarget = NameTarget.UserName,
|
||||||
|
int? minRank = null
|
||||||
|
) {
|
||||||
|
List<UserInfo> users = new();
|
||||||
|
|
||||||
|
foreach(UserInfo info in Users) {
|
||||||
|
if(minRank != null && info.Rank < minRank)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(userIds != null && !userIds.Contains(info.UserId))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(names?.Length > 0) {
|
||||||
|
bool match = false;
|
||||||
|
foreach(string name in names) {
|
||||||
|
if(namesTarget.HasFlag(NameTarget.UserName) && info.UserName.Equals(name, StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
match = true;
|
||||||
|
else if(namesTarget.HasFlag(NameTarget.NickName) && info.NickName.Equals(name, StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
match = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!match)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
users.Add(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
return users.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(UserInfo info) {
|
||||||
|
if(Get(info.UserId, info.UserName) != null)
|
||||||
|
throw new ArgumentException("A user with that id and/or name has already been registred.", nameof(info));
|
||||||
|
|
||||||
|
Users.Add(info);
|
||||||
|
++TotalCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(UserInfo info) {
|
||||||
|
if(!Users.Contains(info)) {
|
||||||
|
Remove(info.UserId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Users.Remove(info);
|
||||||
|
--TotalCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(
|
||||||
|
long? userId = null,
|
||||||
|
string? name = null,
|
||||||
|
NameTarget nameTarget = NameTarget.UserName
|
||||||
|
) {
|
||||||
|
UserInfo? info = Get(userId, name, nameTarget);
|
||||||
|
if(info == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Users.Remove(info);
|
||||||
|
--TotalCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +0,0 @@
|
||||||
namespace SharpChat {
|
|
||||||
public static class Utility {
|
|
||||||
public static string Sanitise(string? body) {
|
|
||||||
if(string.IsNullOrEmpty(body))
|
|
||||||
return string.Empty;
|
|
||||||
|
|
||||||
return body.Replace("<", "<").Replace(">", ">").Replace("\n", " <br/> ").Replace("\t", " ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue