Drew the rest of the fucking owl.

This commit is contained in:
flash 2024-05-14 22:17:25 +00:00
parent 7bcf5acb7e
commit a6a7e56bd1
52 changed files with 504 additions and 215 deletions

View file

@ -28,7 +28,7 @@ namespace SharpChat {
public void DispatchEvent(IChatEvent eventInfo) { public void DispatchEvent(IChatEvent eventInfo) {
if(eventInfo is MessageCreateEvent mce) { if(eventInfo is MessageCreateEvent mce) {
if(mce.IsBroadcast) { if(mce.IsBroadcast) {
Send(new BroadcastPacket(mce.MessageText)); Send(new MessageBroadcastPacket(mce.MessageText));
} else if(mce.IsPrivate) { } else if(mce.IsPrivate) {
// The channel name returned by GetDMChannelName should not be exposed to the user, instead @<Target User> should be displayed // The channel name returned by GetDMChannelName should not be exposed to the user, instead @<Target User> should be displayed
// e.g. nook sees @Arysil and Arysil sees @nook // e.g. nook sees @Arysil and Arysil sees @nook
@ -47,7 +47,7 @@ namespace SharpChat {
return; return;
foreach(ChatUser user in users) foreach(ChatUser user in users)
SendTo(user, new ChatMessageAddPacket( SendTo(user, new MessageAddPacket(
mce.MessageId, mce.MessageId,
DateTimeOffset.Now, DateTimeOffset.Now,
mce.SenderId, mce.SenderId,
@ -58,7 +58,7 @@ namespace SharpChat {
} else { } else {
ChatChannel? channel = Channels.FirstOrDefault(c => c.NameEquals(mce.ChannelName)); ChatChannel? channel = Channels.FirstOrDefault(c => c.NameEquals(mce.ChannelName));
if(channel != null) if(channel != null)
SendTo(channel, new ChatMessageAddPacket( SendTo(channel, new MessageAddPacket(
mce.MessageId, mce.MessageId,
DateTimeOffset.Now, DateTimeOffset.Now,
mce.SenderId, mce.SenderId,
@ -242,15 +242,15 @@ namespace SharpChat {
chan.Name, chan.Name,
maxMsgLength maxMsgLength
)); ));
conn.Send(new ContextUsersPacket(GetChannelUsers(chan).Except(new[] { user }).Select( conn.Send(new UsersPopulatePacket(GetChannelUsers(chan).Except(new[] { user }).Select(
user => new ContextUsersPacket.ListEntry(user.UserId, user.LegacyNameWithStatus, user.Colour, user.Rank, user.Permissions, true) user => new UsersPopulatePacket.ListEntry(user.UserId, user.LegacyNameWithStatus, user.Colour, user.Rank, user.Permissions, true)
).OrderByDescending(user => user.Rank).ToArray())); ).OrderByDescending(user => user.Rank).ToArray()));
foreach(StoredEventInfo msg in Events.GetChannelEventLog(chan.Name)) foreach(StoredEventInfo msg in Events.GetChannelEventLog(chan.Name))
conn.Send(new ContextMessagePacket(msg)); conn.Send(new MessagePopulatePacket(msg));
conn.Send(new ContextChannelsPacket(Channels.Where(c => c.Rank <= user.Rank).Select( conn.Send(new ChannelsPopulatePacket(Channels.Where(c => c.Rank <= user.Rank).Select(
channel => new ContextChannelsPacket.ListEntry(channel.Name, channel.HasPassword, channel.IsTemporary) channel => new ChannelsPopulatePacket.ListEntry(channel.Name, channel.HasPassword, channel.IsTemporary)
).ToArray())); ).ToArray()));
Users.Add(user); Users.Add(user);
@ -285,13 +285,13 @@ namespace SharpChat {
if(!user.Can(ChatUserPermissions.JoinAnyChannel) && chan.IsOwner(user)) { if(!user.Can(ChatUserPermissions.JoinAnyChannel) && chan.IsOwner(user)) {
if(chan.Rank > user.Rank) { if(chan.Rank > user.Rank) {
SendTo(user, new LegacyCommandResponse(LCR.CHANNEL_INSUFFICIENT_RANK, true, chan.Name)); SendTo(user, new ChannelRankTooLowErrorPacket(chan.Name));
ForceChannel(user); ForceChannel(user);
return; return;
} }
if(!string.IsNullOrEmpty(chan.Password) && chan.Password != password) { if(!string.IsNullOrEmpty(chan.Password) && chan.Password.Equals(password)) {
SendTo(user, new LegacyCommandResponse(LCR.CHANNEL_INVALID_PASSWORD, true, chan.Name)); SendTo(user, new ChannelPasswordWrongErrorPacket(chan.Name));
ForceChannel(user); ForceChannel(user);
return; return;
} }
@ -312,12 +312,12 @@ namespace SharpChat {
Events.AddEvent("chan:join", user, oldChan, flags: StoredEventFlags.Log); Events.AddEvent("chan:join", user, oldChan, flags: StoredEventFlags.Log);
SendTo(user, new ContextClearPacket(ContextClearPacket.ClearMode.MessagesUsers)); SendTo(user, new ContextClearPacket(ContextClearPacket.ClearMode.MessagesUsers));
SendTo(user, new ContextUsersPacket(GetChannelUsers(chan).Except(new[] { user }).Select( SendTo(user, new UsersPopulatePacket(GetChannelUsers(chan).Except(new[] { user }).Select(
user => new ContextUsersPacket.ListEntry(user.UserId, user.LegacyNameWithStatus, user.Colour, user.Rank, user.Permissions, true) user => new UsersPopulatePacket.ListEntry(user.UserId, user.LegacyNameWithStatus, user.Colour, user.Rank, user.Permissions, true)
).OrderByDescending(u => u.Rank).ToArray())); ).OrderByDescending(u => u.Rank).ToArray()));
foreach(StoredEventInfo msg in Events.GetChannelEventLog(chan.Name)) foreach(StoredEventInfo msg in Events.GetChannelEventLog(chan.Name))
SendTo(user, new ContextMessagePacket(msg)); SendTo(user, new MessagePopulatePacket(msg));
ForceChannel(user, chan); ForceChannel(user, chan);

View file

@ -24,7 +24,7 @@ namespace SharpChat.Commands {
} }
Task.Run(async () => { Task.Run(async () => {
ctx.Chat.SendTo(ctx.User, new BanListPacket( ctx.Chat.SendTo(ctx.User, new BanListResponsePacket(
(await Misuzu.GetBanListAsync() ?? Array.Empty<MisuzuBanInfo>()).Select( (await Misuzu.GetBanListAsync() ?? Array.Empty<MisuzuBanInfo>()).Select(
ban => string.IsNullOrEmpty(ban.UserName) ? (string.IsNullOrEmpty(ban.RemoteAddress) ? string.Empty : ban.RemoteAddress) : ban.UserName ban => string.IsNullOrEmpty(ban.UserName) ? (string.IsNullOrEmpty(ban.RemoteAddress) ? string.Empty : ban.RemoteAddress) : ban.UserName
).ToArray() ).ToArray()

View file

@ -2,7 +2,7 @@
using System.Linq; using System.Linq;
namespace SharpChat.Commands { namespace SharpChat.Commands {
public class CreateChannelCommand : IChatCommand { public class ChannelCreateCommand : IChatCommand {
public bool IsMatch(ChatCommandContext ctx) { public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("create"); return ctx.NameEquals("create");
} }
@ -27,19 +27,19 @@ namespace SharpChat.Commands {
createChanHierarchy = 0; createChanHierarchy = 0;
if(createChanHierarchy > ctx.User.Rank) { if(createChanHierarchy > ctx.User.Rank) {
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.INSUFFICIENT_RANK)); ctx.Chat.SendTo(ctx.User, new ChannelRankTooHighErrorPacket());
return; return;
} }
string createChanName = string.Join('_', ctx.Args.Skip(createChanHasHierarchy ? 1 : 0)); string createChanName = string.Join('_', ctx.Args.Skip(createChanHasHierarchy ? 1 : 0));
if(!ChatChannel.CheckName(createChanName)) { if(!ChatChannel.CheckName(createChanName)) {
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_NAME_INVALID)); ctx.Chat.SendTo(ctx.User, new ChannelNameFormatErrorPacket());
return; return;
} }
if(ctx.Chat.Channels.Any(c => c.NameEquals(createChanName))) { if(ctx.Chat.Channels.Any(c => c.NameEquals(createChanName))) {
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_ALREADY_EXISTS, true, createChanName)); ctx.Chat.SendTo(ctx.User, new ChannelNameInUseErrorPacket(createChanName));
return; return;
} }
@ -58,7 +58,7 @@ namespace SharpChat.Commands {
)); ));
ctx.Chat.SwitchChannel(ctx.User, createChan, createChan.Password); ctx.Chat.SwitchChannel(ctx.User, createChan, createChan.Password);
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_CREATED, false, createChan.Name)); ctx.Chat.SendTo(ctx.User, new ChannelCreateResponsePacket(createChan.Name));
} }
} }
} }

View file

@ -2,7 +2,7 @@
using System.Linq; using System.Linq;
namespace SharpChat.Commands { namespace SharpChat.Commands {
public class DeleteChannelCommand : IChatCommand { public class ChannelDeleteCommand : IChatCommand {
public bool IsMatch(ChatCommandContext ctx) { public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("delchan") || ( return ctx.NameEquals("delchan") || (
ctx.NameEquals("delete") ctx.NameEquals("delete")
@ -20,17 +20,17 @@ namespace SharpChat.Commands {
ChatChannel? delChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(delChanName)); ChatChannel? delChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(delChanName));
if(delChan == null) { if(delChan == null) {
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_NOT_FOUND, true, delChanName)); ctx.Chat.SendTo(ctx.User, new ChannelNotFoundErrorPacket(delChanName));
return; return;
} }
if(!ctx.User.Can(ChatUserPermissions.DeleteChannel) && delChan.IsOwner(ctx.User)) { if(!ctx.User.Can(ChatUserPermissions.DeleteChannel) && delChan.IsOwner(ctx.User)) {
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_DELETE_FAILED, true, delChan.Name)); ctx.Chat.SendTo(ctx.User, new ChannelDeleteNotAllowedErrorPacket(delChan.Name));
return; return;
} }
ctx.Chat.RemoveChannel(delChan); ctx.Chat.RemoveChannel(delChan);
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_DELETED, false, delChan.Name)); ctx.Chat.SendTo(ctx.User, new ChannelDeleteResponsePacket(delChan.Name));
} }
} }
} }

View file

@ -2,17 +2,17 @@
using System.Linq; using System.Linq;
namespace SharpChat.Commands { namespace SharpChat.Commands {
public class JoinChannelCommand : IChatCommand { public class ChannelJoinCommand : IChatCommand {
public bool IsMatch(ChatCommandContext ctx) { public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("join"); return ctx.NameEquals("join");
} }
public void Dispatch(ChatCommandContext ctx) { public void Dispatch(ChatCommandContext ctx) {
string? joinChanStr = ctx.Args.FirstOrDefault(); string joinChanStr = ctx.Args.FirstOrDefault() ?? string.Empty;
ChatChannel? joinChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(joinChanStr)); ChatChannel? joinChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(joinChanStr));
if(joinChan == null) { if(joinChan == null) {
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_NOT_FOUND, true, joinChanStr ?? string.Empty)); ctx.Chat.SendTo(ctx.User, new ChannelNotFoundErrorPacket(joinChanStr));
ctx.Chat.ForceChannel(ctx.User); ctx.Chat.ForceChannel(ctx.User);
return; return;
} }

View file

@ -1,7 +1,7 @@
using SharpChat.Packet; using SharpChat.Packet;
namespace SharpChat.Commands { namespace SharpChat.Commands {
public class PasswordChannelCommand : IChatCommand { public class ChannelPasswordCommand : IChatCommand {
public bool IsMatch(ChatCommandContext ctx) { public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("pwd") return ctx.NameEquals("pwd")
|| ctx.NameEquals("password"); || ctx.NameEquals("password");
@ -19,7 +19,7 @@ namespace SharpChat.Commands {
chanPass = string.Empty; chanPass = string.Empty;
ctx.Chat.UpdateChannel(ctx.Channel, password: chanPass); ctx.Chat.UpdateChannel(ctx.Channel, password: chanPass);
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_PASSWORD_CHANGED, false)); ctx.Chat.SendTo(ctx.User, new ChannelPasswordChangedResponsePacket());
} }
} }
} }

View file

@ -2,7 +2,7 @@
using System.Linq; using System.Linq;
namespace SharpChat.Commands { namespace SharpChat.Commands {
public class RankChannelCommand : IChatCommand { public class ChannelRankCommand : IChatCommand {
public bool IsMatch(ChatCommandContext ctx) { public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("rank") return ctx.NameEquals("rank")
|| ctx.NameEquals("privilege") || ctx.NameEquals("privilege")
@ -16,12 +16,12 @@ namespace SharpChat.Commands {
} }
if(!ctx.Args.Any() || !int.TryParse(ctx.Args.First(), out int chanHierarchy) || chanHierarchy > ctx.User.Rank) { if(!ctx.Args.Any() || !int.TryParse(ctx.Args.First(), out int chanHierarchy) || chanHierarchy > ctx.User.Rank) {
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.INSUFFICIENT_RANK)); ctx.Chat.SendTo(ctx.User, new ChannelRankTooHighErrorPacket());
return; return;
} }
ctx.Chat.UpdateChannel(ctx.Channel, minRank: chanHierarchy); ctx.Chat.UpdateChannel(ctx.Channel, minRank: chanHierarchy);
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_RANK_CHANGED, false)); ctx.Chat.SendTo(ctx.User, new ChannelRankChangedResponsePacket());
} }
} }
} }

View file

@ -25,18 +25,18 @@ namespace SharpChat.Commands {
return; return;
} }
string? banUserTarget = ctx.Args.ElementAtOrDefault(0); string banUserTarget = ctx.Args.ElementAtOrDefault(0) ?? string.Empty;
string? banDurationStr = ctx.Args.ElementAtOrDefault(1); string? banDurationStr = ctx.Args.ElementAtOrDefault(1);
int banReasonIndex = 1; int banReasonIndex = 1;
ChatUser? banUser = null; ChatUser? banUser = null;
if(banUserTarget == null || (banUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(banUserTarget))) == null) { if(string.IsNullOrEmpty(banUserTarget) || (banUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(banUserTarget))) == null) {
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, banUserTarget ?? string.Empty)); ctx.Chat.SendTo(ctx.User, new UserNotFoundErrorPacket(banUserTarget));
return; return;
} }
if(!ctx.User.IsSuper && banUser.Rank >= ctx.User.Rank && banUser != ctx.User) { if(!ctx.User.IsSuper && banUser.Rank >= ctx.User.Rank && banUser != ctx.User) {
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.KICK_NOT_ALLOWED, true, banUser.LegacyName)); ctx.Chat.SendTo(ctx.User, new KickBanNotAllowedErrorPacket(banUser.LegacyName));
return; return;
} }
@ -66,7 +66,7 @@ namespace SharpChat.Commands {
MisuzuBanInfo? fbi = await Misuzu.CheckBanAsync(userId, userIp); MisuzuBanInfo? fbi = await Misuzu.CheckBanAsync(userId, userIp);
if(fbi != null && fbi.IsBanned && !fbi.HasExpired) { if(fbi != null && fbi.IsBanned && !fbi.HasExpired) {
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.KICK_NOT_ALLOWED, true, banUser.LegacyName)); ctx.Chat.SendTo(ctx.User, new KickBanNotAllowedErrorPacket(banUser.LegacyName));
return; return;
} }

View file

@ -3,7 +3,7 @@ using System;
using System.Linq; using System.Linq;
namespace SharpChat.Commands { namespace SharpChat.Commands {
public class ActionCommand : IChatCommand { public class MessageActionCommand : IChatCommand {
public bool IsMatch(ChatCommandContext ctx) { public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("action") return ctx.NameEquals("action")
|| ctx.NameEquals("me"); || ctx.NameEquals("me");

View file

@ -3,7 +3,7 @@ using SharpChat.Packet;
using System; using System;
namespace SharpChat.Commands { namespace SharpChat.Commands {
public class BroadcastCommand : IChatCommand { public class MessageBroadcastCommand : IChatCommand {
public bool IsMatch(ChatCommandContext ctx) { public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("say") return ctx.NameEquals("say")
|| ctx.NameEquals("broadcast"); || ctx.NameEquals("broadcast");

View file

@ -4,7 +4,7 @@ using System.Linq;
namespace SharpChat.Commands namespace SharpChat.Commands
{ {
public class DeleteMessageCommand : IChatCommand { public class MessageDeleteCommand : IChatCommand {
public bool IsMatch(ChatCommandContext ctx) { public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("delmsg") || ( return ctx.NameEquals("delmsg") || (
ctx.NameEquals("delete") ctx.NameEquals("delete")
@ -30,12 +30,12 @@ namespace SharpChat.Commands
StoredEventInfo? delMsg = ctx.Chat.Events.GetEvent(delSeqId); StoredEventInfo? delMsg = ctx.Chat.Events.GetEvent(delSeqId);
if(delMsg == null || delMsg.Sender?.Rank > ctx.User.Rank || (!deleteAnyMessage && delMsg.Sender?.UserId != ctx.User.UserId)) { if(delMsg == null || delMsg.Sender?.Rank > ctx.User.Rank || (!deleteAnyMessage && delMsg.Sender?.UserId != ctx.User.UserId)) {
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.MESSAGE_DELETE_ERROR)); ctx.Chat.SendTo(ctx.User, new MessageDeleteNotAllowedErrorPacket());
return; return;
} }
ctx.Chat.Events.RemoveEvent(delMsg); ctx.Chat.Events.RemoveEvent(delMsg);
ctx.Chat.Send(new ChatMessageDeletePacket(delMsg.Id)); ctx.Chat.Send(new MessageDeletePacket(delMsg.Id));
} }
} }
} }

View file

@ -4,7 +4,7 @@ using System;
using System.Linq; using System.Linq;
namespace SharpChat.Commands { namespace SharpChat.Commands {
public class WhisperCommand : IChatCommand { public class MessageWhisperCommand : IChatCommand {
public bool IsMatch(ChatCommandContext ctx) { public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("whisper") return ctx.NameEquals("whisper")
|| ctx.NameEquals("msg"); || ctx.NameEquals("msg");
@ -16,11 +16,11 @@ namespace SharpChat.Commands {
return; return;
} }
string? whisperUserStr = ctx.Args.FirstOrDefault(); string whisperUserStr = ctx.Args.FirstOrDefault() ?? string.Empty;
ChatUser? whisperUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(whisperUserStr)); ChatUser? whisperUser = ctx.Chat.Users.FirstOrDefault(u => u.NameEquals(whisperUserStr));
if(whisperUser == null) { if(whisperUser == null) {
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_FOUND, true, whisperUserStr ?? string.Empty)); ctx.Chat.SendTo(ctx.User, new UserNotFoundErrorPacket(whisperUserStr));
return; return;
} }

View file

@ -36,15 +36,15 @@ namespace SharpChat.Commands {
MisuzuBanInfo? banInfo = await Misuzu.CheckBanAsync(ipAddr: unbanAddrTarget); MisuzuBanInfo? banInfo = await Misuzu.CheckBanAsync(ipAddr: unbanAddrTarget);
if(banInfo?.IsBanned != true || banInfo.HasExpired) { if(banInfo?.IsBanned != true || banInfo.HasExpired) {
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_BANNED, true, unbanAddrTarget)); ctx.Chat.SendTo(ctx.User, new KickBanNoRecordErrorPacket(unbanAddrTarget));
return; return;
} }
bool wasBanned = await Misuzu.RevokeBanAsync(banInfo, MisuzuClient.BanRevokeKind.RemoteAddress); bool wasBanned = await Misuzu.RevokeBanAsync(banInfo, MisuzuClient.BanRevokeKind.RemoteAddress);
if(wasBanned) if(wasBanned)
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_UNBANNED, false, unbanAddrTarget)); ctx.Chat.SendTo(ctx.User, new PardonResponsePacket(unbanAddrTarget));
else else
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_BANNED, true, unbanAddrTarget)); ctx.Chat.SendTo(ctx.User, new KickBanNoRecordErrorPacket(unbanAddrTarget));
}).Wait(); }).Wait();
} }
} }

View file

@ -43,15 +43,15 @@ namespace SharpChat.Commands {
MisuzuBanInfo? banInfo = await Misuzu.CheckBanAsync(unbanUserTarget, userIdIsName: unbanUserTargetIsName); MisuzuBanInfo? banInfo = await Misuzu.CheckBanAsync(unbanUserTarget, userIdIsName: unbanUserTargetIsName);
if(banInfo?.IsBanned != true || banInfo.HasExpired) { if(banInfo?.IsBanned != true || banInfo.HasExpired) {
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_BANNED, true, unbanUserTarget)); ctx.Chat.SendTo(ctx.User, new KickBanNoRecordErrorPacket(unbanUserTarget));
return; return;
} }
bool wasBanned = await Misuzu.RevokeBanAsync(banInfo, MisuzuClient.BanRevokeKind.UserId); bool wasBanned = await Misuzu.RevokeBanAsync(banInfo, MisuzuClient.BanRevokeKind.UserId);
if(wasBanned) if(wasBanned)
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_UNBANNED, false, unbanUserTarget)); ctx.Chat.SendTo(ctx.User, new PardonResponsePacket(unbanUserTarget));
else else
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USER_NOT_BANNED, true, unbanUserTarget)); ctx.Chat.SendTo(ctx.User, new KickBanNoRecordErrorPacket(unbanUserTarget));
}).Wait(); }).Wait();
} }
} }

View file

@ -2,7 +2,7 @@
using System.Linq; using System.Linq;
namespace SharpChat.Commands { namespace SharpChat.Commands {
public class AFKCommand : IChatCommand { public class UserAFKCommand : IChatCommand {
private const string DEFAULT = "AFK"; private const string DEFAULT = "AFK";
private const int MAX_LENGTH = 5; private const int MAX_LENGTH = 5;

View file

@ -2,7 +2,7 @@
using System.Linq; using System.Linq;
namespace SharpChat.Commands { namespace SharpChat.Commands {
public class NickCommand : IChatCommand { public class UserNickCommand : IChatCommand {
public bool IsMatch(ChatCommandContext ctx) { public bool IsMatch(ChatCommandContext ctx) {
return ctx.NameEquals("nick"); return ctx.NameEquals("nick");
} }
@ -43,7 +43,7 @@ namespace SharpChat.Commands {
nickStr = string.Empty; nickStr = string.Empty;
if(!string.IsNullOrWhiteSpace(nickStr) && ctx.Chat.Users.Any(u => u.NameEquals(nickStr))) { if(!string.IsNullOrWhiteSpace(nickStr) && ctx.Chat.Users.Any(u => u.NameEquals(nickStr))) {
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.NAME_IN_USE, true, nickStr)); ctx.Chat.SendTo(ctx.User, new UserNameInUseErrorPacket(nickStr));
return; return;
} }

View file

@ -1,6 +1,5 @@
using SharpChat.Packet; using SharpChat.Packet;
using System.Linq; using System.Linq;
using System.Text;
namespace SharpChat.Commands { namespace SharpChat.Commands {
public class WhoCommand : IChatCommand { public class WhoCommand : IChatCommand {
@ -9,50 +8,33 @@ namespace SharpChat.Commands {
} }
public void Dispatch(ChatCommandContext ctx) { public void Dispatch(ChatCommandContext ctx) {
StringBuilder whoChanSB = new(); string? channelName = ctx.Args.FirstOrDefault();
string? whoChanStr = ctx.Args.FirstOrDefault();
if(string.IsNullOrEmpty(whoChanStr)) { if(string.IsNullOrEmpty(channelName)) {
foreach(ChatUser whoUser in ctx.Chat.Users) { ctx.Chat.SendTo(ctx.User, new WhoServerResponsePacket(
whoChanSB.Append(@"<a href=""javascript:void(0);"" onclick=""UI.InsertChatText(this.innerHTML);"""); ctx.Chat.Users.Select(u => u.LegacyName).ToArray(),
ctx.User.LegacyName
if(whoUser == ctx.User) ));
whoChanSB.Append(@" style=""font-weight: bold;"""); return;
whoChanSB.AppendFormat(@">{0}</a>, ", whoUser.LegacyName);
}
if(whoChanSB.Length > 2)
whoChanSB.Length -= 2;
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USERS_LISTING_SERVER, false, whoChanSB));
} else {
ChatChannel? whoChan = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(whoChanStr));
if(whoChan == null) {
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_NOT_FOUND, true, whoChanStr));
return;
}
if(whoChan.Rank > ctx.User.Rank || (whoChan.HasPassword && !ctx.User.Can(ChatUserPermissions.JoinAnyChannel))) {
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USERS_LISTING_ERROR, true, whoChanStr));
return;
}
foreach(ChatUser 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.AppendFormat(@">{0}</a>, ", whoUser.LegacyName);
}
if(whoChanSB.Length > 2)
whoChanSB.Length -= 2;
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.USERS_LISTING_CHANNEL, false, whoChan.Name, whoChanSB));
} }
ChatChannel? channel = ctx.Chat.Channels.FirstOrDefault(c => c.NameEquals(channelName));
if(channel == null) {
ctx.Chat.SendTo(ctx.User, new ChannelNotFoundErrorPacket(channelName));
return;
}
if(channel.Rank > ctx.User.Rank || (channel.HasPassword && !ctx.User.Can(ChatUserPermissions.JoinAnyChannel))) {
ctx.Chat.SendTo(ctx.User, new WhoChannelNotFoundErrorPacket(channelName));
return;
}
ctx.Chat.SendTo(ctx.User, new WhoChannelResponsePacket(
channel.Name,
ctx.Chat.GetChannelUsers(channel).Select(user => user.LegacyName).ToArray(),
ctx.User.LegacyName
));
} }
} }
} }

View file

@ -15,11 +15,11 @@ namespace SharpChat.Commands {
return; return;
} }
string? ipUserStr = ctx.Args.FirstOrDefault(); string ipUserStr = ctx.Args.FirstOrDefault() ?? string.Empty;
ChatUser? ipUser; ChatUser? ipUser;
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 LegacyCommandResponse(LCR.USER_NOT_FOUND, true, ipUserStr ?? string.Empty)); ctx.Chat.SendTo(ctx.User, new UserNotFoundErrorPacket(ipUserStr));
return; return;
} }

View file

@ -10,7 +10,7 @@ namespace SharpChat.Packet {
} }
private readonly FailReason Reason; private readonly FailReason Reason;
private readonly DateTimeOffset Expires; private readonly long Expires;
public AuthFailPacket(FailReason reason) { public AuthFailPacket(FailReason reason) {
Reason = reason; Reason = reason;
@ -18,7 +18,7 @@ namespace SharpChat.Packet {
public AuthFailPacket(DateTimeOffset expires) { public AuthFailPacket(DateTimeOffset expires) {
Reason = FailReason.Banned; Reason = FailReason.Banned;
Expires = expires; Expires = expires.Year >= 2100 ? -1 : expires.ToUnixTimeSeconds();
} }
public override string Pack() { public override string Pack() {
@ -30,7 +30,7 @@ namespace SharpChat.Packet {
}); });
if(Reason == FailReason.Banned) if(Reason == FailReason.Banned)
packet += string.Format("\t{0}", Expires.Year >= 2100 ? -1 : Expires.ToUnixTimeSeconds()); packet += string.Format("\t{0}", Expires);
return packet; return packet;
} }

View file

@ -1,24 +1,25 @@
using System; using System;
using System.Linq;
using System.Text; using System.Text;
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class BanListPacket : ServerPacket { public class BanListResponsePacket : ServerPacket {
private readonly long Timestamp;
private readonly string[] Bans; private readonly string[] Bans;
public BanListPacket(string[] bans) { public BanListResponsePacket(string[] bans) {
Bans = bans ?? throw new ArgumentNullException(nameof(bans)); Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
Bans = bans;
} }
public override string Pack() { public override string Pack() {
StringBuilder sb = new(); StringBuilder sb = new();
sb.AppendFormat("2\t{0}\t-1\t0\fbanlist\f", DateTimeOffset.Now.ToUnixTimeSeconds()); sb.AppendFormat("2\t{0}\t-1\t0\fbanlist\f", Timestamp);
foreach(string ban in Bans) foreach(string ban in Bans)
sb.AppendFormat(@"<a href=""javascript:void(0);"" onclick=""Chat.SendMessageWrapper('/unban '+ this.innerHTML);"">{0}</a>, ", ban); sb.AppendFormat(@"<a href=""javascript:void(0);"" onclick=""Chat.SendMessageWrapper('/unban '+ this.innerHTML);"">{0}</a>, ", ban);
if(Bans.Any()) if(Bans.Length > 0)
sb.Length -= 2; sb.Length -= 2;
sb.AppendFormat("\t{0}\t10010", SequenceId); sb.AppendFormat("\t{0}\t10010", SequenceId);

View file

@ -0,0 +1,17 @@
using System;
namespace SharpChat.Packet {
public class ChannelCreateResponsePacket : ServerPacket {
private readonly long Timestamp;
private readonly string ChannelName;
public ChannelCreateResponsePacket(string channelName) {
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
ChannelName = channelName;
}
public override string Pack() {
return string.Format("2\t{0}\t-1\t0\fcrchan\f{1}\t{2}\t10010", Timestamp, ChannelName, SequenceId);
}
}
}

View file

@ -0,0 +1,17 @@
using System;
namespace SharpChat.Packet {
public class ChannelDeleteNotAllowedErrorPacket : ServerPacket {
private readonly long Timestamp;
private readonly string ChannelName;
public ChannelDeleteNotAllowedErrorPacket(string channelName) {
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
ChannelName = channelName;
}
public override string Pack() {
return string.Format("2\t{0}\t-1\t1\fndchan\f{1}\t{2}\t10010", Timestamp, ChannelName, SequenceId);
}
}
}

View file

@ -5,7 +5,7 @@ namespace SharpChat.Packet {
private readonly string ChannelName; private readonly string ChannelName;
public ChannelDeletePacket(string channelName) { public ChannelDeletePacket(string channelName) {
ChannelName = channelName ?? throw new ArgumentNullException(nameof(channelName)); ChannelName = channelName;
} }
public override string Pack() { public override string Pack() {

View file

@ -0,0 +1,17 @@
using System;
namespace SharpChat.Packet {
public class ChannelDeleteResponsePacket : ServerPacket {
private readonly long Timestamp;
private readonly string ChannelName;
public ChannelDeleteResponsePacket(string channelName) {
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
ChannelName = channelName;
}
public override string Pack() {
return string.Format("2\t{0}\t-1\t0\fdelchan\f{1}\t{2}\t10010", Timestamp, ChannelName, SequenceId);
}
}
}

View file

@ -0,0 +1,15 @@
using System;
namespace SharpChat.Packet {
public class ChannelNameFormatErrorPacket : ServerPacket {
private readonly long Timestamp;
public ChannelNameFormatErrorPacket() {
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
}
public override string Pack() {
return string.Format("2\t{0}\t-1\t1\finchan\t{1}\t10010", Timestamp, SequenceId);
}
}
}

View file

@ -0,0 +1,17 @@
using System;
namespace SharpChat.Packet {
public class ChannelNameInUseErrorPacket : ServerPacket {
private readonly long Timestamp;
private readonly string ChannelName;
public ChannelNameInUseErrorPacket(string channelName) {
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
ChannelName = channelName;
}
public override string Pack() {
return string.Format("2\t{0}\t-1\t1\fnischan\f{1}\t{2}\t10010", Timestamp, ChannelName, SequenceId);
}
}
}

View file

@ -0,0 +1,17 @@
using System;
namespace SharpChat.Packet {
public class ChannelNotFoundErrorPacket : ServerPacket {
private readonly long Timestamp;
private readonly string ChannelName;
public ChannelNotFoundErrorPacket(string channelName) {
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
ChannelName = channelName;
}
public override string Pack() {
return string.Format("2\t{0}\t-1\t1\fnochan\f{1}\t{2}\t10010", Timestamp, ChannelName, SequenceId);
}
}
}

View file

@ -0,0 +1,15 @@
using System;
namespace SharpChat.Packet {
public class ChannelPasswordChangedResponsePacket : ServerPacket {
private readonly long Timestamp;
public ChannelPasswordChangedResponsePacket() {
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
}
public override string Pack() {
return string.Format("2\t{0}\t-1\t0\fcpwdchan\t{1}\t10010", Timestamp, SequenceId);
}
}
}

View file

@ -0,0 +1,17 @@
using System;
namespace SharpChat.Packet {
public class ChannelPasswordWrongErrorPacket : ServerPacket {
private readonly long Timestamp;
private readonly string ChannelName;
public ChannelPasswordWrongErrorPacket(string channelName) {
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
ChannelName = channelName;
}
public override string Pack() {
return string.Format("2\t{0}\t-1\t1\fipwchan\f{1}\t{2}\t10010", Timestamp, ChannelName, SequenceId);
}
}
}

View file

@ -0,0 +1,15 @@
using System;
namespace SharpChat.Packet {
public class ChannelRankChangedResponsePacket : ServerPacket {
private readonly long Timestamp;
public ChannelRankChangedResponsePacket() {
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
}
public override string Pack() {
return string.Format("2\t{0}\t-1\t0\fcprivchan\t{1}\t10010", Timestamp, SequenceId);
}
}
}

View file

@ -0,0 +1,15 @@
using System;
namespace SharpChat.Packet {
public class ChannelRankTooHighErrorPacket : ServerPacket {
private readonly long Timestamp;
public ChannelRankTooHighErrorPacket() {
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
}
public override string Pack() {
return string.Format("2\t{0}\t-1\t1\frankerr\t{1}\t10010", Timestamp, SequenceId);
}
}
}

View file

@ -0,0 +1,17 @@
using System;
namespace SharpChat.Packet {
public class ChannelRankTooLowErrorPacket : ServerPacket {
private readonly long Timestamp;
private readonly string ChannelName;
public ChannelRankTooLowErrorPacket(string channelName) {
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
ChannelName = channelName;
}
public override string Pack() {
return string.Format("2\t{0}\t-1\t1\fipchan\f{1}\t{2}\t10010", Timestamp, ChannelName, SequenceId);
}
}
}

View file

@ -2,12 +2,12 @@
using System.Text; using System.Text;
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class ContextChannelsPacket : ServerPacket { public class ChannelsPopulatePacket : ServerPacket {
public record ListEntry(string Name, bool HasPassword, bool IsTemporary); public record ListEntry(string Name, bool HasPassword, bool IsTemporary);
private readonly ListEntry[] Entries; private readonly ListEntry[] Entries;
public ContextChannelsPacket(ListEntry[] entries) { public ChannelsPopulatePacket(ListEntry[] entries) {
Entries = entries ?? throw new ArgumentNullException(nameof(entries)); Entries = entries ?? throw new ArgumentNullException(nameof(entries));
} }

View file

@ -2,22 +2,17 @@
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class ForceDisconnectPacket : ServerPacket { public class ForceDisconnectPacket : ServerPacket {
private readonly DateTimeOffset Expires; private readonly long Expires;
public ForceDisconnectPacket() { public ForceDisconnectPacket() {}
Expires = DateTimeOffset.MinValue;
}
public ForceDisconnectPacket(DateTimeOffset expires) { public ForceDisconnectPacket(DateTimeOffset expires) {
Expires = expires; Expires = expires.Year >= 2100 ? -1 : expires.ToUnixTimeSeconds();
} }
public override string Pack() { public override string Pack() {
if(Expires == DateTimeOffset.MinValue) if(Expires != 0)
return string.Format( return string.Format("9\t1\t{0}", Expires);
"9\t1\t{0}",
Expires.Year >= 2100 ? -1 : Expires.ToUnixTimeSeconds()
);
return "9\t0"; return "9\t0";
} }

View file

@ -0,0 +1,17 @@
using System;
namespace SharpChat.Packet {
public class KickBanNoRecordErrorPacket : ServerPacket {
private readonly long Timestamp;
private readonly string TargetName;
public KickBanNoRecordErrorPacket(string targetName) {
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
TargetName = targetName;
}
public override string Pack() {
return string.Format("2\t{0}\t-1\t1\fnotban\f{1}\t{2}\t10010", Timestamp, TargetName, SequenceId);
}
}
}

View file

@ -0,0 +1,17 @@
using System;
namespace SharpChat.Packet {
public class KickBanNotAllowedErrorPacket : ServerPacket {
private readonly long Timestamp;
private readonly string UserName;
public KickBanNotAllowedErrorPacket(string userName) {
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
UserName = userName;
}
public override string Pack() {
return string.Format("2\t{0}\t-1\t1\fkickna\f{1}\t{2}\t10010", Timestamp, UserName, SequenceId);
}
}
}

View file

@ -1,63 +0,0 @@
using System;
using System.Text;
namespace SharpChat.Packet {
public class LegacyCommandResponse : ServerPacket {
private readonly bool IsError;
private readonly string StringId;
private readonly object[] Arguments;
public LegacyCommandResponse(
string stringId,
bool isError = true,
params object[] args
) {
IsError = isError;
StringId = stringId;
Arguments = args;
}
public override string Pack() {
StringBuilder sb = new();
sb.AppendFormat(
"2\t{0}\t-1\t{1}\f{2}",
DateTimeOffset.Now.ToUnixTimeSeconds(),
IsError ? 1 : 0,
StringId
);
foreach(object arg in Arguments)
sb.AppendFormat("\f{0}", arg);
sb.AppendFormat("\t{0}\t10010", SequenceId);
return sb.ToString();
}
}
// Abbreviated class name because otherwise shit gets wide
public static class LCR {
public const string CHANNEL_CREATED = "crchan";
public const string CHANNEL_DELETED = "delchan";
public const string CHANNEL_PASSWORD_CHANGED = "cpwdchan";
public const string CHANNEL_RANK_CHANGED = "cprivchan";
public const string USERS_LISTING_CHANNEL = "whochan";
public const string USERS_LISTING_SERVER = "who";
public const string USER_UNBANNED = "unban";
public const string USER_NOT_FOUND = "usernf";
public const string NAME_IN_USE = "nameinuse";
public const string CHANNEL_INSUFFICIENT_RANK = "ipchan";
public const string CHANNEL_INVALID_PASSWORD = "ipwchan";
public const string CHANNEL_NOT_FOUND = "nochan";
public const string CHANNEL_ALREADY_EXISTS = "nischan";
public const string CHANNEL_NAME_INVALID = "inchan";
public const string CHANNEL_DELETE_FAILED = "ndchan";
public const string USERS_LISTING_ERROR = "whoerr";
public const string INSUFFICIENT_RANK = "rankerr";
public const string MESSAGE_DELETE_ERROR = "delerr";
public const string KICK_NOT_ALLOWED = "kickna";
public const string USER_NOT_BANNED = "notban";
}
}

View file

@ -1,14 +1,14 @@
using System; using System;
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class ChatMessageAddPacket : ServerPacket { public class MessageAddPacket : ServerPacket {
private readonly DateTimeOffset Created; private readonly long Created;
private readonly long UserId; private readonly long UserId;
private readonly string Body; private readonly string Body;
private readonly bool IsAction; private readonly bool IsAction;
private readonly bool IsPrivate; private readonly bool IsPrivate;
public ChatMessageAddPacket( public MessageAddPacket(
long msgId, long msgId,
DateTimeOffset created, DateTimeOffset created,
long userId, long userId,
@ -16,7 +16,7 @@ namespace SharpChat.Packet {
bool isAction, bool isAction,
bool isPrivate bool isPrivate
) : base(msgId) { ) : base(msgId) {
Created = created; Created = created.ToUnixTimeSeconds();
UserId = userId < 0 ? -1 : userId; UserId = userId < 0 ? -1 : userId;
Body = body ?? throw new ArgumentNullException(nameof(body)); Body = body ?? throw new ArgumentNullException(nameof(body));
IsAction = isAction; IsAction = isAction;
@ -30,7 +30,7 @@ namespace SharpChat.Packet {
return string.Format( return string.Format(
"2\t{0}\t{1}\t{2}\t{3}\t{4}{5}{6}{7}{8}", "2\t{0}\t{1}\t{2}\t{3}\t{4}{5}{6}{7}{8}",
Created.ToUnixTimeSeconds(), Created,
UserId, UserId,
body, body,
SequenceId, SequenceId,

View file

@ -1,11 +1,11 @@
using System; using System;
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class BroadcastPacket : ServerPacket { public class MessageBroadcastPacket : ServerPacket {
private readonly long Timestamp; private readonly long Timestamp;
private readonly string Body; private readonly string Body;
public BroadcastPacket(string body) { public MessageBroadcastPacket(string body) {
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
Body = body; Body = body;
} }

View file

@ -0,0 +1,15 @@
using System;
namespace SharpChat.Packet {
public class MessageDeleteNotAllowedErrorPacket : ServerPacket {
private readonly long Timestamp;
public MessageDeleteNotAllowedErrorPacket() {
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
}
public override string Pack() {
return string.Format("2\t{0}\t-1\t1\fdelerr\t{1}\t10010", Timestamp, SequenceId);
}
}
}

View file

@ -1,8 +1,8 @@
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class ChatMessageDeletePacket : ServerPacket { public class MessageDeletePacket : ServerPacket {
private readonly long MessageId; private readonly long MessageId;
public ChatMessageDeletePacket(long msgId) { public MessageDeletePacket(long msgId) {
MessageId = msgId; MessageId = msgId;
} }

View file

@ -4,11 +4,11 @@ using System.Text;
namespace SharpChat.Packet { namespace SharpChat.Packet {
// this entire class is disgusting // this entire class is disgusting
public class ContextMessagePacket : ServerPacket { public class MessagePopulatePacket : ServerPacket {
private readonly StoredEventInfo Event; private readonly StoredEventInfo Event;
private readonly bool Notify; private readonly bool Notify;
public ContextMessagePacket(StoredEventInfo evt, bool notify = false) { public MessagePopulatePacket(StoredEventInfo evt, bool notify = false) {
Event = evt ?? throw new ArgumentNullException(nameof(evt)); Event = evt ?? throw new ArgumentNullException(nameof(evt));
Notify = notify; Notify = notify;
} }

View file

@ -0,0 +1,17 @@
using System;
namespace SharpChat.Packet {
public class PardonResponsePacket : ServerPacket {
private readonly long Timestamp;
private readonly string Subject;
public PardonResponsePacket(string subject) {
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
Subject = subject;
}
public override string Pack() {
return string.Format("2\t{0}\t-1\t0\funban\f{1}\t{2}\t10010", Timestamp, Subject, SequenceId);
}
}
}

View file

@ -9,18 +9,18 @@ namespace SharpChat.Packet {
} }
public class UserDisconnectPacket : ServerPacket { public class UserDisconnectPacket : ServerPacket {
private readonly DateTimeOffset Disconnected; private readonly long Timestamp;
private readonly long UserId; private readonly long UserId;
private readonly string UserName; private readonly string UserName;
private readonly UserDisconnectReason Reason; private readonly UserDisconnectReason Reason;
public UserDisconnectPacket( public UserDisconnectPacket(
DateTimeOffset disconnected, DateTimeOffset timestamp,
long userId, long userId,
string userName, string userName,
UserDisconnectReason reason UserDisconnectReason reason
) { ) {
Disconnected = disconnected; Timestamp = timestamp.ToUnixTimeSeconds();
UserId = userId; UserId = userId;
UserName = userName ?? throw new ArgumentNullException(nameof(userName)); UserName = userName ?? throw new ArgumentNullException(nameof(userName));
Reason = reason; Reason = reason;
@ -38,7 +38,7 @@ namespace SharpChat.Packet {
UserDisconnectReason.Flood => "flood", UserDisconnectReason.Flood => "flood",
_ => "leave", _ => "leave",
}, },
Disconnected.ToUnixTimeSeconds(), Timestamp,
SequenceId SequenceId
); );
} }

View file

@ -0,0 +1,17 @@
using System;
namespace SharpChat.Packet {
public class UserNameInUseErrorPacket : ServerPacket {
private readonly long Timestamp;
private readonly string UserName;
public UserNameInUseErrorPacket(string userName) {
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
UserName = userName;
}
public override string Pack() {
return string.Format("2\t{0}\t-1\t1\fnameinuse\f{1}\t{2}\t10010", Timestamp, UserName, SequenceId);
}
}
}

View file

@ -0,0 +1,17 @@
using System;
namespace SharpChat.Packet {
public class UserNotFoundErrorPacket : ServerPacket {
private readonly long Timestamp;
private readonly string UserName;
public UserNotFoundErrorPacket(string userName) {
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
UserName = userName;
}
public override string Pack() {
return string.Format("2\t{0}\t-1\t1\fusernf\f{1}\t{2}\t10010", Timestamp, UserName, SequenceId);
}
}
}

View file

@ -4,18 +4,18 @@ namespace SharpChat.Packet {
public class UserUpdateNotificationPacket : ServerPacket { public class UserUpdateNotificationPacket : ServerPacket {
private readonly string PreviousName; private readonly string PreviousName;
private readonly string NewName; private readonly string NewName;
private readonly DateTimeOffset Timestamp; private readonly long Timestamp;
public UserUpdateNotificationPacket(string previousName, string newName) { public UserUpdateNotificationPacket(string previousName, string newName) {
PreviousName = previousName ?? throw new ArgumentNullException(nameof(previousName)); PreviousName = previousName ?? throw new ArgumentNullException(nameof(previousName));
NewName = newName ?? throw new ArgumentNullException(nameof(newName)); NewName = newName ?? throw new ArgumentNullException(nameof(newName));
Timestamp = DateTimeOffset.Now; Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
} }
public override string Pack() { public override string Pack() {
return string.Format( return string.Format(
"2\t{0}\t-1\t0\fnick\f{1}\f{2}\t{3}\t10010", "2\t{0}\t-1\t0\fnick\f{1}\f{2}\t{3}\t10010",
Timestamp.ToUnixTimeSeconds(), Timestamp,
PreviousName, PreviousName,
NewName, NewName,
SequenceId SequenceId

View file

@ -2,12 +2,12 @@
using System.Text; using System.Text;
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class ContextUsersPacket : ServerPacket { public class UsersPopulatePacket : ServerPacket {
public record ListEntry(long Id, string Name, ChatColour Colour, int Rank, ChatUserPermissions Perms, bool Visible); public record ListEntry(long Id, string Name, ChatColour Colour, int Rank, ChatUserPermissions Perms, bool Visible);
private readonly ListEntry[] Entries; private readonly ListEntry[] Entries;
public ContextUsersPacket(ListEntry[] entries) { public UsersPopulatePacket(ListEntry[] entries) {
Entries = entries ?? throw new ArgumentNullException(nameof(entries)); Entries = entries ?? throw new ArgumentNullException(nameof(entries));
} }

View file

@ -0,0 +1,17 @@
using System;
namespace SharpChat.Packet {
public class WhoChannelNotFoundErrorPacket : ServerPacket {
private readonly long Timestamp;
private readonly string ChannelName;
public WhoChannelNotFoundErrorPacket(string channelName) {
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
ChannelName = channelName;
}
public override string Pack() {
return string.Format("2\t{0}\t-1\t1\fwhoerr\f{1}\t{2}\t10010", Timestamp, ChannelName, SequenceId);
}
}
}

View file

@ -0,0 +1,40 @@
using System;
using System.Text;
namespace SharpChat.Packet {
public class WhoChannelResponsePacket : ServerPacket {
private readonly long Timestamp;
private readonly string ChannelName;
private readonly string[] Users;
private readonly string SelfName;
public WhoChannelResponsePacket(string channelName, string[] users, string selfName) {
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
ChannelName = channelName;
Users = users;
SelfName = selfName;
}
public override string Pack() {
StringBuilder sb = new();
sb.AppendFormat("2\t{0}\t-1\t0\fwhochan\f{1}\f", Timestamp, ChannelName);
foreach(string userName in Users) {
sb.Append(@"<a href=""javascript:void(0);"" onclick=""UI.InsertChatText(this.innerHTML);""");
if(userName.Equals(SelfName, StringComparison.InvariantCultureIgnoreCase))
sb.Append(@" style=""font-weight: bold;""");
sb.AppendFormat(@">{0}</a>, ", userName);
}
if(Users.Length > 0)
sb.Length -= 2;
sb.AppendFormat("\t{0}\t10010", SequenceId);
return sb.ToString();
}
}
}

View file

@ -0,0 +1,38 @@
using System;
using System.Text;
namespace SharpChat.Packet {
public class WhoServerResponsePacket : ServerPacket {
private readonly long Timestamp;
private readonly string[] Users;
private readonly string SelfName;
public WhoServerResponsePacket(string[] users, string selfName) {
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
Users = users;
SelfName = selfName;
}
public override string Pack() {
StringBuilder sb = new();
sb.AppendFormat("2\t{0}\t-1\t0\fwho\f", Timestamp);
foreach(string userName in Users) {
sb.Append(@"<a href=""javascript:void(0);"" onclick=""UI.InsertChatText(this.innerHTML);""");
if(userName.Equals(SelfName, StringComparison.InvariantCultureIgnoreCase))
sb.Append(@" style=""font-weight: bold;""");
sb.AppendFormat(@">{0}</a>, ", userName);
}
if(Users.Length > 0)
sb.Length -= 2;
sb.AppendFormat("\t{0}\t10010", SequenceId);
return sb.ToString();
}
}
}

View file

@ -83,18 +83,18 @@ namespace SharpChat {
}); });
SendMessageHandler.AddCommands(new IChatCommand[] { SendMessageHandler.AddCommands(new IChatCommand[] {
new AFKCommand(), new UserAFKCommand(),
new NickCommand(), new UserNickCommand(),
new WhisperCommand(), new MessageWhisperCommand(),
new ActionCommand(), new MessageActionCommand(),
new WhoCommand(), new WhoCommand(),
new JoinChannelCommand(), new ChannelJoinCommand(),
new CreateChannelCommand(), new ChannelCreateCommand(),
new DeleteChannelCommand(), new ChannelDeleteCommand(),
new PasswordChannelCommand(), new ChannelPasswordCommand(),
new RankChannelCommand(), new ChannelRankCommand(),
new BroadcastCommand(), new MessageBroadcastCommand(),
new DeleteMessageCommand(), new MessageDeleteCommand(),
new KickBanCommand(msz), new KickBanCommand(msz),
new PardonUserCommand(msz), new PardonUserCommand(msz),
new PardonAddressCommand(msz), new PardonAddressCommand(msz),