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
34
SharpChat/ClientCommands/AFKClientCommand.cs
Normal file
34
SharpChat/ClientCommands/AFKClientCommand.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class AFKClientCommand : ClientCommand {
|
||||
private const string DEFAULT = "AFK";
|
||||
public const int MAX_GRAPHEMES = 5;
|
||||
public const int MAX_BYTES = MAX_GRAPHEMES * 10;
|
||||
|
||||
public bool IsMatch(ClientCommandContext ctx) {
|
||||
return ctx.NameEquals("afk");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
string? statusText = ctx.Args.FirstOrDefault();
|
||||
if(string.IsNullOrWhiteSpace(statusText))
|
||||
statusText = DEFAULT;
|
||||
else {
|
||||
statusText = statusText.Trim();
|
||||
|
||||
StringInfo sti = new(statusText);
|
||||
if(Encoding.UTF8.GetByteCount(statusText) > MAX_BYTES
|
||||
|| sti.LengthInTextElements > MAX_GRAPHEMES)
|
||||
statusText = sti.SubstringByTextElements(0, Math.Min(sti.LengthInTextElements, MAX_GRAPHEMES)).Trim();
|
||||
}
|
||||
|
||||
ctx.Chat.UpdateUser(
|
||||
ctx.User,
|
||||
status: UserStatus.Away,
|
||||
statusText: statusText
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
33
SharpChat/ClientCommands/ActionClientCommand.cs
Normal file
33
SharpChat/ClientCommands/ActionClientCommand.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using SharpChat.Events;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class ActionClientCommand : ClientCommand {
|
||||
public bool IsMatch(ClientCommandContext ctx) {
|
||||
return ctx.NameEquals("action")
|
||||
|| ctx.NameEquals("me");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
if(ctx.Args.Length < 1)
|
||||
return;
|
||||
|
||||
string actionStr = string.Join(' ', ctx.Args);
|
||||
if(string.IsNullOrWhiteSpace(actionStr))
|
||||
return;
|
||||
|
||||
ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
||||
ctx.Chat.RandomSnowflake.Next(),
|
||||
ctx.Channel.Name,
|
||||
ctx.User.UserId,
|
||||
ctx.User.UserName,
|
||||
ctx.User.Colour,
|
||||
ctx.User.Rank,
|
||||
ctx.User.NickName,
|
||||
ctx.User.Permissions,
|
||||
DateTimeOffset.Now,
|
||||
actionStr,
|
||||
false, true, false
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
37
SharpChat/ClientCommands/BanListClientCommand.cs
Normal file
37
SharpChat/ClientCommands/BanListClientCommand.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
using SharpChat.Misuzu;
|
||||
using SharpChat.S2CPackets;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class BanListClientCommand(MisuzuClient msz) : ClientCommand {
|
||||
private readonly MisuzuClient Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
|
||||
|
||||
public bool IsMatch(ClientCommandContext ctx) {
|
||||
return ctx.NameEquals("bans")
|
||||
|| ctx.NameEquals("banned");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(!ctx.User.Can(UserPermissions.BanUser | UserPermissions.KickUser)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
Task.Run(async () => {
|
||||
MisuzuBanInfo[]? mbis = await Misuzu.GetBanListAsync();
|
||||
if(mbis is null)
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.GENERIC_ERROR, true));
|
||||
else
|
||||
ctx.Chat.SendTo(ctx.User, new BanListS2CPacket(
|
||||
msgId,
|
||||
mbis.Where(mbi => mbi.IsBanned && !mbi.HasExpired)
|
||||
.Select(mbi => new BanListS2CPacket.Entry(
|
||||
BanListS2CPacket.Type.UserName, // Misuzu currently only does username bans so we can just do this
|
||||
mbi.UserName ?? $"({mbi.UserId})"
|
||||
))
|
||||
));
|
||||
}).Wait();
|
||||
}
|
||||
}
|
||||
}
|
34
SharpChat/ClientCommands/BroadcastClientCommand.cs
Normal file
34
SharpChat/ClientCommands/BroadcastClientCommand.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using SharpChat.Events;
|
||||
using SharpChat.S2CPackets;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class BroadcastClientCommand : ClientCommand {
|
||||
public bool IsMatch(ClientCommandContext ctx) {
|
||||
return ctx.NameEquals("say")
|
||||
|| ctx.NameEquals("broadcast");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(!ctx.User.Can(UserPermissions.Broadcast)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
||||
msgId,
|
||||
string.Empty,
|
||||
ctx.User.UserId,
|
||||
ctx.User.UserName,
|
||||
ctx.User.Colour,
|
||||
ctx.User.Rank,
|
||||
ctx.User.NickName,
|
||||
ctx.User.Permissions,
|
||||
DateTimeOffset.Now,
|
||||
string.Join(' ', ctx.Args),
|
||||
false, false, true
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
62
SharpChat/ClientCommands/CreateChannelClientCommand.cs
Normal file
62
SharpChat/ClientCommands/CreateChannelClientCommand.cs
Normal file
|
@ -0,0 +1,62 @@
|
|||
using SharpChat.S2CPackets;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class CreateChannelClientCommand : ClientCommand {
|
||||
public bool IsMatch(ClientCommandContext ctx) {
|
||||
return ctx.NameEquals("create");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(!ctx.User.Can(UserPermissions.CreateChannel)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
string firstArg = ctx.Args.First();
|
||||
|
||||
bool createChanHasHierarchy;
|
||||
if(ctx.Args.Length < 1 || (createChanHasHierarchy = firstArg.All(char.IsDigit) && ctx.Args.Length < 2)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
int createChanHierarchy = 0;
|
||||
if(createChanHasHierarchy)
|
||||
if(!int.TryParse(firstArg, out createChanHierarchy))
|
||||
createChanHierarchy = 0;
|
||||
|
||||
if(createChanHierarchy > ctx.User.Rank) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.INSUFFICIENT_HIERARCHY));
|
||||
return;
|
||||
}
|
||||
|
||||
string createChanName = string.Join('_', ctx.Args.Skip(createChanHasHierarchy ? 1 : 0));
|
||||
|
||||
if(!Channel.CheckName(createChanName)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NAME_INVALID));
|
||||
return;
|
||||
}
|
||||
|
||||
if(ctx.Chat.Channels.Any(c => c.NameEquals(createChanName))) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_ALREADY_EXISTS, true, createChanName));
|
||||
return;
|
||||
}
|
||||
|
||||
Channel createChan = new(
|
||||
createChanName,
|
||||
isTemporary: !ctx.User.Can(UserPermissions.SetChannelPermanent),
|
||||
rank: createChanHierarchy,
|
||||
ownerId: ctx.User.UserId
|
||||
);
|
||||
|
||||
ctx.Chat.Channels.Add(createChan);
|
||||
foreach(User ccu in ctx.Chat.Users.Where(u => u.Rank >= ctx.Channel.Rank))
|
||||
ctx.Chat.SendTo(ccu, new ChannelCreateS2CPacket(createChan.Name, createChan.HasPassword, createChan.IsTemporary));
|
||||
|
||||
ctx.Chat.SwitchChannel(ctx.User, createChan, createChan.Password);
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_CREATED, false, createChan.Name));
|
||||
}
|
||||
}
|
||||
}
|
37
SharpChat/ClientCommands/DeleteChannelClientCommand.cs
Normal file
37
SharpChat/ClientCommands/DeleteChannelClientCommand.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
using SharpChat.S2CPackets;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class DeleteChannelClientCommand : ClientCommand {
|
||||
public bool IsMatch(ClientCommandContext ctx) {
|
||||
return ctx.NameEquals("delchan") || (
|
||||
ctx.NameEquals("delete")
|
||||
&& ctx.Args.FirstOrDefault()?.All(char.IsDigit) == false
|
||||
);
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(ctx.Args.Length < 1 || string.IsNullOrWhiteSpace(ctx.Args.FirstOrDefault())) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
string delChanName = string.Join('_', ctx.Args);
|
||||
Channel? delChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(delChanName));
|
||||
|
||||
if(delChan == null) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NOT_FOUND, true, delChanName));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!ctx.User.Can(UserPermissions.DeleteChannel) && delChan.IsOwner(ctx.User)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_DELETE_FAILED, true, delChan.Name));
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.Chat.RemoveChannel(delChan);
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_DELETED, false, delChan.Name));
|
||||
}
|
||||
}
|
||||
}
|
41
SharpChat/ClientCommands/DeleteMessageClientCommand.cs
Normal file
41
SharpChat/ClientCommands/DeleteMessageClientCommand.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using SharpChat.EventStorage;
|
||||
using SharpChat.S2CPackets;
|
||||
|
||||
namespace SharpChat.ClientCommands
|
||||
{
|
||||
public class DeleteMessageClientCommand : ClientCommand {
|
||||
public bool IsMatch(ClientCommandContext ctx) {
|
||||
return ctx.NameEquals("delmsg") || (
|
||||
ctx.NameEquals("delete")
|
||||
&& ctx.Args.FirstOrDefault()?.All(char.IsDigit) == true
|
||||
);
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
bool deleteAnyMessage = ctx.User.Can(UserPermissions.DeleteAnyMessage);
|
||||
|
||||
if(!deleteAnyMessage && !ctx.User.Can(UserPermissions.DeleteOwnMessage)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
string? firstArg = ctx.Args.FirstOrDefault();
|
||||
|
||||
if(string.IsNullOrWhiteSpace(firstArg) || !firstArg.All(char.IsDigit) || !long.TryParse(firstArg, out long delSeqId)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
StoredEventInfo? delMsg = ctx.Chat.Events.GetEvent(delSeqId);
|
||||
|
||||
if(delMsg?.Sender is null || delMsg.Sender.Rank > ctx.User.Rank || (!deleteAnyMessage && delMsg.Sender.UserId != ctx.User.UserId)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.MESSAGE_DELETE_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.Chat.Events.RemoveEvent(delMsg);
|
||||
ctx.Chat.Send(new ChatMessageDeleteS2CPacket(delMsg.Id));
|
||||
}
|
||||
}
|
||||
}
|
23
SharpChat/ClientCommands/JoinChannelClientCommand.cs
Normal file
23
SharpChat/ClientCommands/JoinChannelClientCommand.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using SharpChat.S2CPackets;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class JoinChannelClientCommand : ClientCommand {
|
||||
public bool IsMatch(ClientCommandContext ctx) {
|
||||
return ctx.NameEquals("join");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
string joinChanStr = ctx.Args.FirstOrDefault() ?? "Channel";
|
||||
Channel? joinChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(joinChanStr));
|
||||
|
||||
if(joinChan is null) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NOT_FOUND, true, joinChanStr));
|
||||
ctx.Chat.ForceChannel(ctx.User);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.Chat.SwitchChannel(ctx.User, joinChan, string.Join(' ', ctx.Args.Skip(1)));
|
||||
}
|
||||
}
|
||||
}
|
81
SharpChat/ClientCommands/KickBanClientCommand.cs
Normal file
81
SharpChat/ClientCommands/KickBanClientCommand.cs
Normal file
|
@ -0,0 +1,81 @@
|
|||
using SharpChat.Misuzu;
|
||||
using SharpChat.S2CPackets;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class KickBanClientCommand(MisuzuClient msz) : ClientCommand {
|
||||
private readonly MisuzuClient Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
|
||||
|
||||
public bool IsMatch(ClientCommandContext ctx) {
|
||||
return ctx.NameEquals("kick")
|
||||
|| ctx.NameEquals("ban");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
bool isBanning = ctx.NameEquals("ban");
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(!ctx.User.Can(isBanning ? UserPermissions.BanUser : UserPermissions.KickUser)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
string? banUserTarget = ctx.Args.ElementAtOrDefault(0);
|
||||
string? banDurationStr = ctx.Args.ElementAtOrDefault(1);
|
||||
int banReasonIndex = 1;
|
||||
User? banUser = null;
|
||||
|
||||
if(banUserTarget == null || (banUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(banUserTarget))) == null) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_FOUND, true, banUserTarget ?? "User"));
|
||||
return;
|
||||
}
|
||||
|
||||
if(banUser.Rank >= ctx.User.Rank && banUser != ctx.User) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.KICK_NOT_ALLOWED, true, banUser.LegacyName));
|
||||
return;
|
||||
}
|
||||
|
||||
TimeSpan duration = isBanning ? TimeSpan.MaxValue : TimeSpan.Zero;
|
||||
if(!string.IsNullOrWhiteSpace(banDurationStr) && double.TryParse(banDurationStr, out double durationSeconds)) {
|
||||
if(durationSeconds < 0) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
duration = TimeSpan.FromSeconds(durationSeconds);
|
||||
++banReasonIndex;
|
||||
}
|
||||
|
||||
if(duration <= TimeSpan.Zero) {
|
||||
ctx.Chat.BanUser(banUser, duration);
|
||||
return;
|
||||
}
|
||||
|
||||
string banReason = string.Join(' ', ctx.Args.Skip(banReasonIndex));
|
||||
|
||||
Task.Run(async () => {
|
||||
string userId = banUser.UserId.ToString();
|
||||
string userIp = ctx.Chat.GetRemoteAddresses(banUser).FirstOrDefault()?.ToString() ?? string.Empty;
|
||||
|
||||
// obviously it makes no sense to only check for one ip address but that's current misuzu limitations
|
||||
MisuzuBanInfo? fbi = await Misuzu.CheckBanAsync(userId, userIp);
|
||||
if(fbi is null) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.GENERIC_ERROR, true));
|
||||
return;
|
||||
}
|
||||
|
||||
if(fbi.IsBanned && !fbi.HasExpired) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.KICK_NOT_ALLOWED, true, banUser.LegacyName));
|
||||
return;
|
||||
}
|
||||
|
||||
await Misuzu.CreateBanAsync(
|
||||
userId, userIp,
|
||||
ctx.User.UserId.ToString(), ctx.Connection.RemoteAddress.ToString(),
|
||||
duration, banReason
|
||||
);
|
||||
|
||||
ctx.Chat.BanUser(banUser, duration);
|
||||
}).Wait();
|
||||
}
|
||||
}
|
||||
}
|
63
SharpChat/ClientCommands/NickClientCommand.cs
Normal file
63
SharpChat/ClientCommands/NickClientCommand.cs
Normal file
|
@ -0,0 +1,63 @@
|
|||
using SharpChat.S2CPackets;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class NickClientCommand : ClientCommand {
|
||||
private const int MAX_GRAPHEMES = 16;
|
||||
private const int MAX_BYTES = MAX_GRAPHEMES * 10;
|
||||
|
||||
public bool IsMatch(ClientCommandContext ctx) {
|
||||
return ctx.NameEquals("nick");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
bool setOthersNick = ctx.User.Can(UserPermissions.SetOthersNickname);
|
||||
|
||||
if(!setOthersNick && !ctx.User.Can(UserPermissions.SetOwnNickname)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
User? targetUser = null;
|
||||
int offset = 0;
|
||||
|
||||
if(setOthersNick && long.TryParse(ctx.Args.FirstOrDefault(), out long targetUserId) && targetUserId > 0) {
|
||||
targetUser = ctx.Chat.Users.FirstOrDefault(u => u.UserId == targetUserId);
|
||||
++offset;
|
||||
}
|
||||
|
||||
targetUser ??= ctx.User;
|
||||
|
||||
if(ctx.Args.Length < offset) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
string nickStr = string.Join('_', ctx.Args.Skip(offset))
|
||||
.Replace("\n", string.Empty).Replace("\r", string.Empty)
|
||||
.Replace("\f", string.Empty).Replace("\t", string.Empty)
|
||||
.Replace(' ', '_').Trim();
|
||||
|
||||
if(nickStr == targetUser.UserName)
|
||||
nickStr = string.Empty;
|
||||
else if(string.IsNullOrEmpty(nickStr))
|
||||
nickStr = string.Empty;
|
||||
else {
|
||||
StringInfo nsi = new(nickStr);
|
||||
if(Encoding.UTF8.GetByteCount(nickStr) > MAX_BYTES
|
||||
|| nsi.LengthInTextElements > MAX_GRAPHEMES)
|
||||
nickStr = nsi.SubstringByTextElements(0, Math.Min(nsi.LengthInTextElements, MAX_GRAPHEMES)).Trim();
|
||||
}
|
||||
|
||||
if(!string.IsNullOrWhiteSpace(nickStr) && ctx.Chat.Users.Any(u => u.NameEquals(nickStr))) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.NAME_IN_USE, true, nickStr));
|
||||
return;
|
||||
}
|
||||
|
||||
string? previousName = targetUser.UserId == ctx.User.UserId ? (targetUser.NickName ?? targetUser.UserName) : null;
|
||||
ctx.Chat.UpdateUser(targetUser, nickName: nickStr, silent: previousName == null);
|
||||
}
|
||||
}
|
||||
}
|
50
SharpChat/ClientCommands/PardonAddressClientCommand.cs
Normal file
50
SharpChat/ClientCommands/PardonAddressClientCommand.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
using SharpChat.Misuzu;
|
||||
using SharpChat.S2CPackets;
|
||||
using System.Net;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class PardonAddressClientCommand(MisuzuClient msz) : ClientCommand {
|
||||
private readonly MisuzuClient Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
|
||||
|
||||
public bool IsMatch(ClientCommandContext ctx) {
|
||||
return ctx.NameEquals("pardonip")
|
||||
|| ctx.NameEquals("unbanip");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(!ctx.User.Can(UserPermissions.BanUser | UserPermissions.KickUser)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
string? unbanAddrTarget = ctx.Args.FirstOrDefault();
|
||||
if(string.IsNullOrWhiteSpace(unbanAddrTarget) || !IPAddress.TryParse(unbanAddrTarget, out IPAddress? unbanAddr)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
unbanAddrTarget = unbanAddr.ToString();
|
||||
|
||||
Task.Run(async () => {
|
||||
MisuzuBanInfo? banInfo = await Misuzu.CheckBanAsync(ipAddr: unbanAddrTarget);
|
||||
if(banInfo is null) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.GENERIC_ERROR, true));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!banInfo.IsBanned || banInfo.HasExpired) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_BANNED, true, unbanAddrTarget));
|
||||
return;
|
||||
}
|
||||
|
||||
bool wasBanned = await Misuzu.RevokeBanAsync(banInfo, MisuzuClient.BanRevokeKind.RemoteAddress);
|
||||
if(wasBanned)
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_UNBANNED, false, unbanAddrTarget));
|
||||
else
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_BANNED, true, unbanAddrTarget));
|
||||
}).Wait();
|
||||
}
|
||||
}
|
||||
}
|
57
SharpChat/ClientCommands/PardonUserClientCommand.cs
Normal file
57
SharpChat/ClientCommands/PardonUserClientCommand.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
using SharpChat.Misuzu;
|
||||
using SharpChat.S2CPackets;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class PardonUserClientCommand(MisuzuClient msz) : ClientCommand {
|
||||
private readonly MisuzuClient Misuzu = msz ?? throw new ArgumentNullException(nameof(msz));
|
||||
|
||||
public bool IsMatch(ClientCommandContext ctx) {
|
||||
return ctx.NameEquals("pardon")
|
||||
|| ctx.NameEquals("unban");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(!ctx.User.Can(UserPermissions.BanUser | UserPermissions.KickUser)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
bool unbanUserTargetIsName = true;
|
||||
string? unbanUserTarget = ctx.Args.FirstOrDefault();
|
||||
if(string.IsNullOrWhiteSpace(unbanUserTarget)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
User? unbanUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(unbanUserTarget));
|
||||
if(unbanUser == null && long.TryParse(unbanUserTarget, out long unbanUserId)) {
|
||||
unbanUserTargetIsName = false;
|
||||
unbanUser = ctx.Chat.Users.FirstOrDefault(u => u.UserId == unbanUserId);
|
||||
}
|
||||
|
||||
if(unbanUser != null)
|
||||
unbanUserTarget = unbanUser.UserId.ToString();
|
||||
|
||||
Task.Run(async () => {
|
||||
MisuzuBanInfo? banInfo = await Misuzu.CheckBanAsync(unbanUserTarget, userIdIsName: unbanUserTargetIsName);
|
||||
if(banInfo is null) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.GENERIC_ERROR, true));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!banInfo.IsBanned || banInfo.HasExpired) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_BANNED, true, unbanUserTarget));
|
||||
return;
|
||||
}
|
||||
|
||||
bool wasBanned = await Misuzu.RevokeBanAsync(banInfo, MisuzuClient.BanRevokeKind.UserId);
|
||||
if(wasBanned)
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_UNBANNED, false, unbanUserTarget));
|
||||
else
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_BANNED, true, unbanUserTarget));
|
||||
}).Wait();
|
||||
}
|
||||
}
|
||||
}
|
27
SharpChat/ClientCommands/PasswordChannelClientCommand.cs
Normal file
27
SharpChat/ClientCommands/PasswordChannelClientCommand.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using SharpChat.S2CPackets;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class PasswordChannelClientCommand : ClientCommand {
|
||||
public bool IsMatch(ClientCommandContext ctx) {
|
||||
return ctx.NameEquals("pwd")
|
||||
|| ctx.NameEquals("password");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(!ctx.User.Can(UserPermissions.SetChannelPassword) || ctx.Channel.IsOwner(ctx.User)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
string chanPass = string.Join(' ', ctx.Args).Trim();
|
||||
|
||||
if(string.IsNullOrWhiteSpace(chanPass))
|
||||
chanPass = string.Empty;
|
||||
|
||||
ctx.Chat.UpdateChannel(ctx.Channel, password: chanPass);
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_PASSWORD_CHANGED, false));
|
||||
}
|
||||
}
|
||||
}
|
28
SharpChat/ClientCommands/RankChannelClientCommand.cs
Normal file
28
SharpChat/ClientCommands/RankChannelClientCommand.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using SharpChat.S2CPackets;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class RankChannelClientCommand : ClientCommand {
|
||||
public bool IsMatch(ClientCommandContext ctx) {
|
||||
return ctx.NameEquals("rank")
|
||||
|| ctx.NameEquals("privilege")
|
||||
|| ctx.NameEquals("priv");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(!ctx.User.Can(UserPermissions.SetChannelHierarchy) || ctx.Channel.IsOwner(ctx.User)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
if(ctx.Args.Length < 1 || !int.TryParse(ctx.Args.First(), out int chanHierarchy) || chanHierarchy > ctx.User.Rank) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.INSUFFICIENT_HIERARCHY));
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.Chat.UpdateChannel(ctx.Channel, hierarchy: chanHierarchy);
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_HIERARCHY_CHANGED, false));
|
||||
}
|
||||
}
|
||||
}
|
31
SharpChat/ClientCommands/RemoteAddressClientCommand.cs
Normal file
31
SharpChat/ClientCommands/RemoteAddressClientCommand.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using SharpChat.S2CPackets;
|
||||
using System.Net;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class RemoteAddressClientCommand : ClientCommand {
|
||||
public bool IsMatch(ClientCommandContext ctx) {
|
||||
return ctx.NameEquals("ip")
|
||||
|| ctx.NameEquals("whois");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(!ctx.User.Can(UserPermissions.SeeIPAddress)) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, "/ip"));
|
||||
return;
|
||||
}
|
||||
|
||||
string? ipUserStr = ctx.Args.FirstOrDefault();
|
||||
User? ipUser = null;
|
||||
|
||||
if(string.IsNullOrWhiteSpace(ipUserStr) || (ipUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(ipUserStr))) == null) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_FOUND, true, ipUserStr ?? "User"));
|
||||
return;
|
||||
}
|
||||
|
||||
foreach(IPAddress ip in ctx.Chat.GetRemoteAddresses(ipUser))
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.IP_ADDRESS, false, ipUser.UserName, ip));
|
||||
}
|
||||
}
|
||||
}
|
31
SharpChat/ClientCommands/ShutdownRestartClientCommand.cs
Normal file
31
SharpChat/ClientCommands/ShutdownRestartClientCommand.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using SharpChat.S2CPackets;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class ShutdownRestartClientCommand(ManualResetEvent waitHandle, Func<bool> shutdownCheck) : ClientCommand {
|
||||
private readonly ManualResetEvent WaitHandle = waitHandle ?? throw new ArgumentNullException(nameof(waitHandle));
|
||||
private readonly Func<bool> ShutdownCheck = shutdownCheck ?? throw new ArgumentNullException(nameof(shutdownCheck));
|
||||
|
||||
public bool IsMatch(ClientCommandContext ctx) {
|
||||
return ctx.NameEquals("shutdown")
|
||||
|| ctx.NameEquals("restart");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
if(ctx.User.UserId != 1) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!ShutdownCheck())
|
||||
return;
|
||||
|
||||
if(ctx.NameEquals("restart"))
|
||||
foreach(Connection conn in ctx.Chat.Connections)
|
||||
conn.PrepareForRestart();
|
||||
|
||||
ctx.Chat.Update();
|
||||
WaitHandle?.Set();
|
||||
}
|
||||
}
|
||||
}
|
45
SharpChat/ClientCommands/WhisperClientCommand.cs
Normal file
45
SharpChat/ClientCommands/WhisperClientCommand.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
using SharpChat.Events;
|
||||
using SharpChat.S2CPackets;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class WhisperClientCommand : ClientCommand {
|
||||
public bool IsMatch(ClientCommandContext ctx) {
|
||||
return ctx.NameEquals("whisper")
|
||||
|| ctx.NameEquals("msg");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
|
||||
if(ctx.Args.Length < 2) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.COMMAND_FORMAT_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
string whisperUserStr = ctx.Args.FirstOrDefault() ?? string.Empty;
|
||||
User? whisperUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(whisperUserStr));
|
||||
|
||||
if(whisperUser == null) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USER_NOT_FOUND, true, whisperUserStr));
|
||||
return;
|
||||
}
|
||||
|
||||
if(whisperUser == ctx.User)
|
||||
return;
|
||||
|
||||
ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
||||
msgId,
|
||||
User.GetDMChannelName(ctx.User, whisperUser),
|
||||
ctx.User.UserId,
|
||||
ctx.User.UserName,
|
||||
ctx.User.Colour,
|
||||
ctx.User.Rank,
|
||||
ctx.User.NickName,
|
||||
ctx.User.Permissions,
|
||||
DateTimeOffset.Now,
|
||||
string.Join(' ', ctx.Args.Skip(1)),
|
||||
true, false, false
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
62
SharpChat/ClientCommands/WhoClientCommand.cs
Normal file
62
SharpChat/ClientCommands/WhoClientCommand.cs
Normal file
|
@ -0,0 +1,62 @@
|
|||
using SharpChat.S2CPackets;
|
||||
using System.Text;
|
||||
|
||||
namespace SharpChat.ClientCommands {
|
||||
public class WhoClientCommand : ClientCommand {
|
||||
public bool IsMatch(ClientCommandContext ctx) {
|
||||
return ctx.NameEquals("who");
|
||||
}
|
||||
|
||||
public void Dispatch(ClientCommandContext ctx) {
|
||||
long msgId = ctx.Chat.RandomSnowflake.Next();
|
||||
StringBuilder whoChanSB = new();
|
||||
string? whoChanStr = ctx.Args.FirstOrDefault();
|
||||
|
||||
if(string.IsNullOrEmpty(whoChanStr)) {
|
||||
foreach(User whoUser in ctx.Chat.Users) {
|
||||
whoChanSB.Append(@"<a href=""javascript:void(0);"" onclick=""UI.InsertChatText(this.innerHTML);""");
|
||||
|
||||
if(whoUser == ctx.User)
|
||||
whoChanSB.Append(@" style=""font-weight: bold;""");
|
||||
|
||||
whoChanSB.Append('>');
|
||||
whoChanSB.Append(whoUser.LegacyName);
|
||||
whoChanSB.Append("</a>, ");
|
||||
}
|
||||
|
||||
if(whoChanSB.Length > 2)
|
||||
whoChanSB.Length -= 2;
|
||||
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USERS_LISTING_SERVER, false, whoChanSB));
|
||||
} else {
|
||||
Channel? whoChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(whoChanStr));
|
||||
|
||||
if(whoChan is null) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.CHANNEL_NOT_FOUND, true, whoChanStr));
|
||||
return;
|
||||
}
|
||||
|
||||
if(whoChan.Rank > ctx.User.Rank || (whoChan.HasPassword && !ctx.User.Can(UserPermissions.JoinAnyChannel))) {
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USERS_LISTING_ERROR, true, whoChanStr));
|
||||
return;
|
||||
}
|
||||
|
||||
foreach(User whoUser in ctx.Chat.GetChannelUsers(whoChan)) {
|
||||
whoChanSB.Append(@"<a href=""javascript:void(0);"" onclick=""UI.InsertChatText(this.innerHTML);""");
|
||||
|
||||
if(whoUser == ctx.User)
|
||||
whoChanSB.Append(@" style=""font-weight: bold;""");
|
||||
|
||||
whoChanSB.Append('>');
|
||||
whoChanSB.Append(whoUser.LegacyName);
|
||||
whoChanSB.Append("</a>, ");
|
||||
}
|
||||
|
||||
if(whoChanSB.Length > 2)
|
||||
whoChanSB.Length -= 2;
|
||||
|
||||
ctx.Chat.SendTo(ctx.User, new CommandResponseS2CPacket(msgId, LCR.USERS_LISTING_CHANNEL, false, whoChan.Name, whoChanSB));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue