Name adjustments and moved some things to the common lib.
This commit is contained in:
parent
b8ec381f3b
commit
0cc5d46ea9
50 changed files with 323 additions and 323 deletions
SharpChat
C2SPacketHandler.csC2SPacketHandlerContext.cs
C2SPacketHandlers
Channel.csChatCommand.csClientCommand.csClientCommandContext.csClientCommands
AFKClientCommand.csActionClientCommand.csBanListClientCommand.csBroadcastClientCommand.csCreateChannelClientCommand.csDeleteChannelClientCommand.csDeleteMessageClientCommand.csJoinChannelClientCommand.csKickBanClientCommand.csNickClientCommand.csPardonAddressClientCommand.csPardonUserClientCommand.csPasswordChannelClientCommand.csRankChannelClientCommand.csRemoteAddressClientCommand.csShutdownRestartClientCommand.csWhisperClientCommand.csWhoClientCommand.cs
Connection.csContext.csEventStorage
Events
Misuzu
S2CPackets
AuthSuccessS2CPacket.csContextMessageS2CPacket.csContextUsersS2CPacket.csUserChannelJoinS2CPacket.csUserConnectS2CPacket.csUserUpdateS2CPacket.cs
SockChatServer.csUser.csUserPermissions.csUserStatus.csSharpChatCommon
|
@ -1,6 +1,6 @@
|
||||||
namespace SharpChat {
|
namespace SharpChat {
|
||||||
public interface C2SPacketHandler {
|
public interface C2SPacketHandler {
|
||||||
bool IsMatch(ChatPacketHandlerContext ctx);
|
bool IsMatch(C2SPacketHandlerContext ctx);
|
||||||
void Handle(ChatPacketHandlerContext ctx);
|
void Handle(C2SPacketHandlerContext ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
namespace SharpChat {
|
namespace SharpChat {
|
||||||
public class ChatPacketHandlerContext(
|
public class C2SPacketHandlerContext(
|
||||||
string text,
|
string text,
|
||||||
ChatContext chat,
|
Context chat,
|
||||||
ChatConnection connection
|
Connection connection
|
||||||
) {
|
) {
|
||||||
public string Text { get; } = text ?? throw new ArgumentNullException(nameof(text));
|
public string Text { get; } = text ?? throw new ArgumentNullException(nameof(text));
|
||||||
public ChatContext Chat { get; } = chat ?? throw new ArgumentNullException(nameof(chat));
|
public Context Chat { get; } = chat ?? throw new ArgumentNullException(nameof(chat));
|
||||||
public ChatConnection Connection { get; } = connection ?? throw new ArgumentNullException(nameof(connection));
|
public Connection Connection { get; } = connection ?? throw new ArgumentNullException(nameof(connection));
|
||||||
|
|
||||||
public bool CheckPacketId(string packetId) {
|
public bool CheckPacketId(string packetId) {
|
||||||
return Text == packetId || Text.StartsWith(packetId + '\t');
|
return Text == packetId || Text.StartsWith(packetId + '\t');
|
|
@ -5,20 +5,20 @@ using SharpChat.S2CPackets;
|
||||||
namespace SharpChat.C2SPacketHandlers {
|
namespace SharpChat.C2SPacketHandlers {
|
||||||
public class AuthC2SPacketHandler(
|
public class AuthC2SPacketHandler(
|
||||||
MisuzuClient msz,
|
MisuzuClient msz,
|
||||||
ChatChannel defaultChannel,
|
Channel defaultChannel,
|
||||||
CachedValue<int> maxMsgLength,
|
CachedValue<int> maxMsgLength,
|
||||||
CachedValue<int> maxConns
|
CachedValue<int> maxConns
|
||||||
) : C2SPacketHandler {
|
) : C2SPacketHandler {
|
||||||
private readonly MisuzuClient Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
|
private readonly MisuzuClient Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
|
||||||
private readonly ChatChannel DefaultChannel = defaultChannel ?? throw new ArgumentNullException(nameof(defaultChannel));
|
private readonly Channel DefaultChannel = defaultChannel ?? throw new ArgumentNullException(nameof(defaultChannel));
|
||||||
private readonly CachedValue<int> MaxMessageLength = maxMsgLength ?? throw new ArgumentNullException(nameof(maxMsgLength));
|
private readonly CachedValue<int> MaxMessageLength = maxMsgLength ?? throw new ArgumentNullException(nameof(maxMsgLength));
|
||||||
private readonly CachedValue<int> MaxConnections = maxConns ?? throw new ArgumentNullException(nameof(maxConns));
|
private readonly CachedValue<int> MaxConnections = maxConns ?? throw new ArgumentNullException(nameof(maxConns));
|
||||||
|
|
||||||
public bool IsMatch(ChatPacketHandlerContext ctx) {
|
public bool IsMatch(C2SPacketHandlerContext ctx) {
|
||||||
return ctx.CheckPacketId("1");
|
return ctx.CheckPacketId("1");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Handle(ChatPacketHandlerContext ctx) {
|
public void Handle(C2SPacketHandlerContext ctx) {
|
||||||
string[] args = ctx.SplitText(3);
|
string[] args = ctx.SplitText(3);
|
||||||
|
|
||||||
string? authMethod = args.ElementAtOrDefault(1);
|
string? authMethod = args.ElementAtOrDefault(1);
|
||||||
|
@ -88,10 +88,10 @@ namespace SharpChat.C2SPacketHandlers {
|
||||||
|
|
||||||
await ctx.Chat.ContextAccess.WaitAsync();
|
await ctx.Chat.ContextAccess.WaitAsync();
|
||||||
try {
|
try {
|
||||||
ChatUser? user = ctx.Chat.Users.FirstOrDefault(u => u.UserId == fai.UserId);
|
User? user = ctx.Chat.Users.FirstOrDefault(u => u.UserId == fai.UserId);
|
||||||
|
|
||||||
if(user == null)
|
if(user == null)
|
||||||
user = new ChatUser(
|
user = new User(
|
||||||
fai.UserId,
|
fai.UserId,
|
||||||
fai.UserName ?? $"({fai.UserId})",
|
fai.UserName ?? $"({fai.UserId})",
|
||||||
fai.Colour,
|
fai.Colour,
|
||||||
|
|
|
@ -8,11 +8,11 @@ namespace SharpChat.C2SPacketHandlers {
|
||||||
private readonly TimeSpan BumpInterval = TimeSpan.FromMinutes(1);
|
private readonly TimeSpan BumpInterval = TimeSpan.FromMinutes(1);
|
||||||
private DateTimeOffset LastBump = DateTimeOffset.MinValue;
|
private DateTimeOffset LastBump = DateTimeOffset.MinValue;
|
||||||
|
|
||||||
public bool IsMatch(ChatPacketHandlerContext ctx) {
|
public bool IsMatch(C2SPacketHandlerContext ctx) {
|
||||||
return ctx.CheckPacketId("0");
|
return ctx.CheckPacketId("0");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Handle(ChatPacketHandlerContext ctx) {
|
public void Handle(C2SPacketHandlerContext ctx) {
|
||||||
string[] parts = ctx.SplitText(2);
|
string[] parts = ctx.SplitText(2);
|
||||||
|
|
||||||
if(!int.TryParse(parts.FirstOrDefault(), out int pTime))
|
if(!int.TryParse(parts.FirstOrDefault(), out int pTime))
|
||||||
|
@ -25,7 +25,7 @@ namespace SharpChat.C2SPacketHandlers {
|
||||||
try {
|
try {
|
||||||
if(LastBump < DateTimeOffset.UtcNow - BumpInterval) {
|
if(LastBump < DateTimeOffset.UtcNow - BumpInterval) {
|
||||||
(string, string)[] bumpList = [.. ctx.Chat.Users
|
(string, string)[] bumpList = [.. ctx.Chat.Users
|
||||||
.Where(u => u.Status == ChatUserStatus.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))];
|
||||||
|
|
||||||
if(bumpList.Length > 0)
|
if(bumpList.Length > 0)
|
||||||
|
|
|
@ -11,27 +11,27 @@ namespace SharpChat.C2SPacketHandlers {
|
||||||
) : C2SPacketHandler {
|
) : C2SPacketHandler {
|
||||||
private readonly CachedValue<int> MaxMessageLength = maxMsgLength ?? throw new ArgumentNullException(nameof(maxMsgLength));
|
private readonly CachedValue<int> MaxMessageLength = maxMsgLength ?? throw new ArgumentNullException(nameof(maxMsgLength));
|
||||||
|
|
||||||
private List<ChatCommand> Commands { get; } = [];
|
private List<ClientCommand> Commands { get; } = [];
|
||||||
|
|
||||||
public void AddCommand(ChatCommand command) {
|
public void AddCommand(ClientCommand command) {
|
||||||
Commands.Add(command ?? throw new ArgumentNullException(nameof(command)));
|
Commands.Add(command ?? throw new ArgumentNullException(nameof(command)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddCommands(IEnumerable<ChatCommand> commands) {
|
public void AddCommands(IEnumerable<ClientCommand> commands) {
|
||||||
Commands.AddRange(commands ?? throw new ArgumentNullException(nameof(commands)));
|
Commands.AddRange(commands ?? throw new ArgumentNullException(nameof(commands)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsMatch(ChatPacketHandlerContext ctx) {
|
public bool IsMatch(C2SPacketHandlerContext ctx) {
|
||||||
return ctx.CheckPacketId("2");
|
return ctx.CheckPacketId("2");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Handle(ChatPacketHandlerContext ctx) {
|
public void Handle(C2SPacketHandlerContext ctx) {
|
||||||
string[] args = ctx.SplitText(3);
|
string[] args = ctx.SplitText(3);
|
||||||
|
|
||||||
ChatUser? user = ctx.Connection.User;
|
User? user = ctx.Connection.User;
|
||||||
string? messageText = args.ElementAtOrDefault(2);
|
string? messageText = args.ElementAtOrDefault(2);
|
||||||
|
|
||||||
if(user == null || !user.Can(ChatUserPermissions.SendMessage) || string.IsNullOrWhiteSpace(messageText))
|
if(user == null || !user.Can(UserPermissions.SendMessage) || string.IsNullOrWhiteSpace(messageText))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Extra validation step, not necessary at all but enforces proper formatting in SCv1.
|
// Extra validation step, not necessary at all but enforces proper formatting in SCv1.
|
||||||
|
@ -40,12 +40,12 @@ namespace SharpChat.C2SPacketHandlers {
|
||||||
|
|
||||||
ctx.Chat.ContextAccess.Wait();
|
ctx.Chat.ContextAccess.Wait();
|
||||||
try {
|
try {
|
||||||
if(!ctx.Chat.UserLastChannel.TryGetValue(user.UserId, out ChatChannel? channel)
|
if(!ctx.Chat.UserLastChannel.TryGetValue(user.UserId, out Channel? channel)
|
||||||
&& (channel is null || !ctx.Chat.IsInChannel(user, channel)))
|
&& (channel is null || !ctx.Chat.IsInChannel(user, channel)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(user.Status != ChatUserStatus.Online)
|
if(user.Status != UserStatus.Online)
|
||||||
ctx.Chat.UpdateUser(user, status: ChatUserStatus.Online);
|
ctx.Chat.UpdateUser(user, status: UserStatus.Online);
|
||||||
|
|
||||||
int maxMsgLength = MaxMessageLength;
|
int maxMsgLength = MaxMessageLength;
|
||||||
StringInfo messageTextInfo = new(messageText);
|
StringInfo messageTextInfo = new(messageText);
|
||||||
|
@ -60,8 +60,8 @@ namespace SharpChat.C2SPacketHandlers {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(messageText.StartsWith('/')) {
|
if(messageText.StartsWith('/')) {
|
||||||
ChatCommandContext context = new(messageText, ctx.Chat, user, ctx.Connection, channel);
|
ClientCommandContext context = new(messageText, ctx.Chat, user, ctx.Connection, channel);
|
||||||
foreach(ChatCommand cmd in Commands)
|
foreach(ClientCommand cmd in Commands)
|
||||||
if(cmd.IsMatch(context)) {
|
if(cmd.IsMatch(context)) {
|
||||||
cmd.Dispatch(context);
|
cmd.Dispatch(context);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
namespace SharpChat {
|
namespace SharpChat {
|
||||||
public class ChatChannel(
|
public class Channel(
|
||||||
string name,
|
string name,
|
||||||
string password = "",
|
string password = "",
|
||||||
bool isTemporary = false,
|
bool isTemporary = false,
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
return string.Equals(name, Name, StringComparison.InvariantCultureIgnoreCase);
|
return string.Equals(name, Name, StringComparison.InvariantCultureIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsOwner(ChatUser user) {
|
public bool IsOwner(User user) {
|
||||||
return OwnerId > 0
|
return OwnerId > 0
|
||||||
&& user != null
|
&& user != null
|
||||||
&& OwnerId == user.UserId;
|
&& OwnerId == user.UserId;
|
|
@ -1,6 +0,0 @@
|
||||||
namespace SharpChat {
|
|
||||||
public interface ChatCommand {
|
|
||||||
bool IsMatch(ChatCommandContext ctx);
|
|
||||||
void Dispatch(ChatCommandContext ctx);
|
|
||||||
}
|
|
||||||
}
|
|
6
SharpChat/ClientCommand.cs
Normal file
6
SharpChat/ClientCommand.cs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
namespace SharpChat {
|
||||||
|
public interface ClientCommand {
|
||||||
|
bool IsMatch(ClientCommandContext ctx);
|
||||||
|
void Dispatch(ClientCommandContext ctx);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +1,18 @@
|
||||||
namespace SharpChat {
|
namespace SharpChat {
|
||||||
public class ChatCommandContext {
|
public class ClientCommandContext {
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
public string[] Args { get; }
|
public string[] Args { get; }
|
||||||
public ChatContext Chat { get; }
|
public Context Chat { get; }
|
||||||
public ChatUser User { get; }
|
public User User { get; }
|
||||||
public ChatConnection Connection { get; }
|
public Connection Connection { get; }
|
||||||
public ChatChannel Channel { get; }
|
public Channel Channel { get; }
|
||||||
|
|
||||||
public ChatCommandContext(
|
public ClientCommandContext(
|
||||||
string text,
|
string text,
|
||||||
ChatContext chat,
|
Context chat,
|
||||||
ChatUser user,
|
User user,
|
||||||
ChatConnection connection,
|
Connection connection,
|
||||||
ChatChannel channel
|
Channel channel
|
||||||
) {
|
) {
|
||||||
ArgumentNullException.ThrowIfNull(text);
|
ArgumentNullException.ThrowIfNull(text);
|
||||||
|
|
||||||
|
@ -26,13 +26,13 @@
|
||||||
Args = [.. parts.Skip(1)];
|
Args = [.. parts.Skip(1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChatCommandContext(
|
public ClientCommandContext(
|
||||||
string name,
|
string name,
|
||||||
string[] args,
|
string[] args,
|
||||||
ChatContext chat,
|
Context chat,
|
||||||
ChatUser user,
|
User user,
|
||||||
ChatConnection connection,
|
Connection connection,
|
||||||
ChatChannel channel
|
Channel channel
|
||||||
) {
|
) {
|
||||||
Name = name ?? throw new ArgumentNullException(nameof(name));
|
Name = name ?? throw new ArgumentNullException(nameof(name));
|
||||||
Args = args ?? throw new ArgumentNullException(nameof(args));
|
Args = args ?? throw new ArgumentNullException(nameof(args));
|
|
@ -1,17 +1,17 @@
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
namespace SharpChat.ClientCommands {
|
||||||
public class AFKCommand : ChatCommand {
|
public class AFKClientCommand : ClientCommand {
|
||||||
private const string DEFAULT = "AFK";
|
private const string DEFAULT = "AFK";
|
||||||
public const int MAX_GRAPHEMES = 5;
|
public const int MAX_GRAPHEMES = 5;
|
||||||
public const int MAX_BYTES = MAX_GRAPHEMES * 10;
|
public const int MAX_BYTES = MAX_GRAPHEMES * 10;
|
||||||
|
|
||||||
public bool IsMatch(ChatCommandContext ctx) {
|
public bool IsMatch(ClientCommandContext ctx) {
|
||||||
return ctx.NameEquals("afk");
|
return ctx.NameEquals("afk");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispatch(ChatCommandContext ctx) {
|
public void Dispatch(ClientCommandContext ctx) {
|
||||||
string? statusText = ctx.Args.FirstOrDefault();
|
string? statusText = ctx.Args.FirstOrDefault();
|
||||||
if(string.IsNullOrWhiteSpace(statusText))
|
if(string.IsNullOrWhiteSpace(statusText))
|
||||||
statusText = DEFAULT;
|
statusText = DEFAULT;
|
||||||
|
@ -26,7 +26,7 @@ namespace SharpChat.Commands {
|
||||||
|
|
||||||
ctx.Chat.UpdateUser(
|
ctx.Chat.UpdateUser(
|
||||||
ctx.User,
|
ctx.User,
|
||||||
status: ChatUserStatus.Away,
|
status: UserStatus.Away,
|
||||||
statusText: statusText
|
statusText: statusText
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -1,13 +1,13 @@
|
||||||
using SharpChat.Events;
|
using SharpChat.Events;
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
namespace SharpChat.ClientCommands {
|
||||||
public class ActionCommand : ChatCommand {
|
public class ActionClientCommand : ClientCommand {
|
||||||
public bool IsMatch(ChatCommandContext ctx) {
|
public bool IsMatch(ClientCommandContext ctx) {
|
||||||
return ctx.NameEquals("action")
|
return ctx.NameEquals("action")
|
||||||
|| ctx.NameEquals("me");
|
|| ctx.NameEquals("me");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispatch(ChatCommandContext ctx) {
|
public void Dispatch(ClientCommandContext ctx) {
|
||||||
if(ctx.Args.Length < 1)
|
if(ctx.Args.Length < 1)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
using SharpChat.Misuzu;
|
using SharpChat.Misuzu;
|
||||||
using SharpChat.S2CPackets;
|
using SharpChat.S2CPackets;
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
namespace SharpChat.ClientCommands {
|
||||||
public class BanListCommand(MisuzuClient msz) : ChatCommand {
|
public class BanListClientCommand(MisuzuClient msz) : ClientCommand {
|
||||||
private readonly MisuzuClient Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
|
private readonly MisuzuClient Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
|
||||||
|
|
||||||
public bool IsMatch(ChatCommandContext ctx) {
|
public bool IsMatch(ClientCommandContext ctx) {
|
||||||
return ctx.NameEquals("bans")
|
return ctx.NameEquals("bans")
|
||||||
|| ctx.NameEquals("banned");
|
|| ctx.NameEquals("banned");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispatch(ChatCommandContext ctx) {
|
public void Dispatch(ClientCommandContext ctx) {
|
||||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||||
|
|
||||||
if(!ctx.User.Can(ChatUserPermissions.BanUser | ChatUserPermissions.KickUser)) {
|
if(!ctx.User.Can(UserPermissions.BanUser | UserPermissions.KickUser)) {
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
|
@ -1,17 +1,17 @@
|
||||||
using SharpChat.Events;
|
using SharpChat.Events;
|
||||||
using SharpChat.S2CPackets;
|
using SharpChat.S2CPackets;
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
namespace SharpChat.ClientCommands {
|
||||||
public class BroadcastCommand : ChatCommand {
|
public class BroadcastClientCommand : ClientCommand {
|
||||||
public bool IsMatch(ChatCommandContext ctx) {
|
public bool IsMatch(ClientCommandContext ctx) {
|
||||||
return ctx.NameEquals("say")
|
return ctx.NameEquals("say")
|
||||||
|| ctx.NameEquals("broadcast");
|
|| ctx.NameEquals("broadcast");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispatch(ChatCommandContext ctx) {
|
public void Dispatch(ClientCommandContext ctx) {
|
||||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||||
|
|
||||||
if(!ctx.User.Can(ChatUserPermissions.Broadcast)) {
|
if(!ctx.User.Can(UserPermissions.Broadcast)) {
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
|
@ -1,15 +1,15 @@
|
||||||
using SharpChat.S2CPackets;
|
using SharpChat.S2CPackets;
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
namespace SharpChat.ClientCommands {
|
||||||
public class CreateChannelCommand : ChatCommand {
|
public class CreateChannelClientCommand : ClientCommand {
|
||||||
public bool IsMatch(ChatCommandContext ctx) {
|
public bool IsMatch(ClientCommandContext ctx) {
|
||||||
return ctx.NameEquals("create");
|
return ctx.NameEquals("create");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispatch(ChatCommandContext ctx) {
|
public void Dispatch(ClientCommandContext ctx) {
|
||||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||||
|
|
||||||
if(!ctx.User.Can(ChatUserPermissions.CreateChannel)) {
|
if(!ctx.User.Can(UserPermissions.CreateChannel)) {
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ namespace SharpChat.Commands {
|
||||||
|
|
||||||
string createChanName = string.Join('_', ctx.Args.Skip(createChanHasHierarchy ? 1 : 0));
|
string createChanName = string.Join('_', ctx.Args.Skip(createChanHasHierarchy ? 1 : 0));
|
||||||
|
|
||||||
if(!ChatChannel.CheckName(createChanName)) {
|
if(!Channel.CheckName(createChanName)) {
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NAME_INVALID));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NAME_INVALID));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -44,15 +44,15 @@ namespace SharpChat.Commands {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatChannel createChan = new(
|
Channel createChan = new(
|
||||||
createChanName,
|
createChanName,
|
||||||
isTemporary: !ctx.User.Can(ChatUserPermissions.SetChannelPermanent),
|
isTemporary: !ctx.User.Can(UserPermissions.SetChannelPermanent),
|
||||||
rank: createChanHierarchy,
|
rank: createChanHierarchy,
|
||||||
ownerId: ctx.User.UserId
|
ownerId: ctx.User.UserId
|
||||||
);
|
);
|
||||||
|
|
||||||
ctx.Chat.Channels.Add(createChan);
|
ctx.Chat.Channels.Add(createChan);
|
||||||
foreach(ChatUser ccu in ctx.Chat.Users.Where(u => u.Rank >= ctx.Channel.Rank))
|
foreach(User ccu in ctx.Chat.Users.Where(u => u.Rank >= ctx.Channel.Rank))
|
||||||
ctx.Chat.SendTo(ccu, new ChannelCreateS2CPacket(createChan.Name, createChan.HasPassword, createChan.IsTemporary));
|
ctx.Chat.SendTo(ccu, new ChannelCreateS2CPacket(createChan.Name, createChan.HasPassword, createChan.IsTemporary));
|
||||||
|
|
||||||
ctx.Chat.SwitchChannel(ctx.User, createChan, createChan.Password);
|
ctx.Chat.SwitchChannel(ctx.User, createChan, createChan.Password);
|
|
@ -1,15 +1,15 @@
|
||||||
using SharpChat.S2CPackets;
|
using SharpChat.S2CPackets;
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
namespace SharpChat.ClientCommands {
|
||||||
public class DeleteChannelCommand : ChatCommand {
|
public class DeleteChannelClientCommand : ClientCommand {
|
||||||
public bool IsMatch(ChatCommandContext ctx) {
|
public bool IsMatch(ClientCommandContext ctx) {
|
||||||
return ctx.NameEquals("delchan") || (
|
return ctx.NameEquals("delchan") || (
|
||||||
ctx.NameEquals("delete")
|
ctx.NameEquals("delete")
|
||||||
&& ctx.Args.FirstOrDefault()?.All(char.IsDigit) == false
|
&& ctx.Args.FirstOrDefault()?.All(char.IsDigit) == false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispatch(ChatCommandContext ctx) {
|
public void Dispatch(ClientCommandContext ctx) {
|
||||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||||
|
|
||||||
if(ctx.Args.Length < 1 || string.IsNullOrWhiteSpace(ctx.Args.FirstOrDefault())) {
|
if(ctx.Args.Length < 1 || string.IsNullOrWhiteSpace(ctx.Args.FirstOrDefault())) {
|
||||||
|
@ -18,14 +18,14 @@ namespace SharpChat.Commands {
|
||||||
}
|
}
|
||||||
|
|
||||||
string delChanName = string.Join('_', ctx.Args);
|
string delChanName = string.Join('_', ctx.Args);
|
||||||
ChatChannel? delChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(delChanName));
|
Channel? delChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(delChanName));
|
||||||
|
|
||||||
if(delChan == null) {
|
if(delChan == null) {
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NOT_FOUND, true, delChanName));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NOT_FOUND, true, delChanName));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!ctx.User.Can(ChatUserPermissions.DeleteChannel) && delChan.IsOwner(ctx.User)) {
|
if(!ctx.User.Can(UserPermissions.DeleteChannel) && delChan.IsOwner(ctx.User)) {
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_DELETE_FAILED, true, delChan.Name));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_DELETE_FAILED, true, delChan.Name));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
|
@ -1,21 +1,21 @@
|
||||||
using SharpChat.EventStorage;
|
using SharpChat.EventStorage;
|
||||||
using SharpChat.S2CPackets;
|
using SharpChat.S2CPackets;
|
||||||
|
|
||||||
namespace SharpChat.Commands
|
namespace SharpChat.ClientCommands
|
||||||
{
|
{
|
||||||
public class DeleteMessageCommand : ChatCommand {
|
public class DeleteMessageClientCommand : ClientCommand {
|
||||||
public bool IsMatch(ChatCommandContext ctx) {
|
public bool IsMatch(ClientCommandContext ctx) {
|
||||||
return ctx.NameEquals("delmsg") || (
|
return ctx.NameEquals("delmsg") || (
|
||||||
ctx.NameEquals("delete")
|
ctx.NameEquals("delete")
|
||||||
&& ctx.Args.FirstOrDefault()?.All(char.IsDigit) == true
|
&& ctx.Args.FirstOrDefault()?.All(char.IsDigit) == true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispatch(ChatCommandContext ctx) {
|
public void Dispatch(ClientCommandContext ctx) {
|
||||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||||
bool deleteAnyMessage = ctx.User.Can(ChatUserPermissions.DeleteAnyMessage);
|
bool deleteAnyMessage = ctx.User.Can(UserPermissions.DeleteAnyMessage);
|
||||||
|
|
||||||
if(!deleteAnyMessage && !ctx.User.Can(ChatUserPermissions.DeleteOwnMessage)) {
|
if(!deleteAnyMessage && !ctx.User.Can(UserPermissions.DeleteOwnMessage)) {
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
|
@ -1,15 +1,15 @@
|
||||||
using SharpChat.S2CPackets;
|
using SharpChat.S2CPackets;
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
namespace SharpChat.ClientCommands {
|
||||||
public class JoinChannelCommand : ChatCommand {
|
public class JoinChannelClientCommand : ClientCommand {
|
||||||
public bool IsMatch(ChatCommandContext ctx) {
|
public bool IsMatch(ClientCommandContext ctx) {
|
||||||
return ctx.NameEquals("join");
|
return ctx.NameEquals("join");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispatch(ChatCommandContext ctx) {
|
public void Dispatch(ClientCommandContext ctx) {
|
||||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||||
string joinChanStr = ctx.Args.FirstOrDefault() ?? "Channel";
|
string joinChanStr = ctx.Args.FirstOrDefault() ?? "Channel";
|
||||||
ChatChannel? joinChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(joinChanStr));
|
Channel? joinChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(joinChanStr));
|
||||||
|
|
||||||
if(joinChan is null) {
|
if(joinChan is null) {
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NOT_FOUND, true, joinChanStr));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NOT_FOUND, true, joinChanStr));
|
|
@ -1,20 +1,20 @@
|
||||||
using SharpChat.Misuzu;
|
using SharpChat.Misuzu;
|
||||||
using SharpChat.S2CPackets;
|
using SharpChat.S2CPackets;
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
namespace SharpChat.ClientCommands {
|
||||||
public class KickBanCommand(MisuzuClient msz) : ChatCommand {
|
public class KickBanClientCommand(MisuzuClient msz) : ClientCommand {
|
||||||
private readonly MisuzuClient Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
|
private readonly MisuzuClient Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
|
||||||
|
|
||||||
public bool IsMatch(ChatCommandContext ctx) {
|
public bool IsMatch(ClientCommandContext ctx) {
|
||||||
return ctx.NameEquals("kick")
|
return ctx.NameEquals("kick")
|
||||||
|| ctx.NameEquals("ban");
|
|| ctx.NameEquals("ban");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispatch(ChatCommandContext ctx) {
|
public void Dispatch(ClientCommandContext ctx) {
|
||||||
bool isBanning = ctx.NameEquals("ban");
|
bool isBanning = ctx.NameEquals("ban");
|
||||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||||
|
|
||||||
if(!ctx.User.Can(isBanning ? ChatUserPermissions.BanUser : ChatUserPermissions.KickUser)) {
|
if(!ctx.User.Can(isBanning ? UserPermissions.BanUser : UserPermissions.KickUser)) {
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ namespace SharpChat.Commands {
|
||||||
string? banUserTarget = ctx.Args.ElementAtOrDefault(0);
|
string? banUserTarget = ctx.Args.ElementAtOrDefault(0);
|
||||||
string? banDurationStr = ctx.Args.ElementAtOrDefault(1);
|
string? banDurationStr = ctx.Args.ElementAtOrDefault(1);
|
||||||
int banReasonIndex = 1;
|
int banReasonIndex = 1;
|
||||||
ChatUser? banUser = null;
|
User? banUser = null;
|
||||||
|
|
||||||
if(banUserTarget == null || (banUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(banUserTarget))) == null) {
|
if(banUserTarget == null || (banUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(banUserTarget))) == null) {
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_FOUND, true, banUserTarget ?? "User"));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_FOUND, true, banUserTarget ?? "User"));
|
|
@ -2,25 +2,25 @@
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
namespace SharpChat.ClientCommands {
|
||||||
public class NickCommand : ChatCommand {
|
public class NickClientCommand : ClientCommand {
|
||||||
private const int MAX_GRAPHEMES = 16;
|
private const int MAX_GRAPHEMES = 16;
|
||||||
private const int MAX_BYTES = MAX_GRAPHEMES * 10;
|
private const int MAX_BYTES = MAX_GRAPHEMES * 10;
|
||||||
|
|
||||||
public bool IsMatch(ChatCommandContext ctx) {
|
public bool IsMatch(ClientCommandContext ctx) {
|
||||||
return ctx.NameEquals("nick");
|
return ctx.NameEquals("nick");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispatch(ChatCommandContext ctx) {
|
public void Dispatch(ClientCommandContext ctx) {
|
||||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||||
bool setOthersNick = ctx.User.Can(ChatUserPermissions.SetOthersNickname);
|
bool setOthersNick = ctx.User.Can(UserPermissions.SetOthersNickname);
|
||||||
|
|
||||||
if(!setOthersNick && !ctx.User.Can(ChatUserPermissions.SetOwnNickname)) {
|
if(!setOthersNick && !ctx.User.Can(UserPermissions.SetOwnNickname)) {
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatUser? targetUser = null;
|
User? targetUser = null;
|
||||||
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) {
|
|
@ -2,19 +2,19 @@
|
||||||
using SharpChat.S2CPackets;
|
using SharpChat.S2CPackets;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
namespace SharpChat.ClientCommands {
|
||||||
public class PardonAddressCommand(MisuzuClient msz) : ChatCommand {
|
public class PardonAddressClientCommand(MisuzuClient msz) : ClientCommand {
|
||||||
private readonly MisuzuClient Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
|
private readonly MisuzuClient Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
|
||||||
|
|
||||||
public bool IsMatch(ChatCommandContext ctx) {
|
public bool IsMatch(ClientCommandContext ctx) {
|
||||||
return ctx.NameEquals("pardonip")
|
return ctx.NameEquals("pardonip")
|
||||||
|| ctx.NameEquals("unbanip");
|
|| ctx.NameEquals("unbanip");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispatch(ChatCommandContext ctx) {
|
public void Dispatch(ClientCommandContext ctx) {
|
||||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||||
|
|
||||||
if(!ctx.User.Can(ChatUserPermissions.BanUser | ChatUserPermissions.KickUser)) {
|
if(!ctx.User.Can(UserPermissions.BanUser | UserPermissions.KickUser)) {
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
|
@ -1,19 +1,19 @@
|
||||||
using SharpChat.Misuzu;
|
using SharpChat.Misuzu;
|
||||||
using SharpChat.S2CPackets;
|
using SharpChat.S2CPackets;
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
namespace SharpChat.ClientCommands {
|
||||||
public class PardonUserCommand(MisuzuClient msz) : ChatCommand {
|
public class PardonUserClientCommand(MisuzuClient msz) : ClientCommand {
|
||||||
private readonly MisuzuClient Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
|
private readonly MisuzuClient Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
|
||||||
|
|
||||||
public bool IsMatch(ChatCommandContext ctx) {
|
public bool IsMatch(ClientCommandContext ctx) {
|
||||||
return ctx.NameEquals("pardon")
|
return ctx.NameEquals("pardon")
|
||||||
|| ctx.NameEquals("unban");
|
|| ctx.NameEquals("unban");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispatch(ChatCommandContext ctx) {
|
public void Dispatch(ClientCommandContext ctx) {
|
||||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||||
|
|
||||||
if(!ctx.User.Can(ChatUserPermissions.BanUser | ChatUserPermissions.KickUser)) {
|
if(!ctx.User.Can(UserPermissions.BanUser | UserPermissions.KickUser)) {
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ namespace SharpChat.Commands {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatUser? unbanUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(unbanUserTarget));
|
User? unbanUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(unbanUserTarget));
|
||||||
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.FirstOrDefault(u => u.UserId == unbanUserId);
|
unbanUser = ctx.Chat.Users.FirstOrDefault(u => u.UserId == unbanUserId);
|
|
@ -1,16 +1,16 @@
|
||||||
using SharpChat.S2CPackets;
|
using SharpChat.S2CPackets;
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
namespace SharpChat.ClientCommands {
|
||||||
public class PasswordChannelCommand : ChatCommand {
|
public class PasswordChannelClientCommand : ClientCommand {
|
||||||
public bool IsMatch(ChatCommandContext ctx) {
|
public bool IsMatch(ClientCommandContext ctx) {
|
||||||
return ctx.NameEquals("pwd")
|
return ctx.NameEquals("pwd")
|
||||||
|| ctx.NameEquals("password");
|
|| ctx.NameEquals("password");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispatch(ChatCommandContext ctx) {
|
public void Dispatch(ClientCommandContext ctx) {
|
||||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||||
|
|
||||||
if(!ctx.User.Can(ChatUserPermissions.SetChannelPassword) || ctx.Channel.IsOwner(ctx.User)) {
|
if(!ctx.User.Can(UserPermissions.SetChannelPassword) || ctx.Channel.IsOwner(ctx.User)) {
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
|
@ -1,17 +1,17 @@
|
||||||
using SharpChat.S2CPackets;
|
using SharpChat.S2CPackets;
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
namespace SharpChat.ClientCommands {
|
||||||
public class RankChannelCommand : ChatCommand {
|
public class RankChannelClientCommand : ClientCommand {
|
||||||
public bool IsMatch(ChatCommandContext ctx) {
|
public bool IsMatch(ClientCommandContext ctx) {
|
||||||
return ctx.NameEquals("rank")
|
return ctx.NameEquals("rank")
|
||||||
|| ctx.NameEquals("privilege")
|
|| ctx.NameEquals("privilege")
|
||||||
|| ctx.NameEquals("priv");
|
|| ctx.NameEquals("priv");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispatch(ChatCommandContext ctx) {
|
public void Dispatch(ClientCommandContext ctx) {
|
||||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||||
|
|
||||||
if(!ctx.User.Can(ChatUserPermissions.SetChannelHierarchy) || ctx.Channel.IsOwner(ctx.User)) {
|
if(!ctx.User.Can(UserPermissions.SetChannelHierarchy) || ctx.Channel.IsOwner(ctx.User)) {
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
|
@ -1,23 +1,23 @@
|
||||||
using SharpChat.S2CPackets;
|
using SharpChat.S2CPackets;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
namespace SharpChat.ClientCommands {
|
||||||
public class RemoteAddressCommand : ChatCommand {
|
public class RemoteAddressClientCommand : ClientCommand {
|
||||||
public bool IsMatch(ChatCommandContext ctx) {
|
public bool IsMatch(ClientCommandContext ctx) {
|
||||||
return ctx.NameEquals("ip")
|
return ctx.NameEquals("ip")
|
||||||
|| ctx.NameEquals("whois");
|
|| ctx.NameEquals("whois");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispatch(ChatCommandContext ctx) {
|
public void Dispatch(ClientCommandContext ctx) {
|
||||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||||
|
|
||||||
if(!ctx.User.Can(ChatUserPermissions.SeeIPAddress)) {
|
if(!ctx.User.Can(UserPermissions.SeeIPAddress)) {
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, "/ip"));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, "/ip"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string? ipUserStr = ctx.Args.FirstOrDefault();
|
string? ipUserStr = ctx.Args.FirstOrDefault();
|
||||||
ChatUser? ipUser = null;
|
User? ipUser = null;
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(ipUserStr) || (ipUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(ipUserStr))) == null) {
|
if(string.IsNullOrWhiteSpace(ipUserStr) || (ipUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(ipUserStr))) == null) {
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_FOUND, true, ipUserStr ?? "User"));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_FOUND, true, ipUserStr ?? "User"));
|
|
@ -1,16 +1,16 @@
|
||||||
using SharpChat.S2CPackets;
|
using SharpChat.S2CPackets;
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
namespace SharpChat.ClientCommands {
|
||||||
public class ShutdownRestartCommand(ManualResetEvent waitHandle, Func<bool> shutdownCheck) : ChatCommand {
|
public class ShutdownRestartClientCommand(ManualResetEvent waitHandle, Func<bool> shutdownCheck) : ClientCommand {
|
||||||
private readonly ManualResetEvent WaitHandle = waitHandle ?? throw new ArgumentNullException(nameof(waitHandle));
|
private readonly ManualResetEvent WaitHandle = waitHandle ?? throw new ArgumentNullException(nameof(waitHandle));
|
||||||
private readonly Func<bool> ShutdownCheck = shutdownCheck ?? throw new ArgumentNullException(nameof(shutdownCheck));
|
private readonly Func<bool> ShutdownCheck = shutdownCheck ?? throw new ArgumentNullException(nameof(shutdownCheck));
|
||||||
|
|
||||||
public bool IsMatch(ChatCommandContext ctx) {
|
public bool IsMatch(ClientCommandContext ctx) {
|
||||||
return ctx.NameEquals("shutdown")
|
return ctx.NameEquals("shutdown")
|
||||||
|| ctx.NameEquals("restart");
|
|| ctx.NameEquals("restart");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispatch(ChatCommandContext ctx) {
|
public void Dispatch(ClientCommandContext ctx) {
|
||||||
if(ctx.User.UserId != 1) {
|
if(ctx.User.UserId != 1) {
|
||||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||||
|
@ -21,7 +21,7 @@ namespace SharpChat.Commands {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(ctx.NameEquals("restart"))
|
if(ctx.NameEquals("restart"))
|
||||||
foreach(ChatConnection conn in ctx.Chat.Connections)
|
foreach(Connection conn in ctx.Chat.Connections)
|
||||||
conn.PrepareForRestart();
|
conn.PrepareForRestart();
|
||||||
|
|
||||||
ctx.Chat.Update();
|
ctx.Chat.Update();
|
|
@ -1,14 +1,14 @@
|
||||||
using SharpChat.Events;
|
using SharpChat.Events;
|
||||||
using SharpChat.S2CPackets;
|
using SharpChat.S2CPackets;
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
namespace SharpChat.ClientCommands {
|
||||||
public class WhisperCommand : ChatCommand {
|
public class WhisperClientCommand : ClientCommand {
|
||||||
public bool IsMatch(ChatCommandContext ctx) {
|
public bool IsMatch(ClientCommandContext ctx) {
|
||||||
return ctx.NameEquals("whisper")
|
return ctx.NameEquals("whisper")
|
||||||
|| ctx.NameEquals("msg");
|
|| ctx.NameEquals("msg");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispatch(ChatCommandContext ctx) {
|
public void Dispatch(ClientCommandContext ctx) {
|
||||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||||
|
|
||||||
if(ctx.Args.Length < 2) {
|
if(ctx.Args.Length < 2) {
|
||||||
|
@ -17,7 +17,7 @@ namespace SharpChat.Commands {
|
||||||
}
|
}
|
||||||
|
|
||||||
string whisperUserStr = ctx.Args.FirstOrDefault() ?? string.Empty;
|
string whisperUserStr = ctx.Args.FirstOrDefault() ?? string.Empty;
|
||||||
ChatUser? whisperUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(whisperUserStr));
|
User? whisperUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(whisperUserStr));
|
||||||
|
|
||||||
if(whisperUser == null) {
|
if(whisperUser == null) {
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_FOUND, true, whisperUserStr));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_FOUND, true, whisperUserStr));
|
||||||
|
@ -29,7 +29,7 @@ namespace SharpChat.Commands {
|
||||||
|
|
||||||
ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
||||||
msgId,
|
msgId,
|
||||||
ChatUser.GetDMChannelName(ctx.User, whisperUser),
|
User.GetDMChannelName(ctx.User, whisperUser),
|
||||||
ctx.User.UserId,
|
ctx.User.UserId,
|
||||||
ctx.User.UserName,
|
ctx.User.UserName,
|
||||||
ctx.User.Colour,
|
ctx.User.Colour,
|
|
@ -1,19 +1,19 @@
|
||||||
using SharpChat.S2CPackets;
|
using SharpChat.S2CPackets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace SharpChat.Commands {
|
namespace SharpChat.ClientCommands {
|
||||||
public class WhoCommand : ChatCommand {
|
public class WhoClientCommand : ClientCommand {
|
||||||
public bool IsMatch(ChatCommandContext ctx) {
|
public bool IsMatch(ClientCommandContext ctx) {
|
||||||
return ctx.NameEquals("who");
|
return ctx.NameEquals("who");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispatch(ChatCommandContext ctx) {
|
public void Dispatch(ClientCommandContext ctx) {
|
||||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||||
StringBuilder whoChanSB = new();
|
StringBuilder whoChanSB = new();
|
||||||
string? whoChanStr = ctx.Args.FirstOrDefault();
|
string? whoChanStr = ctx.Args.FirstOrDefault();
|
||||||
|
|
||||||
if(string.IsNullOrEmpty(whoChanStr)) {
|
if(string.IsNullOrEmpty(whoChanStr)) {
|
||||||
foreach(ChatUser whoUser in ctx.Chat.Users) {
|
foreach(User whoUser in ctx.Chat.Users) {
|
||||||
whoChanSB.Append(@"<a href=""javascript:void(0);"" onclick=""UI.InsertChatText(this.innerHTML);""");
|
whoChanSB.Append(@"<a href=""javascript:void(0);"" onclick=""UI.InsertChatText(this.innerHTML);""");
|
||||||
|
|
||||||
if(whoUser == ctx.User)
|
if(whoUser == ctx.User)
|
||||||
|
@ -29,19 +29,19 @@ namespace SharpChat.Commands {
|
||||||
|
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USERS_LISTING_SERVER, false, whoChanSB));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USERS_LISTING_SERVER, false, whoChanSB));
|
||||||
} else {
|
} else {
|
||||||
ChatChannel? whoChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(whoChanStr));
|
Channel? whoChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(whoChanStr));
|
||||||
|
|
||||||
if(whoChan is null) {
|
if(whoChan is null) {
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NOT_FOUND, true, whoChanStr));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NOT_FOUND, true, whoChanStr));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(whoChan.Rank > ctx.User.Rank || (whoChan.HasPassword && !ctx.User.Can(ChatUserPermissions.JoinAnyChannel))) {
|
if(whoChan.Rank > ctx.User.Rank || (whoChan.HasPassword && !ctx.User.Can(UserPermissions.JoinAnyChannel))) {
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USERS_LISTING_ERROR, true, whoChanStr));
|
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USERS_LISTING_ERROR, true, whoChanStr));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(ChatUser whoUser in ctx.Chat.GetChannelUsers(whoChan)) {
|
foreach(User whoUser in ctx.Chat.GetChannelUsers(whoChan)) {
|
||||||
whoChanSB.Append(@"<a href=""javascript:void(0);"" onclick=""UI.InsertChatText(this.innerHTML);""");
|
whoChanSB.Append(@"<a href=""javascript:void(0);"" onclick=""UI.InsertChatText(this.innerHTML);""");
|
||||||
|
|
||||||
if(whoUser == ctx.User)
|
if(whoUser == ctx.User)
|
|
@ -2,7 +2,7 @@
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
|
||||||
namespace SharpChat {
|
namespace SharpChat {
|
||||||
public class ChatConnection : IDisposable {
|
public class Connection : IDisposable {
|
||||||
public const int ID_LENGTH = 20;
|
public const int ID_LENGTH = 20;
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
@ -16,7 +16,7 @@ namespace SharpChat {
|
||||||
public string Id { get; }
|
public string Id { get; }
|
||||||
public bool IsDisposed { get; private set; }
|
public bool IsDisposed { get; private set; }
|
||||||
public DateTimeOffset LastPing { get; set; } = DateTimeOffset.Now;
|
public DateTimeOffset LastPing { get; set; } = DateTimeOffset.Now;
|
||||||
public ChatUser? User { get; set; }
|
public User? User { get; set; }
|
||||||
|
|
||||||
private int CloseCode { get; set; } = 1000;
|
private int CloseCode { get; set; } = 1000;
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ namespace SharpChat {
|
||||||
|
|
||||||
public bool IsAlive => !IsDisposed && !HasTimedOut;
|
public bool IsAlive => !IsDisposed && !HasTimedOut;
|
||||||
|
|
||||||
public ChatConnection(IWebSocketConnection sock) {
|
public Connection(IWebSocketConnection sock) {
|
||||||
Socket = sock;
|
Socket = sock;
|
||||||
Id = RNG.SecureRandomString(ID_LENGTH);
|
Id = RNG.SecureRandomString(ID_LENGTH);
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ namespace SharpChat {
|
||||||
CloseCode = 1012;
|
CloseCode = 1012;
|
||||||
}
|
}
|
||||||
|
|
||||||
~ChatConnection() {
|
~Connection() {
|
||||||
DoDispose();
|
DoDispose();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ using SharpChat.Snowflake;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
|
||||||
namespace SharpChat {
|
namespace SharpChat {
|
||||||
public class ChatContext {
|
public class Context {
|
||||||
public record ChannelUserAssoc(long UserId, string ChannelName);
|
public record ChannelUserAssoc(long UserId, string ChannelName);
|
||||||
|
|
||||||
public readonly SemaphoreSlim ContextAccess = new(1, 1);
|
public readonly SemaphoreSlim ContextAccess = new(1, 1);
|
||||||
|
@ -13,15 +13,15 @@ namespace SharpChat {
|
||||||
public SnowflakeGenerator SnowflakeGenerator { get; } = new();
|
public SnowflakeGenerator SnowflakeGenerator { get; } = new();
|
||||||
public RandomSnowflake RandomSnowflake { get; }
|
public RandomSnowflake RandomSnowflake { get; }
|
||||||
|
|
||||||
public HashSet<ChatChannel> Channels { get; } = [];
|
public HashSet<Channel> Channels { get; } = [];
|
||||||
public HashSet<ChatConnection> Connections { get; } = [];
|
public HashSet<Connection> Connections { get; } = [];
|
||||||
public HashSet<ChatUser> Users { get; } = [];
|
public HashSet<User> Users { get; } = [];
|
||||||
public EventStorage.EventStorage Events { get; }
|
public EventStorage.EventStorage Events { get; }
|
||||||
public HashSet<ChannelUserAssoc> ChannelUsers { get; } = [];
|
public HashSet<ChannelUserAssoc> ChannelUsers { get; } = [];
|
||||||
public Dictionary<long, RateLimiter> UserRateLimiters { get; } = [];
|
public Dictionary<long, RateLimiter> UserRateLimiters { get; } = [];
|
||||||
public Dictionary<long, ChatChannel> UserLastChannel { get; } = [];
|
public Dictionary<long, Channel> UserLastChannel { get; } = [];
|
||||||
|
|
||||||
public ChatContext(EventStorage.EventStorage evtStore) {
|
public Context(EventStorage.EventStorage evtStore) {
|
||||||
Events = evtStore ?? throw new ArgumentNullException(nameof(evtStore));
|
Events = evtStore ?? throw new ArgumentNullException(nameof(evtStore));
|
||||||
RandomSnowflake = new(SnowflakeGenerator);
|
RandomSnowflake = new(SnowflakeGenerator);
|
||||||
}
|
}
|
||||||
|
@ -42,12 +42,12 @@ namespace SharpChat {
|
||||||
if(uids.Count() != 2)
|
if(uids.Count() != 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
IEnumerable<ChatUser> users = Users.Where(u => uids.Any(uid => uid == u.UserId));
|
IEnumerable<User> users = Users.Where(u => uids.Any(uid => uid == u.UserId));
|
||||||
ChatUser? target = users.FirstOrDefault(u => u.UserId != mce.SenderId);
|
User? target = users.FirstOrDefault(u => u.UserId != mce.SenderId);
|
||||||
if(target == null)
|
if(target == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach(ChatUser user in users)
|
foreach(User user in users)
|
||||||
SendTo(user, new ChatMessageAddS2CPacket(
|
SendTo(user, new ChatMessageAddS2CPacket(
|
||||||
mce.MessageId,
|
mce.MessageId,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
|
@ -57,7 +57,7 @@ namespace SharpChat {
|
||||||
true
|
true
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
ChatChannel? channel = Channels.FirstOrDefault(c => c.NameEquals(mce.ChannelName));
|
Channel? channel = Channels.FirstOrDefault(c => c.NameEquals(mce.ChannelName));
|
||||||
if(channel is not null)
|
if(channel is not null)
|
||||||
SendTo(channel, new ChatMessageAddS2CPacket(
|
SendTo(channel, new ChatMessageAddS2CPacket(
|
||||||
mce.MessageId,
|
mce.MessageId,
|
||||||
|
@ -83,7 +83,7 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update() {
|
public void Update() {
|
||||||
foreach(ChatConnection conn in Connections)
|
foreach(Connection conn in Connections)
|
||||||
if(!conn.IsDisposed && conn.HasTimedOut) {
|
if(!conn.IsDisposed && conn.HasTimedOut) {
|
||||||
conn.Dispose();
|
conn.Dispose();
|
||||||
Logger.Write($"Nuked connection {conn.Id} associated with {conn.User}.");
|
Logger.Write($"Nuked connection {conn.Id} associated with {conn.User}.");
|
||||||
|
@ -91,7 +91,7 @@ namespace SharpChat {
|
||||||
|
|
||||||
Connections.RemoveWhere(conn => conn.IsDisposed);
|
Connections.RemoveWhere(conn => conn.IsDisposed);
|
||||||
|
|
||||||
foreach(ChatUser user in Users)
|
foreach(User user in Users)
|
||||||
if(!Connections.Any(conn => conn.User == user)) {
|
if(!Connections.Any(conn => conn.User == user)) {
|
||||||
HandleDisconnect(user, UserDisconnectS2CPacket.Reason.TimeOut);
|
HandleDisconnect(user, UserDisconnectS2CPacket.Reason.TimeOut);
|
||||||
Logger.Write($"Timed out {user} (no more connections).");
|
Logger.Write($"Timed out {user} (no more connections).");
|
||||||
|
@ -107,37 +107,37 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsInChannel(ChatUser user, ChatChannel channel) {
|
public bool IsInChannel(User user, Channel channel) {
|
||||||
return ChannelUsers.Contains(new ChannelUserAssoc(user.UserId, channel.Name));
|
return ChannelUsers.Contains(new ChannelUserAssoc(user.UserId, channel.Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public string[] GetUserChannelNames(ChatUser user) {
|
public string[] GetUserChannelNames(User user) {
|
||||||
return [.. ChannelUsers.Where(cu => cu.UserId == user.UserId).Select(cu => cu.ChannelName)];
|
return [.. ChannelUsers.Where(cu => cu.UserId == user.UserId).Select(cu => cu.ChannelName)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChatChannel[] GetUserChannels(ChatUser user) {
|
public Channel[] GetUserChannels(User user) {
|
||||||
string[] names = GetUserChannelNames(user);
|
string[] names = GetUserChannelNames(user);
|
||||||
return [.. Channels.Where(c => names.Any(n => c.NameEquals(n)))];
|
return [.. Channels.Where(c => names.Any(n => c.NameEquals(n)))];
|
||||||
}
|
}
|
||||||
|
|
||||||
public long[] GetChannelUserIds(ChatChannel channel) {
|
public long[] GetChannelUserIds(Channel channel) {
|
||||||
return [.. ChannelUsers.Where(cu => channel.NameEquals(cu.ChannelName)).Select(cu => cu.UserId)];
|
return [.. ChannelUsers.Where(cu => channel.NameEquals(cu.ChannelName)).Select(cu => cu.UserId)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChatUser[] GetChannelUsers(ChatChannel channel) {
|
public User[] GetChannelUsers(Channel channel) {
|
||||||
long[] ids = GetChannelUserIds(channel);
|
long[] ids = GetChannelUserIds(channel);
|
||||||
return [.. Users.Where(u => ids.Contains(u.UserId))];
|
return [.. Users.Where(u => ids.Contains(u.UserId))];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateUser(
|
public void UpdateUser(
|
||||||
ChatUser user,
|
User user,
|
||||||
string? userName = null,
|
string? userName = null,
|
||||||
string? nickName = null,
|
string? nickName = null,
|
||||||
ChatColour? colour = null,
|
Colour? colour = null,
|
||||||
ChatUserStatus? status = null,
|
UserStatus? status = null,
|
||||||
string? statusText = null,
|
string? statusText = null,
|
||||||
int? rank = null,
|
int? rank = null,
|
||||||
ChatUserPermissions? perms = null,
|
UserPermissions? perms = null,
|
||||||
bool silent = false
|
bool silent = false
|
||||||
) {
|
) {
|
||||||
ArgumentNullException.ThrowIfNull(user);
|
ArgumentNullException.ThrowIfNull(user);
|
||||||
|
@ -191,14 +191,14 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BanUser(ChatUser user, TimeSpan duration, UserDisconnectS2CPacket.Reason reason = UserDisconnectS2CPacket.Reason.Kicked) {
|
public void BanUser(User user, TimeSpan duration, UserDisconnectS2CPacket.Reason reason = UserDisconnectS2CPacket.Reason.Kicked) {
|
||||||
if(duration > TimeSpan.Zero) {
|
if(duration > TimeSpan.Zero) {
|
||||||
DateTimeOffset expires = duration >= TimeSpan.MaxValue ? DateTimeOffset.MaxValue : DateTimeOffset.Now + duration;
|
DateTimeOffset expires = duration >= TimeSpan.MaxValue ? DateTimeOffset.MaxValue : DateTimeOffset.Now + duration;
|
||||||
SendTo(user, new ForceDisconnectS2CPacket(expires));
|
SendTo(user, new ForceDisconnectS2CPacket(expires));
|
||||||
} else
|
} else
|
||||||
SendTo(user, new ForceDisconnectS2CPacket());
|
SendTo(user, new ForceDisconnectS2CPacket());
|
||||||
|
|
||||||
foreach(ChatConnection conn in Connections)
|
foreach(Connection conn in Connections)
|
||||||
if(conn.User == user)
|
if(conn.User == user)
|
||||||
conn.Dispose();
|
conn.Dispose();
|
||||||
Connections.RemoveWhere(conn => conn.IsDisposed);
|
Connections.RemoveWhere(conn => conn.IsDisposed);
|
||||||
|
@ -206,7 +206,7 @@ namespace SharpChat {
|
||||||
HandleDisconnect(user, reason);
|
HandleDisconnect(user, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleJoin(ChatUser user, ChatChannel chan, ChatConnection conn, int maxMsgLength) {
|
public void HandleJoin(User user, Channel chan, Connection conn, int maxMsgLength) {
|
||||||
if(!IsInChannel(user, chan)) {
|
if(!IsInChannel(user, chan)) {
|
||||||
long msgId = RandomSnowflake.Next();
|
long msgId = RandomSnowflake.Next();
|
||||||
SendTo(chan, new UserConnectS2CPacket(msgId, DateTimeOffset.Now, user.UserId, user.LegacyNameWithStatus, user.Colour, user.Rank, user.Permissions));
|
SendTo(chan, new UserConnectS2CPacket(msgId, DateTimeOffset.Now, user.UserId, user.LegacyNameWithStatus, user.Colour, user.Rank, user.Permissions));
|
||||||
|
@ -248,14 +248,14 @@ namespace SharpChat {
|
||||||
UserLastChannel[user.UserId] = chan;
|
UserLastChannel[user.UserId] = chan;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleDisconnect(ChatUser user, UserDisconnectS2CPacket.Reason reason = UserDisconnectS2CPacket.Reason.Leave) {
|
public void HandleDisconnect(User user, UserDisconnectS2CPacket.Reason reason = UserDisconnectS2CPacket.Reason.Leave) {
|
||||||
UpdateUser(user, status: ChatUserStatus.Offline);
|
UpdateUser(user, status: UserStatus.Offline);
|
||||||
Users.Remove(user);
|
Users.Remove(user);
|
||||||
UserLastChannel.Remove(user.UserId);
|
UserLastChannel.Remove(user.UserId);
|
||||||
|
|
||||||
ChatChannel[] channels = GetUserChannels(user);
|
Channel[] channels = GetUserChannels(user);
|
||||||
|
|
||||||
foreach(ChatChannel chan in channels) {
|
foreach(Channel chan in channels) {
|
||||||
ChannelUsers.Remove(new ChannelUserAssoc(user.UserId, chan.Name));
|
ChannelUsers.Remove(new ChannelUserAssoc(user.UserId, chan.Name));
|
||||||
|
|
||||||
long msgId = RandomSnowflake.Next();
|
long msgId = RandomSnowflake.Next();
|
||||||
|
@ -267,13 +267,13 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SwitchChannel(ChatUser user, ChatChannel chan, string password) {
|
public void SwitchChannel(User user, Channel chan, string password) {
|
||||||
if(UserLastChannel.TryGetValue(user.UserId, out ChatChannel? ulc) && chan == ulc) {
|
if(UserLastChannel.TryGetValue(user.UserId, out Channel? ulc) && chan == ulc) {
|
||||||
ForceChannel(user);
|
ForceChannel(user);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!user.Can(ChatUserPermissions.JoinAnyChannel) && chan.IsOwner(user)) {
|
if(!user.Can(UserPermissions.JoinAnyChannel) && chan.IsOwner(user)) {
|
||||||
if(chan.Rank > user.Rank) {
|
if(chan.Rank > user.Rank) {
|
||||||
SendTo(user, new CommandResponseS2CPacket(RandomSnowflake.Next(), LCR.CHANNEL_INSUFFICIENT_HIERARCHY, true, chan.Name));
|
SendTo(user, new CommandResponseS2CPacket(RandomSnowflake.Next(), LCR.CHANNEL_INSUFFICIENT_HIERARCHY, true, chan.Name));
|
||||||
ForceChannel(user);
|
ForceChannel(user);
|
||||||
|
@ -290,11 +290,11 @@ namespace SharpChat {
|
||||||
ForceChannelSwitch(user, chan);
|
ForceChannelSwitch(user, chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ForceChannelSwitch(ChatUser user, ChatChannel chan) {
|
public void ForceChannelSwitch(User user, Channel chan) {
|
||||||
if(!Channels.Contains(chan))
|
if(!Channels.Contains(chan))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ChatChannel oldChan = UserLastChannel[user.UserId];
|
Channel oldChan = UserLastChannel[user.UserId];
|
||||||
|
|
||||||
long leaveId = RandomSnowflake.Next();
|
long leaveId = RandomSnowflake.Next();
|
||||||
SendTo(oldChan, new UserChannelLeaveS2CPacket(leaveId, user.UserId));
|
SendTo(oldChan, new UserChannelLeaveS2CPacket(leaveId, user.UserId));
|
||||||
|
@ -333,45 +333,45 @@ namespace SharpChat {
|
||||||
public void Send(S2CPacket packet) {
|
public void Send(S2CPacket packet) {
|
||||||
ArgumentNullException.ThrowIfNull(packet);
|
ArgumentNullException.ThrowIfNull(packet);
|
||||||
|
|
||||||
foreach(ChatConnection conn in Connections)
|
foreach(Connection conn in Connections)
|
||||||
if(conn.IsAlive && conn.User is not null)
|
if(conn.IsAlive && conn.User is not null)
|
||||||
conn.Send(packet);
|
conn.Send(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendTo(ChatUser user, S2CPacket packet) {
|
public void SendTo(User user, S2CPacket packet) {
|
||||||
ArgumentNullException.ThrowIfNull(user);
|
ArgumentNullException.ThrowIfNull(user);
|
||||||
ArgumentNullException.ThrowIfNull(packet);
|
ArgumentNullException.ThrowIfNull(packet);
|
||||||
|
|
||||||
foreach(ChatConnection conn in Connections)
|
foreach(Connection conn in Connections)
|
||||||
if(conn.IsAlive && conn.User == user)
|
if(conn.IsAlive && conn.User == user)
|
||||||
conn.Send(packet);
|
conn.Send(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendTo(ChatChannel channel, S2CPacket packet) {
|
public void SendTo(Channel channel, S2CPacket packet) {
|
||||||
ArgumentNullException.ThrowIfNull(channel);
|
ArgumentNullException.ThrowIfNull(channel);
|
||||||
ArgumentNullException.ThrowIfNull(packet);
|
ArgumentNullException.ThrowIfNull(packet);
|
||||||
|
|
||||||
// might be faster to grab the users first and then cascade into that SendTo
|
// might be faster to grab the users first and then cascade into that SendTo
|
||||||
IEnumerable<ChatConnection> conns = Connections.Where(c => c.IsAlive && c.User is not null && IsInChannel(c.User, channel));
|
IEnumerable<Connection> conns = Connections.Where(c => c.IsAlive && c.User is not null && IsInChannel(c.User, channel));
|
||||||
foreach(ChatConnection conn in conns)
|
foreach(Connection conn in conns)
|
||||||
conn.Send(packet);
|
conn.Send(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendToUserChannels(ChatUser user, S2CPacket packet) {
|
public void SendToUserChannels(User user, S2CPacket packet) {
|
||||||
ArgumentNullException.ThrowIfNull(user);
|
ArgumentNullException.ThrowIfNull(user);
|
||||||
ArgumentNullException.ThrowIfNull(packet);
|
ArgumentNullException.ThrowIfNull(packet);
|
||||||
|
|
||||||
IEnumerable<ChatChannel> chans = Channels.Where(c => IsInChannel(user, c));
|
IEnumerable<Channel> chans = Channels.Where(c => IsInChannel(user, c));
|
||||||
IEnumerable<ChatConnection> conns = Connections.Where(conn => conn.IsAlive && conn.User is not null && ChannelUsers.Any(cu => cu.UserId == conn.User.UserId && chans.Any(chan => chan.NameEquals(cu.ChannelName))));
|
IEnumerable<Connection> conns = Connections.Where(conn => conn.IsAlive && conn.User is not null && ChannelUsers.Any(cu => cu.UserId == conn.User.UserId && chans.Any(chan => chan.NameEquals(cu.ChannelName))));
|
||||||
foreach(ChatConnection conn in conns)
|
foreach(Connection conn in conns)
|
||||||
conn.Send(packet);
|
conn.Send(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IPAddress[] GetRemoteAddresses(ChatUser user) {
|
public IPAddress[] GetRemoteAddresses(User user) {
|
||||||
return [.. Connections.Where(c => c.IsAlive && c.User == user).Select(c => c.RemoteAddress).Distinct()];
|
return [.. Connections.Where(c => c.IsAlive && c.User == user).Select(c => c.RemoteAddress).Distinct()];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ForceChannel(ChatUser user, ChatChannel? chan = null) {
|
public void ForceChannel(User user, Channel? chan = null) {
|
||||||
ArgumentNullException.ThrowIfNull(user);
|
ArgumentNullException.ThrowIfNull(user);
|
||||||
|
|
||||||
if(chan == null && !UserLastChannel.TryGetValue(user.UserId, out chan))
|
if(chan == null && !UserLastChannel.TryGetValue(user.UserId, out chan))
|
||||||
|
@ -380,7 +380,7 @@ namespace SharpChat {
|
||||||
SendTo(user, new UserChannelForceJoinS2CPacket(chan.Name));
|
SendTo(user, new UserChannelForceJoinS2CPacket(chan.Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateChannel(ChatChannel channel, bool? temporary = null, int? hierarchy = null, string? password = null) {
|
public void UpdateChannel(Channel channel, bool? temporary = null, int? hierarchy = null, string? password = null) {
|
||||||
ArgumentNullException.ThrowIfNull(channel);
|
ArgumentNullException.ThrowIfNull(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));
|
||||||
|
@ -395,15 +395,15 @@ 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 hierarchy 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 hierarchy change should receive delete and create packets respectively
|
||||||
foreach(ChatUser user in Users.Where(u => u.Rank >= channel.Rank))
|
foreach(User user in Users.Where(u => u.Rank >= channel.Rank))
|
||||||
SendTo(user, new ChannelUpdateS2CPacket(channel.Name, channel.Name, channel.HasPassword, channel.IsTemporary));
|
SendTo(user, new ChannelUpdateS2CPacket(channel.Name, channel.Name, channel.HasPassword, channel.IsTemporary));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveChannel(ChatChannel channel) {
|
public void RemoveChannel(Channel channel) {
|
||||||
if(channel == null || Channels.Count < 1)
|
if(channel == null || Channels.Count < 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ChatChannel? defaultChannel = Channels.FirstOrDefault();
|
Channel? defaultChannel = Channels.FirstOrDefault();
|
||||||
if(defaultChannel is null)
|
if(defaultChannel is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -412,11 +412,11 @@ 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 GetChannelUsers(channel))
|
foreach(User user in GetChannelUsers(channel))
|
||||||
SwitchChannel(user, defaultChannel, string.Empty);
|
SwitchChannel(user, defaultChannel, string.Empty);
|
||||||
|
|
||||||
// Broadcast deletion of channel
|
// Broadcast deletion of channel
|
||||||
foreach(ChatUser user in Users.Where(u => u.Rank >= channel.Rank))
|
foreach(User user in Users.Where(u => u.Rank >= channel.Rank))
|
||||||
SendTo(user, new ChannelDeleteS2CPacket(channel.Name));
|
SendTo(user, new ChannelDeleteS2CPacket(channel.Name));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,10 +6,10 @@
|
||||||
string channelName,
|
string channelName,
|
||||||
long senderId,
|
long senderId,
|
||||||
string senderName,
|
string senderName,
|
||||||
ChatColour senderColour,
|
Colour senderColour,
|
||||||
int senderRank,
|
int senderRank,
|
||||||
string senderNick,
|
string senderNick,
|
||||||
ChatUserPermissions senderPerms,
|
UserPermissions senderPerms,
|
||||||
object? data = null,
|
object? data = null,
|
||||||
StoredEventFlags flags = StoredEventFlags.None
|
StoredEventFlags flags = StoredEventFlags.None
|
||||||
);
|
);
|
||||||
|
|
|
@ -12,10 +12,10 @@ namespace SharpChat.EventStorage {
|
||||||
string channelName,
|
string channelName,
|
||||||
long senderId,
|
long senderId,
|
||||||
string senderName,
|
string senderName,
|
||||||
ChatColour senderColour,
|
Colour senderColour,
|
||||||
int senderRank,
|
int senderRank,
|
||||||
string senderNick,
|
string senderNick,
|
||||||
ChatUserPermissions senderPerms,
|
UserPermissions senderPerms,
|
||||||
object? data = null,
|
object? data = null,
|
||||||
StoredEventFlags flags = StoredEventFlags.None
|
StoredEventFlags flags = StoredEventFlags.None
|
||||||
) {
|
) {
|
||||||
|
@ -71,12 +71,12 @@ namespace SharpChat.EventStorage {
|
||||||
return new StoredEventInfo(
|
return new StoredEventInfo(
|
||||||
reader.GetInt64("event_id"),
|
reader.GetInt64("event_id"),
|
||||||
Encoding.ASCII.GetString((byte[])reader["event_type"]),
|
Encoding.ASCII.GetString((byte[])reader["event_type"]),
|
||||||
reader.IsDBNull(reader.GetOrdinal("event_sender")) ? null : new ChatUser(
|
reader.IsDBNull(reader.GetOrdinal("event_sender")) ? null : new User(
|
||||||
reader.GetInt64("event_sender"),
|
reader.GetInt64("event_sender"),
|
||||||
reader.IsDBNull(reader.GetOrdinal("event_sender_name")) ? string.Empty : reader.GetString("event_sender_name"),
|
reader.IsDBNull(reader.GetOrdinal("event_sender_name")) ? string.Empty : reader.GetString("event_sender_name"),
|
||||||
ChatColour.FromMisuzu(reader.GetInt32("event_sender_colour")),
|
Colour.FromMisuzu(reader.GetInt32("event_sender_colour")),
|
||||||
reader.GetInt32("event_sender_rank"),
|
reader.GetInt32("event_sender_rank"),
|
||||||
(ChatUserPermissions)reader.GetInt32("event_sender_perms"),
|
(UserPermissions)reader.GetInt32("event_sender_perms"),
|
||||||
reader.IsDBNull(reader.GetOrdinal("event_sender_nick")) ? string.Empty : reader.GetString("event_sender_nick")
|
reader.IsDBNull(reader.GetOrdinal("event_sender_nick")) ? string.Empty : reader.GetString("event_sender_nick")
|
||||||
),
|
),
|
||||||
DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32("event_created")),
|
DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32("event_created")),
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace SharpChat.EventStorage {
|
||||||
public class StoredEventInfo(
|
public class StoredEventInfo(
|
||||||
long id,
|
long id,
|
||||||
string type,
|
string type,
|
||||||
ChatUser? sender,
|
User? sender,
|
||||||
DateTimeOffset created,
|
DateTimeOffset created,
|
||||||
DateTimeOffset? deleted,
|
DateTimeOffset? deleted,
|
||||||
string? channelName,
|
string? channelName,
|
||||||
|
@ -13,7 +13,7 @@ namespace SharpChat.EventStorage {
|
||||||
) {
|
) {
|
||||||
public long Id { get; set; } = id;
|
public long Id { get; set; } = id;
|
||||||
public string Type { get; set; } = type;
|
public string Type { get; set; } = type;
|
||||||
public ChatUser? Sender { get; set; } = sender;
|
public User? Sender { get; set; } = sender;
|
||||||
public DateTimeOffset Created { get; set; } = created;
|
public DateTimeOffset Created { get; set; } = created;
|
||||||
public DateTimeOffset? Deleted { get; set; } = deleted;
|
public DateTimeOffset? Deleted { get; set; } = deleted;
|
||||||
public string? ChannelName { get; set; } = channelName;
|
public string? ChannelName { get; set; } = channelName;
|
||||||
|
|
|
@ -10,10 +10,10 @@ namespace SharpChat.EventStorage {
|
||||||
string channelName,
|
string channelName,
|
||||||
long senderId,
|
long senderId,
|
||||||
string senderName,
|
string senderName,
|
||||||
ChatColour senderColour,
|
Colour senderColour,
|
||||||
int senderRank,
|
int senderRank,
|
||||||
string senderNick,
|
string senderNick,
|
||||||
ChatUserPermissions senderPerms,
|
UserPermissions senderPerms,
|
||||||
object? data = null,
|
object? data = null,
|
||||||
StoredEventFlags flags = StoredEventFlags.None
|
StoredEventFlags flags = StoredEventFlags.None
|
||||||
) {
|
) {
|
||||||
|
@ -21,7 +21,7 @@ namespace SharpChat.EventStorage {
|
||||||
|
|
||||||
// VES is meant as an emergency fallback but this is something else
|
// VES is meant as an emergency fallback but this is something else
|
||||||
JsonDocument hack = JsonDocument.Parse(data == null ? "{}" : JsonSerializer.Serialize(data));
|
JsonDocument hack = JsonDocument.Parse(data == null ? "{}" : JsonSerializer.Serialize(data));
|
||||||
Events.Add(id, new(id, type, senderId < 1 ? null : new ChatUser(
|
Events.Add(id, new(id, type, senderId < 1 ? null : new User(
|
||||||
senderId,
|
senderId,
|
||||||
senderName,
|
senderName,
|
||||||
senderColour,
|
senderColour,
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
string channelName,
|
string channelName,
|
||||||
long senderId,
|
long senderId,
|
||||||
string senderName,
|
string senderName,
|
||||||
ChatColour senderColour,
|
Colour senderColour,
|
||||||
int senderRank,
|
int senderRank,
|
||||||
string senderNickName,
|
string senderNickName,
|
||||||
ChatUserPermissions senderPerms,
|
UserPermissions senderPerms,
|
||||||
DateTimeOffset msgCreated,
|
DateTimeOffset msgCreated,
|
||||||
string msgText,
|
string msgText,
|
||||||
bool isPrivate,
|
bool isPrivate,
|
||||||
|
@ -18,10 +18,10 @@
|
||||||
public string ChannelName { get; } = channelName;
|
public string ChannelName { get; } = channelName;
|
||||||
public long SenderId { get; } = senderId;
|
public long SenderId { get; } = senderId;
|
||||||
public string SenderName { get; } = senderName;
|
public string SenderName { get; } = senderName;
|
||||||
public ChatColour SenderColour { get; } = senderColour;
|
public Colour SenderColour { get; } = senderColour;
|
||||||
public int SenderRank { get; } = senderRank;
|
public int SenderRank { get; } = senderRank;
|
||||||
public string SenderNickName { get; } = senderNickName;
|
public string SenderNickName { get; } = senderNickName;
|
||||||
public ChatUserPermissions SenderPerms { get; } = senderPerms;
|
public UserPermissions SenderPerms { get; } = senderPerms;
|
||||||
public DateTimeOffset MessageCreated { get; } = msgCreated;
|
public DateTimeOffset MessageCreated { get; } = msgCreated;
|
||||||
public string MessageText { get; } = msgText;
|
public string MessageText { get; } = msgText;
|
||||||
public bool IsPrivate { get; } = isPrivate;
|
public bool IsPrivate { get; } = isPrivate;
|
||||||
|
|
|
@ -17,12 +17,12 @@ namespace SharpChat.Misuzu {
|
||||||
[JsonPropertyName("colour_raw")]
|
[JsonPropertyName("colour_raw")]
|
||||||
public int ColourRaw { get; set; }
|
public int ColourRaw { get; set; }
|
||||||
|
|
||||||
public ChatColour Colour => ChatColour.FromMisuzu(ColourRaw);
|
public Colour Colour => Colour.FromMisuzu(ColourRaw);
|
||||||
|
|
||||||
[JsonPropertyName("hierarchy")]
|
[JsonPropertyName("hierarchy")]
|
||||||
public int Rank { get; set; }
|
public int Rank { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("perms")]
|
[JsonPropertyName("perms")]
|
||||||
public ChatUserPermissions Permissions { get; set; }
|
public UserPermissions Permissions { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,6 @@ namespace SharpChat.Misuzu {
|
||||||
|
|
||||||
public bool HasExpired => !IsPermanent && DateTimeOffset.UtcNow >= ExpiresAt;
|
public bool HasExpired => !IsPermanent && DateTimeOffset.UtcNow >= ExpiresAt;
|
||||||
|
|
||||||
public ChatColour UserColour => ChatColour.FromMisuzu(UserColourRaw);
|
public Colour UserColour => Colour.FromMisuzu(UserColourRaw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,9 @@ namespace SharpChat.S2CPackets {
|
||||||
public class AuthSuccessS2CPacket(
|
public class AuthSuccessS2CPacket(
|
||||||
long userId,
|
long userId,
|
||||||
string userName,
|
string userName,
|
||||||
ChatColour userColour,
|
Colour userColour,
|
||||||
int userRank,
|
int userRank,
|
||||||
ChatUserPermissions userPerms,
|
UserPermissions userPerms,
|
||||||
string channelName,
|
string channelName,
|
||||||
int maxMsgLength
|
int maxMsgLength
|
||||||
) : S2CPacket {
|
) : S2CPacket {
|
||||||
|
@ -22,13 +22,13 @@ namespace SharpChat.S2CPackets {
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append(userRank);
|
sb.Append(userRank);
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(userPerms.HasFlag(ChatUserPermissions.KickUser) ? '1' : '0');
|
sb.Append(userPerms.HasFlag(UserPermissions.KickUser) ? '1' : '0');
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(userPerms.HasFlag(ChatUserPermissions.ViewLogs) ? '1' : '0');
|
sb.Append(userPerms.HasFlag(UserPermissions.ViewLogs) ? '1' : '0');
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(userPerms.HasFlag(ChatUserPermissions.SetOwnNickname) ? '1' : '0');
|
sb.Append(userPerms.HasFlag(UserPermissions.SetOwnNickname) ? '1' : '0');
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(userPerms.HasFlag(ChatUserPermissions.CreateChannel) ? (userPerms.HasFlag(ChatUserPermissions.SetChannelPermanent) ? '2' : '1') : '0');
|
sb.Append(userPerms.HasFlag(UserPermissions.CreateChannel) ? (userPerms.HasFlag(UserPermissions.SetChannelPermanent) ? '2' : '1') : '0');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append(channelName);
|
sb.Append(channelName);
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
|
|
|
@ -31,13 +31,13 @@ namespace SharpChat.S2CPackets
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append(Event.Sender.Rank);
|
sb.Append(Event.Sender.Rank);
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(Event.Sender.Permissions.HasFlag(ChatUserPermissions.KickUser) ? '1' : '0');
|
sb.Append(Event.Sender.Permissions.HasFlag(UserPermissions.KickUser) ? '1' : '0');
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(Event.Sender.Permissions.HasFlag(ChatUserPermissions.ViewLogs) ? '1' : '0');
|
sb.Append(Event.Sender.Permissions.HasFlag(UserPermissions.ViewLogs) ? '1' : '0');
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(Event.Sender.Permissions.HasFlag(ChatUserPermissions.SetOwnNickname) ? '1' : '0');
|
sb.Append(Event.Sender.Permissions.HasFlag(UserPermissions.SetOwnNickname) ? '1' : '0');
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(Event.Sender.Permissions.HasFlag(ChatUserPermissions.CreateChannel) ? (Event.Sender.Permissions.HasFlag(ChatUserPermissions.SetChannelPermanent) ? '2' : '1') : '0');
|
sb.Append(Event.Sender.Permissions.HasFlag(UserPermissions.CreateChannel) ? (Event.Sender.Permissions.HasFlag(UserPermissions.SetChannelPermanent) ? '2' : '1') : '0');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace SharpChat.S2CPackets {
|
namespace SharpChat.S2CPackets {
|
||||||
public class ContextUsersS2CPacket(IEnumerable<ContextUsersS2CPacket.Entry> entries) : S2CPacket {
|
public class ContextUsersS2CPacket(IEnumerable<ContextUsersS2CPacket.Entry> entries) : S2CPacket {
|
||||||
public record Entry(long id, string name, ChatColour colour, int rank, ChatUserPermissions perms, bool visible);
|
public record Entry(long id, string name, Colour colour, int rank, UserPermissions perms, bool visible);
|
||||||
|
|
||||||
public string Pack() {
|
public string Pack() {
|
||||||
StringBuilder sb = new();
|
StringBuilder sb = new();
|
||||||
|
@ -20,13 +20,13 @@ namespace SharpChat.S2CPackets {
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append(entry.rank);
|
sb.Append(entry.rank);
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(entry.perms.HasFlag(ChatUserPermissions.KickUser) ? '1' : '0');
|
sb.Append(entry.perms.HasFlag(UserPermissions.KickUser) ? '1' : '0');
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(entry.perms.HasFlag(ChatUserPermissions.ViewLogs) ? '1' : '0');
|
sb.Append(entry.perms.HasFlag(UserPermissions.ViewLogs) ? '1' : '0');
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(entry.perms.HasFlag(ChatUserPermissions.SetOwnNickname) ? '1' : '0');
|
sb.Append(entry.perms.HasFlag(UserPermissions.SetOwnNickname) ? '1' : '0');
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(entry.perms.HasFlag(ChatUserPermissions.CreateChannel) ? (entry.perms.HasFlag(ChatUserPermissions.SetChannelPermanent) ? '2' : '1') : '0');
|
sb.Append(entry.perms.HasFlag(UserPermissions.CreateChannel) ? (entry.perms.HasFlag(UserPermissions.SetChannelPermanent) ? '2' : '1') : '0');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append(entry.visible ? '1' : '0');
|
sb.Append(entry.visible ? '1' : '0');
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,9 @@ namespace SharpChat.S2CPackets {
|
||||||
long msgId,
|
long msgId,
|
||||||
long userId,
|
long userId,
|
||||||
string userName,
|
string userName,
|
||||||
ChatColour userColour,
|
Colour userColour,
|
||||||
int userRank,
|
int userRank,
|
||||||
ChatUserPermissions userPerms
|
UserPermissions userPerms
|
||||||
) : S2CPacket {
|
) : S2CPacket {
|
||||||
public string Pack() {
|
public string Pack() {
|
||||||
StringBuilder sb = new();
|
StringBuilder sb = new();
|
||||||
|
@ -21,13 +21,13 @@ namespace SharpChat.S2CPackets {
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append(userRank);
|
sb.Append(userRank);
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(userPerms.HasFlag(ChatUserPermissions.KickUser) ? '1' : '0');
|
sb.Append(userPerms.HasFlag(UserPermissions.KickUser) ? '1' : '0');
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(userPerms.HasFlag(ChatUserPermissions.ViewLogs) ? '1' : '0');
|
sb.Append(userPerms.HasFlag(UserPermissions.ViewLogs) ? '1' : '0');
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(userPerms.HasFlag(ChatUserPermissions.SetOwnNickname) ? '1' : '0');
|
sb.Append(userPerms.HasFlag(UserPermissions.SetOwnNickname) ? '1' : '0');
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(userPerms.HasFlag(ChatUserPermissions.CreateChannel) ? (userPerms.HasFlag(ChatUserPermissions.SetChannelPermanent) ? '2' : '1') : '0');
|
sb.Append(userPerms.HasFlag(UserPermissions.CreateChannel) ? (userPerms.HasFlag(UserPermissions.SetChannelPermanent) ? '2' : '1') : '0');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append(msgId);
|
sb.Append(msgId);
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,9 @@ namespace SharpChat.S2CPackets {
|
||||||
DateTimeOffset joined,
|
DateTimeOffset joined,
|
||||||
long userId,
|
long userId,
|
||||||
string userName,
|
string userName,
|
||||||
ChatColour userColour,
|
Colour userColour,
|
||||||
int userRank,
|
int userRank,
|
||||||
ChatUserPermissions userPerms
|
UserPermissions userPerms
|
||||||
) : S2CPacket {
|
) : S2CPacket {
|
||||||
public string Pack() {
|
public string Pack() {
|
||||||
StringBuilder sb = new();
|
StringBuilder sb = new();
|
||||||
|
@ -24,13 +24,13 @@ namespace SharpChat.S2CPackets {
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append(userRank);
|
sb.Append(userRank);
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(userPerms.HasFlag(ChatUserPermissions.KickUser) ? '1' : '0');
|
sb.Append(userPerms.HasFlag(UserPermissions.KickUser) ? '1' : '0');
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(userPerms.HasFlag(ChatUserPermissions.ViewLogs) ? '1' : '0');
|
sb.Append(userPerms.HasFlag(UserPermissions.ViewLogs) ? '1' : '0');
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(userPerms.HasFlag(ChatUserPermissions.SetOwnNickname) ? '1' : '0');
|
sb.Append(userPerms.HasFlag(UserPermissions.SetOwnNickname) ? '1' : '0');
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(userPerms.HasFlag(ChatUserPermissions.CreateChannel) ? (userPerms.HasFlag(ChatUserPermissions.SetChannelPermanent) ? '2' : '1') : '0');
|
sb.Append(userPerms.HasFlag(UserPermissions.CreateChannel) ? (userPerms.HasFlag(UserPermissions.SetChannelPermanent) ? '2' : '1') : '0');
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append(msgId);
|
sb.Append(msgId);
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@ namespace SharpChat.S2CPackets {
|
||||||
public class UserUpdateS2CPacket(
|
public class UserUpdateS2CPacket(
|
||||||
long userId,
|
long userId,
|
||||||
string userName,
|
string userName,
|
||||||
ChatColour userColour,
|
Colour userColour,
|
||||||
int userRank,
|
int userRank,
|
||||||
ChatUserPermissions userPerms
|
UserPermissions userPerms
|
||||||
) : S2CPacket {
|
) : S2CPacket {
|
||||||
public string Pack() {
|
public string Pack() {
|
||||||
StringBuilder sb = new();
|
StringBuilder sb = new();
|
||||||
|
@ -20,13 +20,13 @@ namespace SharpChat.S2CPackets {
|
||||||
sb.Append('\t');
|
sb.Append('\t');
|
||||||
sb.Append(userRank);
|
sb.Append(userRank);
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(userPerms.HasFlag(ChatUserPermissions.KickUser) ? '1' : '0');
|
sb.Append(userPerms.HasFlag(UserPermissions.KickUser) ? '1' : '0');
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(userPerms.HasFlag(ChatUserPermissions.ViewLogs) ? '1' : '0');
|
sb.Append(userPerms.HasFlag(UserPermissions.ViewLogs) ? '1' : '0');
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(userPerms.HasFlag(ChatUserPermissions.SetOwnNickname) ? '1' : '0');
|
sb.Append(userPerms.HasFlag(UserPermissions.SetOwnNickname) ? '1' : '0');
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
sb.Append(userPerms.HasFlag(ChatUserPermissions.CreateChannel) ? (userPerms.HasFlag(ChatUserPermissions.SetChannelPermanent) ? '2' : '1') : '0');
|
sb.Append(userPerms.HasFlag(UserPermissions.CreateChannel) ? (userPerms.HasFlag(UserPermissions.SetChannelPermanent) ? '2' : '1') : '0');
|
||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using Fleck;
|
using Fleck;
|
||||||
using SharpChat.Commands;
|
using SharpChat.ClientCommands;
|
||||||
using SharpChat.Config;
|
using SharpChat.Config;
|
||||||
using SharpChat.EventStorage;
|
using SharpChat.EventStorage;
|
||||||
using SharpChat.Misuzu;
|
using SharpChat.Misuzu;
|
||||||
|
@ -15,7 +15,7 @@ namespace SharpChat {
|
||||||
public const int DEFAULT_FLOOD_KICK_EXEMPT_RANK = 9;
|
public const int DEFAULT_FLOOD_KICK_EXEMPT_RANK = 9;
|
||||||
|
|
||||||
public IWebSocketServer Server { get; }
|
public IWebSocketServer Server { get; }
|
||||||
public ChatContext Context { get; }
|
public Context Context { get; }
|
||||||
|
|
||||||
private readonly HttpClient HttpClient;
|
private readonly HttpClient HttpClient;
|
||||||
private readonly MisuzuClient Misuzu;
|
private readonly MisuzuClient Misuzu;
|
||||||
|
@ -33,7 +33,7 @@ namespace SharpChat {
|
||||||
|
|
||||||
private static readonly string[] DEFAULT_CHANNELS = ["lounge"];
|
private static readonly string[] DEFAULT_CHANNELS = ["lounge"];
|
||||||
|
|
||||||
private ChatChannel DefaultChannel { get; set; }
|
private Channel DefaultChannel { get; set; }
|
||||||
|
|
||||||
public SockChatServer(HttpClient httpClient, MisuzuClient msz, EventStorage.EventStorage evtStore, Config.Config config) {
|
public SockChatServer(HttpClient httpClient, MisuzuClient msz, EventStorage.EventStorage evtStore, Config.Config config) {
|
||||||
Logger.Write("Initialising Sock Chat server...");
|
Logger.Write("Initialising Sock Chat server...");
|
||||||
|
@ -46,7 +46,7 @@ namespace SharpChat {
|
||||||
FloodKickLength = config.ReadCached("floodKickLength", DEFAULT_FLOOD_KICK_LENGTH);
|
FloodKickLength = config.ReadCached("floodKickLength", DEFAULT_FLOOD_KICK_LENGTH);
|
||||||
FloodKickExemptRank = config.ReadCached("floodKickExemptRank", DEFAULT_FLOOD_KICK_EXEMPT_RANK);
|
FloodKickExemptRank = config.ReadCached("floodKickExemptRank", DEFAULT_FLOOD_KICK_EXEMPT_RANK);
|
||||||
|
|
||||||
Context = new ChatContext(evtStore);
|
Context = new Context(evtStore);
|
||||||
|
|
||||||
string[]? channelNames = config.ReadValue("channels", DEFAULT_CHANNELS);
|
string[]? channelNames = config.ReadValue("channels", DEFAULT_CHANNELS);
|
||||||
if(channelNames is not null)
|
if(channelNames is not null)
|
||||||
|
@ -57,7 +57,7 @@ namespace SharpChat {
|
||||||
if(string.IsNullOrWhiteSpace(name))
|
if(string.IsNullOrWhiteSpace(name))
|
||||||
name = channelName;
|
name = channelName;
|
||||||
|
|
||||||
ChatChannel channelInfo = new(
|
Channel channelInfo = new(
|
||||||
name,
|
name,
|
||||||
channelCfg.SafeReadValue("password", string.Empty)!,
|
channelCfg.SafeReadValue("password", string.Empty)!,
|
||||||
rank: channelCfg.SafeReadValue("minRank", 0)
|
rank: channelCfg.SafeReadValue("minRank", 0)
|
||||||
|
@ -78,23 +78,23 @@ namespace SharpChat {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
SendMessageHandler.AddCommands([
|
SendMessageHandler.AddCommands([
|
||||||
new AFKCommand(),
|
new AFKClientCommand(),
|
||||||
new NickCommand(),
|
new NickClientCommand(),
|
||||||
new WhisperCommand(),
|
new WhisperClientCommand(),
|
||||||
new ActionCommand(),
|
new ActionClientCommand(),
|
||||||
new WhoCommand(),
|
new WhoClientCommand(),
|
||||||
new JoinChannelCommand(),
|
new JoinChannelClientCommand(),
|
||||||
new CreateChannelCommand(),
|
new CreateChannelClientCommand(),
|
||||||
new DeleteChannelCommand(),
|
new DeleteChannelClientCommand(),
|
||||||
new PasswordChannelCommand(),
|
new PasswordChannelClientCommand(),
|
||||||
new RankChannelCommand(),
|
new RankChannelClientCommand(),
|
||||||
new BroadcastCommand(),
|
new BroadcastClientCommand(),
|
||||||
new DeleteMessageCommand(),
|
new DeleteMessageClientCommand(),
|
||||||
new KickBanCommand(msz),
|
new KickBanClientCommand(msz),
|
||||||
new PardonUserCommand(msz),
|
new PardonUserClientCommand(msz),
|
||||||
new PardonAddressCommand(msz),
|
new PardonAddressClientCommand(msz),
|
||||||
new BanListCommand(msz),
|
new BanListClientCommand(msz),
|
||||||
new RemoteAddressCommand(),
|
new RemoteAddressClientCommand(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
ushort port = config.SafeReadValue("port", DEFAULT_PORT);
|
ushort port = config.SafeReadValue("port", DEFAULT_PORT);
|
||||||
|
@ -103,7 +103,7 @@ namespace SharpChat {
|
||||||
|
|
||||||
public void Listen(ManualResetEvent waitHandle) {
|
public void Listen(ManualResetEvent waitHandle) {
|
||||||
if(waitHandle != null)
|
if(waitHandle != null)
|
||||||
SendMessageHandler.AddCommand(new ShutdownRestartCommand(waitHandle, () => !IsShuttingDown && (IsShuttingDown = true)));
|
SendMessageHandler.AddCommand(new ShutdownRestartClientCommand(waitHandle, () => !IsShuttingDown && (IsShuttingDown = true)));
|
||||||
|
|
||||||
Server.Start(sock => {
|
Server.Start(sock => {
|
||||||
if(IsShuttingDown) {
|
if(IsShuttingDown) {
|
||||||
|
@ -111,7 +111,7 @@ namespace SharpChat {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatConnection conn = new(sock);
|
Connection conn = new(sock);
|
||||||
Context.Connections.Add(conn);
|
Context.Connections.Add(conn);
|
||||||
|
|
||||||
sock.OnOpen = () => OnOpen(conn);
|
sock.OnOpen = () => OnOpen(conn);
|
||||||
|
@ -123,17 +123,17 @@ namespace SharpChat {
|
||||||
Logger.Write("Listening...");
|
Logger.Write("Listening...");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnOpen(ChatConnection conn) {
|
private void OnOpen(Connection conn) {
|
||||||
Logger.Write($"Connection opened from {conn.RemoteAddress}:{conn.RemotePort}");
|
Logger.Write($"Connection opened from {conn.RemoteAddress}:{conn.RemotePort}");
|
||||||
Context.SafeUpdate();
|
Context.SafeUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnError(ChatConnection conn, Exception ex) {
|
private void OnError(Connection conn, Exception ex) {
|
||||||
Logger.Write($"[{conn.Id} {conn.RemoteAddress}] {ex}");
|
Logger.Write($"[{conn.Id} {conn.RemoteAddress}] {ex}");
|
||||||
Context.SafeUpdate();
|
Context.SafeUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnClose(ChatConnection conn) {
|
private void OnClose(Connection conn) {
|
||||||
Logger.Write($"Connection closed from {conn.RemoteAddress}:{conn.RemotePort}");
|
Logger.Write($"Connection closed from {conn.RemoteAddress}:{conn.RemotePort}");
|
||||||
|
|
||||||
Context.ContextAccess.Wait();
|
Context.ContextAccess.Wait();
|
||||||
|
@ -149,12 +149,12 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMessage(ChatConnection conn, string msg) {
|
private void OnMessage(Connection conn, string msg) {
|
||||||
Context.SafeUpdate();
|
Context.SafeUpdate();
|
||||||
|
|
||||||
// this doesn't affect non-authed connections?????
|
// this doesn't affect non-authed connections?????
|
||||||
if(conn.User is not null && conn.User.Rank < FloodKickExemptRank) {
|
if(conn.User is not null && conn.User.Rank < FloodKickExemptRank) {
|
||||||
ChatUser? banUser = null;
|
User? banUser = null;
|
||||||
string banAddr = string.Empty;
|
string banAddr = string.Empty;
|
||||||
TimeSpan banDuration = TimeSpan.MinValue;
|
TimeSpan banDuration = TimeSpan.MinValue;
|
||||||
|
|
||||||
|
@ -162,9 +162,9 @@ namespace SharpChat {
|
||||||
try {
|
try {
|
||||||
if(!Context.UserRateLimiters.TryGetValue(conn.User.UserId, out RateLimiter? rateLimiter))
|
if(!Context.UserRateLimiters.TryGetValue(conn.User.UserId, out RateLimiter? rateLimiter))
|
||||||
Context.UserRateLimiters.Add(conn.User.UserId, rateLimiter = new RateLimiter(
|
Context.UserRateLimiters.Add(conn.User.UserId, rateLimiter = new RateLimiter(
|
||||||
ChatUser.DEFAULT_SIZE,
|
User.DEFAULT_SIZE,
|
||||||
ChatUser.DEFAULT_MINIMUM_DELAY,
|
User.DEFAULT_MINIMUM_DELAY,
|
||||||
ChatUser.DEFAULT_RISKY_OFFSET
|
User.DEFAULT_RISKY_OFFSET
|
||||||
));
|
));
|
||||||
|
|
||||||
rateLimiter.Update();
|
rateLimiter.Update();
|
||||||
|
@ -199,7 +199,7 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatPacketHandlerContext context = new(msg, Context, conn);
|
C2SPacketHandlerContext context = new(msg, Context, conn);
|
||||||
C2SPacketHandler? handler = conn.User is null
|
C2SPacketHandler? handler = conn.User is null
|
||||||
? GuestHandlers.FirstOrDefault(h => h.IsMatch(context))
|
? GuestHandlers.FirstOrDefault(h => h.IsMatch(context))
|
||||||
: AuthedHandlers.FirstOrDefault(h => h.IsMatch(context));
|
: AuthedHandlers.FirstOrDefault(h => h.IsMatch(context));
|
||||||
|
@ -224,7 +224,7 @@ namespace SharpChat {
|
||||||
IsDisposed = true;
|
IsDisposed = true;
|
||||||
IsShuttingDown = true;
|
IsShuttingDown = true;
|
||||||
|
|
||||||
foreach(ChatConnection conn in Context.Connections)
|
foreach(Connection conn in Context.Connections)
|
||||||
conn.Dispose();
|
conn.Dispose();
|
||||||
|
|
||||||
Server?.Dispose();
|
Server?.Dispose();
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
using SharpChat.Commands;
|
using SharpChat.ClientCommands;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace SharpChat {
|
namespace SharpChat {
|
||||||
public class ChatUser(
|
public class User(
|
||||||
long userId,
|
long userId,
|
||||||
string userName,
|
string userName,
|
||||||
ChatColour colour,
|
Colour colour,
|
||||||
int rank,
|
int rank,
|
||||||
ChatUserPermissions perms,
|
UserPermissions perms,
|
||||||
string nickName = "",
|
string nickName = "",
|
||||||
ChatUserStatus status = ChatUserStatus.Online,
|
UserStatus status = UserStatus.Online,
|
||||||
string statusText = ""
|
string statusText = ""
|
||||||
) {
|
) {
|
||||||
public const int DEFAULT_SIZE = 30;
|
public const int DEFAULT_SIZE = 30;
|
||||||
|
@ -19,11 +19,11 @@ namespace SharpChat {
|
||||||
|
|
||||||
public long UserId { get; } = userId;
|
public long UserId { get; } = userId;
|
||||||
public string UserName { get; set; } = userName ?? throw new ArgumentNullException(nameof(userName));
|
public string UserName { get; set; } = userName ?? throw new ArgumentNullException(nameof(userName));
|
||||||
public ChatColour Colour { get; set; } = colour;
|
public Colour Colour { get; set; } = colour;
|
||||||
public int Rank { get; set; } = rank;
|
public int Rank { get; set; } = rank;
|
||||||
public ChatUserPermissions Permissions { get; set; } = perms;
|
public UserPermissions Permissions { get; set; } = perms;
|
||||||
public string NickName { get; set; } = nickName;
|
public string NickName { get; set; } = nickName;
|
||||||
public ChatUserStatus Status { get; set; } = status;
|
public UserStatus Status { get; set; } = status;
|
||||||
public string StatusText { get; set; } = statusText;
|
public string StatusText { get; set; } = statusText;
|
||||||
|
|
||||||
public string LegacyName => string.IsNullOrWhiteSpace(NickName) ? UserName : $"~{NickName}";
|
public string LegacyName => string.IsNullOrWhiteSpace(NickName) ? UserName : $"~{NickName}";
|
||||||
|
@ -32,12 +32,12 @@ namespace SharpChat {
|
||||||
get {
|
get {
|
||||||
StringBuilder sb = new();
|
StringBuilder sb = new();
|
||||||
|
|
||||||
if(Status == ChatUserStatus.Away) {
|
if(Status == UserStatus.Away) {
|
||||||
string statusText = StatusText.Trim();
|
string statusText = StatusText.Trim();
|
||||||
StringInfo sti = new(statusText);
|
StringInfo sti = new(statusText);
|
||||||
if(Encoding.UTF8.GetByteCount(statusText) > AFKCommand.MAX_BYTES
|
if(Encoding.UTF8.GetByteCount(statusText) > AFKClientCommand.MAX_BYTES
|
||||||
|| sti.LengthInTextElements > AFKCommand.MAX_GRAPHEMES)
|
|| sti.LengthInTextElements > AFKClientCommand.MAX_GRAPHEMES)
|
||||||
statusText = sti.SubstringByTextElements(0, Math.Min(sti.LengthInTextElements, AFKCommand.MAX_GRAPHEMES)).Trim();
|
statusText = sti.SubstringByTextElements(0, Math.Min(sti.LengthInTextElements, AFKClientCommand.MAX_GRAPHEMES)).Trim();
|
||||||
|
|
||||||
sb.AppendFormat("<{0}>_", statusText.ToUpperInvariant());
|
sb.AppendFormat("<{0}>_", statusText.ToUpperInvariant());
|
||||||
}
|
}
|
||||||
|
@ -48,8 +48,8 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Can(ChatUserPermissions perm, bool strict = false) {
|
public bool Can(UserPermissions perm, bool strict = false) {
|
||||||
ChatUserPermissions perms = Permissions & perm;
|
UserPermissions perms = Permissions & perm;
|
||||||
return strict ? perms == perm : perms > 0;
|
return strict ? perms == perm : perms > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ namespace SharpChat {
|
||||||
return UserId.GetHashCode();
|
return UserId.GetHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetDMChannelName(ChatUser user1, ChatUser user2) {
|
public static string GetDMChannelName(User user1, User user2) {
|
||||||
return user1.UserId < user2.UserId
|
return user1.UserId < user2.UserId
|
||||||
? $"@{user1.UserId}-{user2.UserId}"
|
? $"@{user1.UserId}-{user2.UserId}"
|
||||||
: $"@{user2.UserId}-{user1.UserId}";
|
: $"@{user2.UserId}-{user1.UserId}";
|
|
@ -1,6 +1,6 @@
|
||||||
namespace SharpChat {
|
namespace SharpChat {
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum ChatUserPermissions : int {
|
public enum UserPermissions : int {
|
||||||
KickUser = 0x00000001,
|
KickUser = 0x00000001,
|
||||||
BanUser = 0x00000002,
|
BanUser = 0x00000002,
|
||||||
//SilenceUser = 0x00000004,
|
//SilenceUser = 0x00000004,
|
|
@ -1,5 +1,5 @@
|
||||||
namespace SharpChat {
|
namespace SharpChat {
|
||||||
public enum ChatUserStatus {
|
public enum UserStatus {
|
||||||
Online,
|
Online,
|
||||||
Away,
|
Away,
|
||||||
Offline,
|
Offline,
|
|
@ -1,22 +1,22 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
namespace SharpChat {
|
namespace SharpChat {
|
||||||
public readonly struct ChatColour {
|
public readonly struct Colour {
|
||||||
public byte Red { get; }
|
public byte Red { get; }
|
||||||
public byte Green { get; }
|
public byte Green { get; }
|
||||||
public byte Blue { get; }
|
public byte Blue { get; }
|
||||||
public bool Inherits { get; }
|
public bool Inherits { get; }
|
||||||
|
|
||||||
public static ChatColour None { get; } = new();
|
public static Colour None { get; } = new();
|
||||||
|
|
||||||
public ChatColour() {
|
public Colour() {
|
||||||
Red = 0;
|
Red = 0;
|
||||||
Green = 0;
|
Green = 0;
|
||||||
Blue = 0;
|
Blue = 0;
|
||||||
Inherits = true;
|
Inherits = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChatColour(byte red, byte green, byte blue) {
|
public Colour(byte red, byte green, byte blue) {
|
||||||
Red = red;
|
Red = red;
|
||||||
Green = green;
|
Green = green;
|
||||||
Blue = blue;
|
Blue = blue;
|
||||||
|
@ -24,10 +24,10 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Equals([NotNullWhen(true)] object? obj) {
|
public override bool Equals([NotNullWhen(true)] object? obj) {
|
||||||
return obj is ChatColour colour && Equals(colour);
|
return obj is Colour colour && Equals(colour);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(ChatColour other) {
|
public bool Equals(Colour other) {
|
||||||
return Red == other.Red
|
return Red == other.Red
|
||||||
&& Green == other.Green
|
&& Green == other.Green
|
||||||
&& Blue == other.Blue
|
&& Blue == other.Blue
|
||||||
|
@ -48,7 +48,7 @@ namespace SharpChat {
|
||||||
return (Red << 16) | (Green << 8) | Blue;
|
return (Red << 16) | (Green << 8) | Blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ChatColour FromRawRGB(int rgb) {
|
public static Colour FromRawRGB(int rgb) {
|
||||||
return new(
|
return new(
|
||||||
(byte)((rgb >> 16) & 0xFF),
|
(byte)((rgb >> 16) & 0xFF),
|
||||||
(byte)((rgb >> 8) & 0xFF),
|
(byte)((rgb >> 8) & 0xFF),
|
||||||
|
@ -62,17 +62,17 @@ namespace SharpChat {
|
||||||
return Inherits ? MSZ_INHERIT : ToRawRGB();
|
return Inherits ? MSZ_INHERIT : ToRawRGB();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ChatColour FromMisuzu(int raw) {
|
public static Colour FromMisuzu(int raw) {
|
||||||
return (raw & MSZ_INHERIT) > 0
|
return (raw & MSZ_INHERIT) > 0
|
||||||
? None
|
? None
|
||||||
: FromRawRGB(raw);
|
: FromRawRGB(raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool operator ==(ChatColour left, ChatColour right) {
|
public static bool operator ==(Colour left, Colour right) {
|
||||||
return left.Equals(right);
|
return left.Equals(right);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool operator !=(ChatColour left, ChatColour right) {
|
public static bool operator !=(Colour left, Colour right) {
|
||||||
return !(left == right);
|
return !(left == right);
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue