Improved user updating but also other things that were already local.
This commit is contained in:
parent
8de3ba8dbb
commit
82973f7a33
27 changed files with 264 additions and 167 deletions
|
@ -4,19 +4,35 @@ using System.Text;
|
|||
|
||||
namespace SharpChat {
|
||||
public class ChatChannel {
|
||||
public string Name { get; set; }
|
||||
public string Password { get; set; } = string.Empty;
|
||||
public bool IsTemporary { get; set; } = false;
|
||||
public int Rank { get; set; } = 0;
|
||||
public ChatUser Owner { get; set; } = null;
|
||||
public string Name { get; }
|
||||
public string Password { get; set; }
|
||||
public bool IsTemporary { get; set; }
|
||||
public int Rank { get; set; }
|
||||
public long OwnerId { get; set; }
|
||||
|
||||
public bool HasPassword
|
||||
=> !string.IsNullOrWhiteSpace(Password);
|
||||
|
||||
public ChatChannel() { }
|
||||
public ChatChannel(
|
||||
ChatUser owner,
|
||||
string name,
|
||||
string password = null,
|
||||
bool isTemporary = false,
|
||||
int rank = 0
|
||||
) : this(name, password, isTemporary, rank, owner?.UserId ?? 0) {}
|
||||
|
||||
public ChatChannel(string name) {
|
||||
public ChatChannel(
|
||||
string name,
|
||||
string password = null,
|
||||
bool isTemporary = false,
|
||||
int rank = 0,
|
||||
long ownerId = 0
|
||||
) {
|
||||
Name = name;
|
||||
Password = password ?? string.Empty;
|
||||
IsTemporary = isTemporary;
|
||||
Rank = rank;
|
||||
OwnerId = ownerId;
|
||||
}
|
||||
|
||||
public string Pack() {
|
||||
|
@ -35,6 +51,12 @@ namespace SharpChat {
|
|||
return string.Equals(name, Name, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
public bool IsOwner(ChatUser user) {
|
||||
return OwnerId > 0
|
||||
&& user != null
|
||||
&& OwnerId == user.UserId;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
return Name.GetHashCode();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
namespace SharpChat {
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace SharpChat {
|
||||
public struct ChatColour {
|
||||
public byte Red { get; }
|
||||
public byte Green { get; }
|
||||
|
@ -21,6 +23,21 @@
|
|||
Inherits = false;
|
||||
}
|
||||
|
||||
public override bool Equals([NotNullWhen(true)] object obj) {
|
||||
return obj is ChatColour colour && Equals(colour);
|
||||
}
|
||||
|
||||
public bool Equals(ChatColour other) {
|
||||
return Red == other.Red
|
||||
&& Green == other.Green
|
||||
&& Blue == other.Blue
|
||||
&& Inherits == other.Inherits;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
return ToMisuzu();
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return Inherits
|
||||
? "inherit"
|
||||
|
@ -50,5 +67,13 @@
|
|||
? None
|
||||
: FromRawRGB(raw);
|
||||
}
|
||||
|
||||
public static bool operator ==(ChatColour left, ChatColour right) {
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(ChatColour left, ChatColour right) {
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace SharpChat {
|
|||
public IEventStorage Events { get; }
|
||||
public HashSet<ChannelUserAssoc> ChannelUsers { get; } = new();
|
||||
public Dictionary<long, RateLimiter> UserRateLimiters { get; } = new();
|
||||
public Dictionary<long, ChatChannel> UserLastChannel { get; } = new();
|
||||
|
||||
public ChatContext(IEventStorage evtStore) {
|
||||
Events = evtStore ?? throw new ArgumentNullException(nameof(evtStore));
|
||||
|
@ -71,6 +72,65 @@ namespace SharpChat {
|
|||
return Users.Where(u => ids.Contains(u.UserId)).ToArray();
|
||||
}
|
||||
|
||||
public void UpdateUser(
|
||||
ChatUser user,
|
||||
string userName = null,
|
||||
string nickName = null,
|
||||
ChatColour? colour = null,
|
||||
ChatUserStatus? status = null,
|
||||
string statusText = null,
|
||||
int? rank = null,
|
||||
ChatUserPermissions? perms = null,
|
||||
bool silent = false
|
||||
) {
|
||||
if(user == null)
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
|
||||
bool hasChanged = false;
|
||||
string previousName = null;
|
||||
|
||||
if(userName != null && !user.UserName.Equals(userName)) {
|
||||
user.UserName = userName;
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
if(nickName != null && !user.NickName.Equals(nickName)) {
|
||||
if(!silent)
|
||||
previousName = string.IsNullOrWhiteSpace(user.NickName) ? user.UserName : user.NickName;
|
||||
|
||||
user.NickName = nickName;
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
if(colour.HasValue && user.Colour != colour.Value) {
|
||||
user.Colour = colour.Value;
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
if(status.HasValue && user.Status != status.Value) {
|
||||
user.Status = status.Value;
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
if(statusText != null && !user.StatusText.Equals(statusText)) {
|
||||
user.StatusText = statusText;
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
if(rank != null && user.Rank != rank) {
|
||||
user.Rank = (int)rank;
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
if(perms.HasValue && user.Permissions != perms) {
|
||||
user.Permissions = perms.Value;
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
if(hasChanged)
|
||||
SendToUserChannels(user, new UserUpdatePacket(user, previousName));
|
||||
}
|
||||
|
||||
public void BanUser(ChatUser user, TimeSpan duration, UserDisconnectReason reason = UserDisconnectReason.Kicked) {
|
||||
if(duration > TimeSpan.Zero)
|
||||
SendTo(user, new ForceDisconnectPacket(ForceDisconnectReason.Banned, DateTimeOffset.Now + duration));
|
||||
|
@ -102,12 +162,13 @@ namespace SharpChat {
|
|||
Users.Add(user);
|
||||
|
||||
ChannelUsers.Add(new ChannelUserAssoc(user.UserId, chan.Name));
|
||||
user.CurrentChannel = chan;
|
||||
UserLastChannel[user.UserId] = chan;
|
||||
}
|
||||
|
||||
public void HandleDisconnect(ChatUser user, UserDisconnectReason reason = UserDisconnectReason.Leave) {
|
||||
user.Status = ChatUserStatus.Offline;
|
||||
UpdateUser(user, status: ChatUserStatus.Offline);
|
||||
Users.Remove(user);
|
||||
UserLastChannel.Remove(user.UserId);
|
||||
|
||||
ChatChannel[] channels = GetUserChannels(user);
|
||||
|
||||
|
@ -117,25 +178,25 @@ namespace SharpChat {
|
|||
SendTo(chan, new UserDisconnectPacket(DateTimeOffset.Now, user, reason));
|
||||
Events.AddEvent(new UserDisconnectEvent(DateTimeOffset.Now, user, chan, reason));
|
||||
|
||||
if(chan.IsTemporary && chan.Owner == user)
|
||||
if(chan.IsTemporary && chan.IsOwner(user))
|
||||
RemoveChannel(chan);
|
||||
}
|
||||
}
|
||||
|
||||
public void SwitchChannel(ChatUser user, ChatChannel chan, string password) {
|
||||
if(user.CurrentChannel == chan) {
|
||||
if(UserLastChannel.TryGetValue(user.UserId, out ChatChannel ulc) && chan == ulc) {
|
||||
ForceChannel(user);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!user.Can(ChatUserPermissions.JoinAnyChannel) && chan.Owner != user) {
|
||||
if(!user.Can(ChatUserPermissions.JoinAnyChannel) && chan.IsOwner(user)) {
|
||||
if(chan.Rank > user.Rank) {
|
||||
SendTo(user, new LegacyCommandResponse(LCR.CHANNEL_INSUFFICIENT_HIERARCHY, true, chan.Name));
|
||||
ForceChannel(user);
|
||||
return;
|
||||
}
|
||||
|
||||
if(chan.Password != password) {
|
||||
if(!string.IsNullOrEmpty(chan.Password) && chan.Password != password) {
|
||||
SendTo(user, new LegacyCommandResponse(LCR.CHANNEL_INVALID_PASSWORD, true, chan.Name));
|
||||
ForceChannel(user);
|
||||
return;
|
||||
|
@ -149,7 +210,7 @@ namespace SharpChat {
|
|||
if(!Channels.Contains(chan))
|
||||
return;
|
||||
|
||||
ChatChannel oldChan = user.CurrentChannel;
|
||||
ChatChannel oldChan = UserLastChannel[user.UserId];
|
||||
|
||||
SendTo(oldChan, new UserChannelLeavePacket(user));
|
||||
Events.AddEvent(new UserChannelLeaveEvent(DateTimeOffset.Now, user, oldChan));
|
||||
|
@ -166,9 +227,9 @@ namespace SharpChat {
|
|||
|
||||
ChannelUsers.Remove(new ChannelUserAssoc(user.UserId, oldChan.Name));
|
||||
ChannelUsers.Add(new ChannelUserAssoc(user.UserId, chan.Name));
|
||||
user.CurrentChannel = chan;
|
||||
UserLastChannel[user.UserId] = chan;
|
||||
|
||||
if(oldChan.IsTemporary && oldChan.Owner == user)
|
||||
if(oldChan.IsTemporary && oldChan.IsOwner(user))
|
||||
RemoveChannel(oldChan);
|
||||
}
|
||||
|
||||
|
@ -204,6 +265,18 @@ namespace SharpChat {
|
|||
conn.Send(packet);
|
||||
}
|
||||
|
||||
public void SendToUserChannels(ChatUser user, IServerPacket packet) {
|
||||
if(user == null)
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
if(packet == null)
|
||||
throw new ArgumentNullException(nameof(packet));
|
||||
|
||||
IEnumerable<ChatChannel> chans = Channels.Where(c => IsInChannel(user, c));
|
||||
IEnumerable<ChatConnection> conns = Connections.Where(conn => conn.IsAuthed && ChannelUsers.Any(cu => cu.UserId == conn.User.UserId && chans.Any(chan => chan.NameEquals(cu.ChannelName))));
|
||||
foreach(ChatConnection conn in conns)
|
||||
conn.Send(packet);
|
||||
}
|
||||
|
||||
public IPAddress[] GetRemoteAddresses(ChatUser user) {
|
||||
return Connections.Where(c => c.IsAlive && c.User == user).Select(c => c.RemoteAddress).Distinct().ToArray();
|
||||
}
|
||||
|
@ -212,26 +285,18 @@ namespace SharpChat {
|
|||
if(user == null)
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
|
||||
SendTo(user, new UserChannelForceJoinPacket(chan ?? user.CurrentChannel));
|
||||
if(chan == null && !UserLastChannel.TryGetValue(user.UserId, out chan))
|
||||
throw new ArgumentException("no channel???");
|
||||
|
||||
SendTo(user, new UserChannelForceJoinPacket(chan));
|
||||
}
|
||||
|
||||
public void UpdateChannel(ChatChannel channel, string name = null, bool? temporary = null, int? hierarchy = null, string password = null) {
|
||||
public void UpdateChannel(ChatChannel channel, bool? temporary = null, int? hierarchy = null, string password = null) {
|
||||
if(channel == null)
|
||||
throw new ArgumentNullException(nameof(channel));
|
||||
if(!Channels.Contains(channel))
|
||||
throw new ArgumentException("Provided channel is not registered with this manager.", nameof(channel));
|
||||
|
||||
string prevName = channel.Name;
|
||||
int prevHierarchy = channel.Rank;
|
||||
bool nameUpdated = !string.IsNullOrWhiteSpace(name) && name != prevName;
|
||||
|
||||
if(nameUpdated) {
|
||||
if(!ChatChannel.CheckName(name))
|
||||
throw new ArgumentException("Name contains invalid characters.", nameof(name));
|
||||
|
||||
channel.Name = name;
|
||||
}
|
||||
|
||||
if(temporary.HasValue)
|
||||
channel.IsTemporary = temporary.Value;
|
||||
|
||||
|
@ -241,12 +306,9 @@ namespace SharpChat {
|
|||
if(password != null)
|
||||
channel.Password = password;
|
||||
|
||||
// Users that no longer have access to the channel/gained access to the channel by the hierarchy change should receive delete and create packets respectively
|
||||
// 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)) {
|
||||
SendTo(user, new ChannelUpdatePacket(prevName, channel));
|
||||
|
||||
if(nameUpdated)
|
||||
ForceChannel(user);
|
||||
SendTo(user, new ChannelUpdatePacket(channel.Name, channel));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using SharpChat.Misuzu;
|
||||
using System;
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace SharpChat {
|
||||
|
@ -8,57 +7,51 @@ namespace SharpChat {
|
|||
public const int DEFAULT_MINIMUM_DELAY = 10000;
|
||||
public const int DEFAULT_RISKY_OFFSET = 5;
|
||||
|
||||
private const int RANK_NO_FLOOD = 9;
|
||||
|
||||
public long UserId { get; set; }
|
||||
public string Username { get; set; }
|
||||
public long UserId { get; }
|
||||
public string UserName { get; set; }
|
||||
public ChatColour Colour { get; set; }
|
||||
public int Rank { get; set; }
|
||||
public string Nickname { get; set; }
|
||||
public ChatUserPermissions Permissions { get; set; }
|
||||
public ChatUserStatus Status { get; set; } = ChatUserStatus.Online;
|
||||
public string StatusMessage { get; set; }
|
||||
public string NickName { get; set; }
|
||||
public ChatUserStatus Status { get; set; }
|
||||
public string StatusText { get; set; }
|
||||
|
||||
public bool HasFloodProtection
|
||||
=> Rank < RANK_NO_FLOOD;
|
||||
|
||||
// This needs to be a session thing
|
||||
public ChatChannel CurrentChannel { get; set; }
|
||||
|
||||
public string DisplayName {
|
||||
public string LegacyName {
|
||||
get {
|
||||
StringBuilder sb = new();
|
||||
|
||||
if(Status == ChatUserStatus.Away)
|
||||
sb.AppendFormat("<{0}>_", StatusMessage[..Math.Min(StatusMessage.Length, 5)].ToUpperInvariant());
|
||||
sb.AppendFormat("<{0}>_", StatusText[..Math.Min(StatusText.Length, 5)].ToUpperInvariant());
|
||||
|
||||
if(string.IsNullOrWhiteSpace(Nickname))
|
||||
sb.Append(Username);
|
||||
if(string.IsNullOrWhiteSpace(NickName))
|
||||
sb.Append(UserName);
|
||||
else {
|
||||
sb.Append('~');
|
||||
sb.Append(Nickname);
|
||||
sb.Append(NickName);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public ChatUser() { }
|
||||
|
||||
public ChatUser(MisuzuAuthInfo auth) {
|
||||
UserId = auth.UserId;
|
||||
ApplyAuth(auth);
|
||||
}
|
||||
|
||||
public void ApplyAuth(MisuzuAuthInfo auth) {
|
||||
Username = auth.Username;
|
||||
|
||||
if(Status == ChatUserStatus.Offline)
|
||||
Status = ChatUserStatus.Online;
|
||||
|
||||
Colour = ChatColour.FromMisuzu(auth.ColourRaw);
|
||||
Rank = auth.Rank;
|
||||
Permissions = auth.Permissions;
|
||||
public ChatUser(
|
||||
long userId,
|
||||
string userName,
|
||||
ChatColour colour,
|
||||
int rank,
|
||||
ChatUserPermissions perms,
|
||||
string nickName = null,
|
||||
ChatUserStatus status = ChatUserStatus.Online,
|
||||
string statusText = null
|
||||
) {
|
||||
UserId = userId;
|
||||
UserName = userName ?? throw new ArgumentNullException(nameof(userName));
|
||||
Colour = colour;
|
||||
Rank = rank;
|
||||
Permissions = perms;
|
||||
NickName = nickName ?? string.Empty;
|
||||
Status = status;
|
||||
StatusText = statusText ?? string.Empty;
|
||||
}
|
||||
|
||||
public bool Can(ChatUserPermissions perm, bool strict = false) {
|
||||
|
@ -71,27 +64,29 @@ namespace SharpChat {
|
|||
|
||||
sb.Append(UserId);
|
||||
sb.Append('\t');
|
||||
sb.Append(DisplayName);
|
||||
sb.Append(LegacyName);
|
||||
sb.Append('\t');
|
||||
sb.Append(Colour);
|
||||
sb.Append('\t');
|
||||
sb.Append(Rank);
|
||||
sb.Append(' ');
|
||||
sb.Append(Can(ChatUserPermissions.KickUser) ? '1' : '0');
|
||||
sb.Append(" 0 "); // view logs
|
||||
sb.Append(' ');
|
||||
sb.Append(Can(ChatUserPermissions.ViewLogs) ? '1' : '0');
|
||||
sb.Append(' ');
|
||||
sb.Append(Can(ChatUserPermissions.SetOwnNickname) ? '1' : '0');
|
||||
sb.Append(' ');
|
||||
sb.Append(Can(ChatUserPermissions.CreateChannel | ChatUserPermissions.SetChannelPermanent, true) ? 2 : (
|
||||
Can(ChatUserPermissions.CreateChannel) ? 1 : 0
|
||||
sb.Append(Can(ChatUserPermissions.CreateChannel | ChatUserPermissions.SetChannelPermanent, true) ? '2' : (
|
||||
Can(ChatUserPermissions.CreateChannel) ? '1' : '0'
|
||||
));
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public bool NameEquals(string name) {
|
||||
return string.Equals(name, Username, StringComparison.InvariantCultureIgnoreCase)
|
||||
|| string.Equals(name, Nickname, StringComparison.InvariantCultureIgnoreCase)
|
||||
|| string.Equals(name, DisplayName, StringComparison.InvariantCultureIgnoreCase);
|
||||
return string.Equals(name, UserName, StringComparison.InvariantCultureIgnoreCase)
|
||||
|| string.Equals(name, NickName, StringComparison.InvariantCultureIgnoreCase)
|
||||
|| string.Equals(name, LegacyName, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
|
|
|
@ -21,5 +21,6 @@ namespace SharpChat {
|
|||
EditOwnMessage = 0x00002000,
|
||||
EditAnyMessage = 0x00004000,
|
||||
SeeIPAddress = 0x00008000,
|
||||
ViewLogs = 0x00040000,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,9 +20,11 @@ namespace SharpChat.Commands {
|
|||
statusText = statusText[..MAX_LENGTH].Trim();
|
||||
}
|
||||
|
||||
ctx.User.Status = ChatUserStatus.Away;
|
||||
ctx.User.StatusMessage = statusText;
|
||||
ctx.Chat.SendTo(ctx.Channel, new UserUpdatePacket(ctx.User));
|
||||
ctx.Chat.UpdateUser(
|
||||
ctx.User,
|
||||
status: ChatUserStatus.Away,
|
||||
statusText: statusText
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,12 +43,11 @@ namespace SharpChat.Commands {
|
|||
return;
|
||||
}
|
||||
|
||||
ChatChannel createChan = new() {
|
||||
Name = createChanName,
|
||||
IsTemporary = !ctx.User.Can(ChatUserPermissions.SetChannelPermanent),
|
||||
Rank = createChanHierarchy,
|
||||
Owner = ctx.User,
|
||||
};
|
||||
ChatChannel createChan = new(
|
||||
ctx.User, createChanName,
|
||||
isTemporary: !ctx.User.Can(ChatUserPermissions.SetChannelPermanent),
|
||||
rank: createChanHierarchy
|
||||
);
|
||||
|
||||
ctx.Chat.Channels.Add(createChan);
|
||||
foreach(ChatUser ccu in ctx.Chat.Users.Where(u => u.Rank >= ctx.Channel.Rank))
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace SharpChat.Commands {
|
|||
return;
|
||||
}
|
||||
|
||||
if(!ctx.User.Can(ChatUserPermissions.DeleteChannel) && delChan.Owner != 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));
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace SharpChat.Commands {
|
|||
}
|
||||
|
||||
if(banUser == ctx.User || banUser.Rank >= ctx.User.Rank) {
|
||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.KICK_NOT_ALLOWED, true, banUser.DisplayName));
|
||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.KICK_NOT_ALLOWED, true, banUser.LegacyName));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ namespace SharpChat.Commands {
|
|||
MisuzuBanInfo fbi = await Misuzu.CheckBanAsync(userId, userIp);
|
||||
|
||||
if(fbi.IsBanned && !fbi.HasExpired) {
|
||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.KICK_NOT_ALLOWED, true, banUser.DisplayName));
|
||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.KICK_NOT_ALLOWED, true, banUser.LegacyName));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,21 +34,20 @@ namespace SharpChat.Commands {
|
|||
.Replace("\f", string.Empty).Replace("\t", string.Empty)
|
||||
.Replace(' ', '_').Trim();
|
||||
|
||||
if(nickStr == targetUser.Username)
|
||||
nickStr = null;
|
||||
if(nickStr == targetUser.UserName)
|
||||
nickStr = string.Empty;
|
||||
else if(nickStr.Length > 15)
|
||||
nickStr = nickStr[..15];
|
||||
else if(string.IsNullOrEmpty(nickStr))
|
||||
nickStr = null;
|
||||
nickStr = string.Empty;
|
||||
|
||||
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));
|
||||
return;
|
||||
}
|
||||
|
||||
string previousName = targetUser == ctx.User ? (targetUser.Nickname ?? targetUser.Username) : null;
|
||||
targetUser.Nickname = nickStr;
|
||||
ctx.Chat.SendTo(ctx.Channel, new UserUpdatePacket(targetUser, previousName));
|
||||
string previousName = targetUser == ctx.User ? (targetUser.NickName ?? targetUser.UserName) : null;
|
||||
ctx.Chat.UpdateUser(targetUser, nickName: nickStr, silent: previousName == null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace SharpChat.Commands {
|
|||
}
|
||||
|
||||
public void Dispatch(ChatCommandContext ctx) {
|
||||
if(!ctx.User.Can(ChatUserPermissions.SetChannelPassword) || ctx.Channel.Owner != ctx.User) {
|
||||
if(!ctx.User.Can(ChatUserPermissions.SetChannelPassword) || ctx.Channel.IsOwner(ctx.User)) {
|
||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace SharpChat.Commands {
|
|||
}
|
||||
|
||||
public void Dispatch(ChatCommandContext ctx) {
|
||||
if(!ctx.User.Can(ChatUserPermissions.SetChannelHierarchy) || ctx.Channel.Owner != ctx.User) {
|
||||
if(!ctx.User.Can(ChatUserPermissions.SetChannelHierarchy) || ctx.Channel.IsOwner(ctx.User)) {
|
||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}"));
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace SharpChat.Commands {
|
|||
}
|
||||
|
||||
foreach(IPAddress ip in ctx.Chat.GetRemoteAddresses(ipUser))
|
||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.IP_ADDRESS, false, ipUser.Username, ip));
|
||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.IP_ADDRESS, false, ipUser.UserName, ip));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace SharpChat.Commands {
|
|||
|
||||
//silUser.SilencedUntil = silenceUntil;
|
||||
ctx.Chat.SendTo(silUser, new LegacyCommandResponse(LCR.SILENCED, false));
|
||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.TARGET_SILENCED, false, silUser.DisplayName));
|
||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.TARGET_SILENCED, false, silUser.LegacyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace SharpChat.Commands {
|
|||
|
||||
//unsilUser.SilencedUntil = DateTimeOffset.MinValue;
|
||||
ctx.Chat.SendTo(unsilUser, new LegacyCommandResponse(LCR.UNSILENCED, false));
|
||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.TARGET_UNSILENCED, false, unsilUser.DisplayName));
|
||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.TARGET_UNSILENCED, false, unsilUser.LegacyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace SharpChat.Commands {
|
|||
DateTime = dateTime,
|
||||
ChannelName = whisperChan,
|
||||
Sender = ctx.User,
|
||||
Text = $"{whisperUser.DisplayName} {whisperStr}",
|
||||
Text = $"{whisperUser.LegacyName} {whisperStr}",
|
||||
Flags = ChatMessageFlags.Private,
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace SharpChat.Commands {
|
|||
whoChanSB.Append(@" style=""font-weight: bold;""");
|
||||
|
||||
whoChanSB.Append('>');
|
||||
whoChanSB.Append(whoUser.DisplayName);
|
||||
whoChanSB.Append(whoUser.LegacyName);
|
||||
whoChanSB.Append("</a>, ");
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ namespace SharpChat.Commands {
|
|||
whoChanSB.Append(@" style=""font-weight: bold;""");
|
||||
|
||||
whoChanSB.Append('>');
|
||||
whoChanSB.Append(whoUser.DisplayName);
|
||||
whoChanSB.Append(whoUser.LegacyName);
|
||||
whoChanSB.Append("</a>, ");
|
||||
}
|
||||
|
||||
|
|
|
@ -32,10 +32,10 @@ namespace SharpChat.EventStorage {
|
|||
new MySqlParameter("flags", (byte)evt.Flags),
|
||||
new MySqlParameter("data", JsonSerializer.SerializeToUtf8Bytes(evt, evt.GetType())),
|
||||
new MySqlParameter("sender", evt.Sender?.UserId < 1 ? null : (long?)evt.Sender.UserId),
|
||||
new MySqlParameter("sender_name", evt.Sender?.Username),
|
||||
new MySqlParameter("sender_name", evt.Sender?.UserName),
|
||||
new MySqlParameter("sender_colour", evt.Sender?.Colour.ToMisuzu()),
|
||||
new MySqlParameter("sender_rank", evt.Sender?.Rank),
|
||||
new MySqlParameter("sender_nick", evt.Sender?.Nickname),
|
||||
new MySqlParameter("sender_nick", evt.Sender?.NickName),
|
||||
new MySqlParameter("sender_perms", evt.Sender?.Permissions)
|
||||
);
|
||||
}
|
||||
|
@ -72,14 +72,14 @@ namespace SharpChat.EventStorage {
|
|||
evt.DateTime = DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32("event_created"));
|
||||
|
||||
if(!reader.IsDBNull(reader.GetOrdinal("event_sender"))) {
|
||||
evt.Sender = new ChatUser {
|
||||
UserId = reader.GetInt64("event_sender"),
|
||||
Username = reader.GetString("event_sender_name"),
|
||||
Colour = ChatColour.FromMisuzu(reader.GetInt32("event_sender_colour")),
|
||||
Rank = reader.GetInt32("event_sender_rank"),
|
||||
Nickname = reader.IsDBNull(reader.GetOrdinal("event_sender_nick")) ? null : reader.GetString("event_sender_nick"),
|
||||
Permissions = (ChatUserPermissions)reader.GetInt32("event_sender_perms")
|
||||
};
|
||||
evt.Sender = new ChatUser(
|
||||
reader.GetInt64("event_sender"),
|
||||
reader.GetString("event_sender_name"),
|
||||
ChatColour.FromMisuzu(reader.GetInt32("event_sender_colour")),
|
||||
reader.GetInt32("event_sender_rank"),
|
||||
(ChatUserPermissions)reader.GetInt32("event_sender_perms"),
|
||||
reader.IsDBNull(reader.GetOrdinal("event_sender_nick")) ? null : reader.GetString("event_sender_nick")
|
||||
);
|
||||
}
|
||||
|
||||
return evt;
|
||||
|
|
|
@ -13,11 +13,13 @@ namespace SharpChat.Misuzu {
|
|||
public long UserId { get; set; }
|
||||
|
||||
[JsonPropertyName("username")]
|
||||
public string Username { get; set; }
|
||||
public string UserName { get; set; }
|
||||
|
||||
[JsonPropertyName("colour_raw")]
|
||||
public int ColourRaw { get; set; }
|
||||
|
||||
public ChatColour Colour => ChatColour.FromMisuzu(ColourRaw);
|
||||
|
||||
[JsonPropertyName("hierarchy")]
|
||||
public int Rank { get; set; }
|
||||
|
||||
|
|
|
@ -41,19 +41,19 @@ namespace SharpChat.Packet {
|
|||
case UserConnectEvent _:
|
||||
sb.Append(V1_CHATBOT);
|
||||
sb.Append("0\fjoin\f");
|
||||
sb.Append(Event.Sender.Username);
|
||||
sb.Append(Event.Sender.UserName);
|
||||
break;
|
||||
|
||||
case UserChannelJoinEvent _:
|
||||
sb.Append(V1_CHATBOT);
|
||||
sb.Append("0\fjchan\f");
|
||||
sb.Append(Event.Sender.Username);
|
||||
sb.Append(Event.Sender.UserName);
|
||||
break;
|
||||
|
||||
case UserChannelLeaveEvent _:
|
||||
sb.Append(V1_CHATBOT);
|
||||
sb.Append("0\flchan\f");
|
||||
sb.Append(Event.Sender.Username);
|
||||
sb.Append(Event.Sender.UserName);
|
||||
break;
|
||||
|
||||
case UserDisconnectEvent ude:
|
||||
|
@ -77,7 +77,7 @@ namespace SharpChat.Packet {
|
|||
}
|
||||
|
||||
sb.Append('\f');
|
||||
sb.Append(Event.Sender.Username);
|
||||
sb.Append(Event.Sender.UserName);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SharpChat.Packet {
|
||||
public class FloodWarningPacket : ServerPacket {
|
||||
public override IEnumerable<string> Pack() {
|
||||
StringBuilder sb = new();
|
||||
|
||||
sb.Append('2');
|
||||
sb.Append('\t');
|
||||
sb.Append(DateTimeOffset.Now.ToUnixTimeSeconds());
|
||||
sb.Append("\t-1\t0\fflwarn\t");
|
||||
sb.Append(SequenceId);
|
||||
sb.Append("\t10010");
|
||||
|
||||
yield return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -104,5 +104,6 @@ namespace SharpChat.Packet {
|
|||
public const string KICK_NOT_ALLOWED = "kickna";
|
||||
public const string USER_NOT_BANNED = "notban";
|
||||
public const string USER_UNBANNED = "unban";
|
||||
public const string FLOOD_WARN = "flwarn";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace SharpChat.Packet {
|
|||
sb.Append('\t');
|
||||
sb.Append(User.UserId);
|
||||
sb.Append('\t');
|
||||
sb.Append(User.DisplayName);
|
||||
sb.Append(User.LegacyName);
|
||||
sb.Append('\t');
|
||||
|
||||
switch(Reason) {
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace SharpChat.Packet {
|
|||
sb.Append("\t-1\t0\fnick\f");
|
||||
sb.Append(PreviousName);
|
||||
sb.Append('\f');
|
||||
sb.Append(User.DisplayName);
|
||||
sb.Append(User.LegacyName);
|
||||
sb.Append('\t');
|
||||
sb.Append(SequenceId);
|
||||
sb.Append("\t10010");
|
||||
|
|
|
@ -103,12 +103,22 @@ namespace SharpChat.PacketHandlers {
|
|||
ChatUser user = ctx.Chat.Users.FirstOrDefault(u => u.UserId == fai.UserId);
|
||||
|
||||
if(user == null)
|
||||
user = new ChatUser(fai);
|
||||
else {
|
||||
user.ApplyAuth(fai);
|
||||
if(user.CurrentChannel != null)
|
||||
ctx.Chat.SendTo(user.CurrentChannel, new UserUpdatePacket(user));
|
||||
}
|
||||
user = new ChatUser(
|
||||
fai.UserId,
|
||||
fai.UserName,
|
||||
fai.Colour,
|
||||
fai.Rank,
|
||||
fai.Permissions
|
||||
);
|
||||
else
|
||||
ctx.Chat.UpdateUser(
|
||||
user,
|
||||
userName: fai.UserName,
|
||||
status: ChatUserStatus.Online,
|
||||
colour: fai.Colour,
|
||||
rank: fai.Rank,
|
||||
perms: fai.Permissions
|
||||
);
|
||||
|
||||
// Enforce a maximum amount of connections per user
|
||||
if(ctx.Chat.Connections.Count(conn => conn.User == user) >= MaxConnections) {
|
||||
|
@ -119,7 +129,7 @@ namespace SharpChat.PacketHandlers {
|
|||
|
||||
ctx.Connection.BumpPing();
|
||||
ctx.Connection.User = user;
|
||||
ctx.Connection.Send(new LegacyCommandResponse(LCR.WELCOME, false, $"Welcome to Flashii Chat, {user.Username}!"));
|
||||
ctx.Connection.Send(new LegacyCommandResponse(LCR.WELCOME, false, $"Welcome to Flashii Chat, {user.UserName}!"));
|
||||
|
||||
if(File.Exists("welcome.txt")) {
|
||||
IEnumerable<string> lines = File.ReadAllLines("welcome.txt").Where(x => !string.IsNullOrWhiteSpace(x));
|
||||
|
|
|
@ -45,17 +45,13 @@ namespace SharpChat.PacketHandlers {
|
|||
|
||||
ctx.Chat.ContextAccess.Wait();
|
||||
try {
|
||||
ChatChannel channel = user.CurrentChannel;
|
||||
|
||||
if(channel == null
|
||||
|| !ctx.Chat.IsInChannel(user, channel)
|
||||
if(!ctx.Chat.UserLastChannel.TryGetValue(user.UserId, out ChatChannel channel)
|
||||
&& !ctx.Chat.IsInChannel(user, channel)
|
||||
/*|| (user.IsSilenced && !user.Can(ChatUserPermissions.SilenceUser))*/)
|
||||
return;
|
||||
|
||||
if(user.Status != ChatUserStatus.Online) {
|
||||
user.Status = ChatUserStatus.Online;
|
||||
ctx.Chat.SendTo(channel, new UserUpdatePacket(user));
|
||||
}
|
||||
if(user.Status != ChatUserStatus.Online)
|
||||
ctx.Chat.UpdateUser(user, status: ChatUserStatus.Online);
|
||||
|
||||
int maxMsgLength = MaxMessageLength;
|
||||
if(messageText.Length > maxMsgLength)
|
||||
|
@ -64,7 +60,7 @@ namespace SharpChat.PacketHandlers {
|
|||
messageText = messageText.Trim();
|
||||
|
||||
#if DEBUG
|
||||
Logger.Write($"<{ctx.Connection.Id} {user.Username}> {messageText}");
|
||||
Logger.Write($"<{ctx.Connection.Id} {user.UserName}> {messageText}");
|
||||
#endif
|
||||
|
||||
IChatMessage message = null;
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace SharpChat {
|
|||
public const int DEFAULT_MSG_LENGTH_MAX = 5000;
|
||||
public const int DEFAULT_MAX_CONNECTIONS = 5;
|
||||
public const int DEFAULT_FLOOD_KICK_LENGTH = 30;
|
||||
public const int DEFAULT_FLOOD_KICK_EXEMPT_RANK = 9;
|
||||
|
||||
public IWebSocketServer Server { get; }
|
||||
public ChatContext Context { get; }
|
||||
|
@ -27,6 +28,7 @@ namespace SharpChat {
|
|||
private readonly CachedValue<int> MaxMessageLength;
|
||||
private readonly CachedValue<int> MaxConnections;
|
||||
private readonly CachedValue<int> FloodKickLength;
|
||||
private readonly CachedValue<int> FloodKickExemptRank;
|
||||
|
||||
private readonly List<IChatPacketHandler> GuestHandlers = new();
|
||||
private readonly List<IChatPacketHandler> AuthedHandlers = new();
|
||||
|
@ -45,25 +47,26 @@ namespace SharpChat {
|
|||
MaxMessageLength = config.ReadCached("msgMaxLength", DEFAULT_MSG_LENGTH_MAX);
|
||||
MaxConnections = config.ReadCached("connMaxCount", DEFAULT_MAX_CONNECTIONS);
|
||||
FloodKickLength = config.ReadCached("floodKickLength", DEFAULT_FLOOD_KICK_LENGTH);
|
||||
FloodKickExemptRank = config.ReadCached("floodKickExemptRank", DEFAULT_FLOOD_KICK_EXEMPT_RANK);
|
||||
|
||||
Context = new ChatContext(evtStore);
|
||||
|
||||
string[] channelNames = config.ReadValue("channels", new[] { "lounge" });
|
||||
|
||||
foreach(string channelName in channelNames) {
|
||||
ChatChannel channelInfo = new(channelName);
|
||||
IConfig channelCfg = config.ScopeTo($"channels:{channelName}");
|
||||
|
||||
string tmp;
|
||||
tmp = channelCfg.SafeReadValue("name", string.Empty);
|
||||
if(!string.IsNullOrWhiteSpace(tmp))
|
||||
channelInfo.Name = tmp;
|
||||
string name = channelCfg.SafeReadValue("name", string.Empty);
|
||||
if(string.IsNullOrWhiteSpace(name))
|
||||
name = channelName;
|
||||
|
||||
channelInfo.Password = channelCfg.SafeReadValue("password", string.Empty);
|
||||
channelInfo.Rank = channelCfg.SafeReadValue("minRank", 0);
|
||||
ChatChannel channelInfo = new(
|
||||
name,
|
||||
channelCfg.SafeReadValue("password", string.Empty),
|
||||
rank: channelCfg.SafeReadValue("minRank", 0)
|
||||
);
|
||||
|
||||
Context.Channels.Add(channelInfo);
|
||||
|
||||
DefaultChannel ??= channelInfo;
|
||||
}
|
||||
|
||||
|
@ -152,7 +155,7 @@ namespace SharpChat {
|
|||
Context.SafeUpdate();
|
||||
|
||||
// this doesn't affect non-authed connections?????
|
||||
if(conn.User is not null && conn.User.HasFloodProtection) {
|
||||
if(conn.User is not null && conn.User.Rank < FloodKickExemptRank) {
|
||||
ChatUser banUser = null;
|
||||
string banAddr = string.Empty;
|
||||
TimeSpan banDuration = TimeSpan.MinValue;
|
||||
|
@ -178,7 +181,7 @@ namespace SharpChat {
|
|||
|
||||
if(banUser is not null) {
|
||||
if(banDuration == TimeSpan.MinValue) {
|
||||
Context.SendTo(conn.User, new FloodWarningPacket());
|
||||
Context.SendTo(conn.User, new LegacyCommandResponse(LCR.FLOOD_WARN, false));
|
||||
} else {
|
||||
Context.BanUser(conn.User, banDuration, UserDisconnectReason.Flood);
|
||||
|
||||
|
|
Loading…
Reference in a new issue