Events system overhaul.
This commit is contained in:
parent
454a460441
commit
5daad52aba
|
@ -1,5 +1,4 @@
|
||||||
using SharpChat.Events;
|
using SharpChat.Events;
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace SharpChat.SockChat.Commands {
|
namespace SharpChat.SockChat.Commands {
|
||||||
|
@ -17,14 +16,7 @@ namespace SharpChat.SockChat.Commands {
|
||||||
if(string.IsNullOrWhiteSpace(actionStr))
|
if(string.IsNullOrWhiteSpace(actionStr))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
ctx.Chat.Events.Dispatch("msg:add", ctx.Channel, ctx.User, new MessageAddEventData(actionStr, true));
|
||||||
SharpId.Next(),
|
|
||||||
ctx.Channel,
|
|
||||||
ctx.User,
|
|
||||||
DateTimeOffset.Now,
|
|
||||||
actionStr,
|
|
||||||
false, true, false
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using SharpChat.Events;
|
using SharpChat.Events;
|
||||||
using SharpChat.SockChat.PacketsS2C;
|
using SharpChat.SockChat.PacketsS2C;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace SharpChat.SockChat.Commands {
|
namespace SharpChat.SockChat.Commands {
|
||||||
public class MessageBroadcastCommand : ISockChatClientCommand {
|
public class MessageBroadcastCommand : ISockChatClientCommand {
|
||||||
|
@ -15,14 +14,11 @@ namespace SharpChat.SockChat.Commands {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
ctx.Chat.Events.Dispatch(
|
||||||
SharpId.Next(),
|
"msg:add",
|
||||||
string.Empty,
|
|
||||||
ctx.User,
|
ctx.User,
|
||||||
DateTimeOffset.Now,
|
new MessageAddEventData(string.Join(' ', ctx.Args))
|
||||||
string.Join(' ', ctx.Args),
|
);
|
||||||
false, false, true
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using SharpChat.EventStorage;
|
using SharpChat.Events;
|
||||||
using SharpChat.SockChat.PacketsS2C;
|
using SharpChat.SockChat.PacketsS2C;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
|
@ -21,20 +21,22 @@ namespace SharpChat.SockChat.Commands {
|
||||||
|
|
||||||
string? firstArg = ctx.Args.FirstOrDefault();
|
string? firstArg = ctx.Args.FirstOrDefault();
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(firstArg) || !firstArg.All(char.IsDigit) || !long.TryParse(firstArg, out long delSeqId)) {
|
if(string.IsNullOrWhiteSpace(firstArg) || !firstArg.All(char.IsDigit) || !long.TryParse(firstArg, out long eventId)) {
|
||||||
ctx.Chat.SendTo(ctx.User, new CommandFormatErrorS2CPacket());
|
ctx.Chat.SendTo(ctx.User, new CommandFormatErrorS2CPacket());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
StoredEventInfo? delMsg = ctx.Chat.Events.GetEvent(delSeqId);
|
ChatEventInfo? eventInfo = ctx.Chat.EventStorage.GetEvent(eventId);
|
||||||
|
|
||||||
if(delMsg == null || delMsg.Sender?.Rank > ctx.User.Rank || (!deleteAnyMessage && delMsg.Sender?.UserId != ctx.User.UserId)) {
|
if(eventInfo == null
|
||||||
|
|| !eventInfo.Type.Equals("msg:add")
|
||||||
|
|| eventInfo.SenderRank > ctx.User.Rank
|
||||||
|
|| (!deleteAnyMessage && eventInfo.SenderId != ctx.User.UserId)) {
|
||||||
ctx.Chat.SendTo(ctx.User, new MessageDeleteNotAllowedErrorS2CPacket());
|
ctx.Chat.SendTo(ctx.User, new MessageDeleteNotAllowedErrorS2CPacket());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Chat.Events.RemoveEvent(delMsg);
|
ctx.Chat.Events.Dispatch("msg:delete", eventInfo.ChannelName, ctx.User, new MessageDeleteEventData(eventInfo.Id.ToString()));
|
||||||
ctx.Chat.Send(new MessageDeleteS2CPacket(delMsg.Id));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using SharpChat.Events;
|
using SharpChat.Events;
|
||||||
using SharpChat.SockChat.PacketsS2C;
|
using SharpChat.SockChat.PacketsS2C;
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace SharpChat.SockChat.Commands {
|
namespace SharpChat.SockChat.Commands {
|
||||||
|
@ -28,14 +27,12 @@ namespace SharpChat.SockChat.Commands {
|
||||||
if(whisperUser == ctx.User)
|
if(whisperUser == ctx.User)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
ctx.Chat.Events.Dispatch(
|
||||||
SharpId.Next(),
|
"msg:add",
|
||||||
UserInfo.GetDMChannelName(ctx.User, whisperUser),
|
UserInfo.GetDMChannelName(ctx.User, whisperUser),
|
||||||
ctx.User,
|
ctx.User,
|
||||||
DateTimeOffset.Now,
|
new MessageAddEventData(string.Join(' ', ctx.Args.Skip(1)))
|
||||||
string.Join(' ', ctx.Args.Skip(1)),
|
);
|
||||||
true, false, false
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using SharpChat.Config;
|
using SharpChat.Config;
|
||||||
using SharpChat.Events;
|
using SharpChat.Events;
|
||||||
using SharpChat.SockChat.Commands;
|
using SharpChat.SockChat.Commands;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
|
@ -80,14 +79,7 @@ namespace SharpChat.SockChat.PacketsC2S {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Chat.DispatchEvent(new MessageCreateEvent(
|
ctx.Chat.Events.Dispatch("msg:add", channelInfo, user, new MessageAddEventData(messageText));
|
||||||
SharpId.Next(),
|
|
||||||
channelInfo,
|
|
||||||
user,
|
|
||||||
DateTimeOffset.Now,
|
|
||||||
messageText,
|
|
||||||
false, false, false
|
|
||||||
));
|
|
||||||
} finally {
|
} finally {
|
||||||
ctx.Chat.ContextAccess.Release();
|
ctx.Chat.ContextAccess.Release();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
namespace SharpChat.SockChat.PacketsS2C {
|
namespace SharpChat.SockChat.PacketsS2C {
|
||||||
public class MessageDeleteS2CPacket : ISockChatS2CPacket {
|
public class MessageDeleteS2CPacket : ISockChatS2CPacket {
|
||||||
private readonly long DeletedMessageId;
|
private readonly string DeletedMessageId;
|
||||||
|
|
||||||
public MessageDeleteS2CPacket(long deletedMessageId) {
|
public MessageDeleteS2CPacket(string deletedMessageId) {
|
||||||
DeletedMessageId = deletedMessageId;
|
DeletedMessageId = deletedMessageId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,79 +5,133 @@ using SharpChat.SockChat.PacketsS2C;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace SharpChat {
|
namespace SharpChat {
|
||||||
public class SockChatContext {
|
public class SockChatContext : IChatEventHandler {
|
||||||
public readonly SemaphoreSlim ContextAccess = new(1, 1);
|
public readonly SemaphoreSlim ContextAccess = new(1, 1);
|
||||||
|
|
||||||
public ChannelsContext Channels { get; } = new();
|
public ChannelsContext Channels { get; } = new();
|
||||||
public ConnectionsContext Connections { get; } = new();
|
public ConnectionsContext Connections { get; } = new();
|
||||||
public UsersContext Users { get; } = new();
|
public UsersContext Users { get; } = new();
|
||||||
public IEventStorage Events { get; }
|
public IEventStorage EventStorage { get; }
|
||||||
|
public ChatEventDispatcher Events { get; } = new();
|
||||||
public ChannelsUsersContext ChannelsUsers { get; } = new();
|
public ChannelsUsersContext ChannelsUsers { get; } = new();
|
||||||
public Dictionary<long, RateLimiter> UserRateLimiters { get; } = new();
|
public Dictionary<long, RateLimiter> UserRateLimiters { get; } = new();
|
||||||
|
|
||||||
public SockChatContext(IEventStorage evtStore) {
|
public SockChatContext(IEventStorage evtStore) {
|
||||||
Events = evtStore;
|
EventStorage = evtStore;
|
||||||
|
Events.Subscribe(evtStore);
|
||||||
|
Events.Subscribe(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DispatchEvent(IChatEvent eventInfo) {
|
public void HandleEvent(ChatEventInfo info) {
|
||||||
if(eventInfo is MessageCreateEvent mce) {
|
// user status should be stored outside of the UserInfo class so we don't need to do this:
|
||||||
if(mce.IsBroadcast) {
|
UserInfo? userInfo = Users.Get(info.SenderId);
|
||||||
Send(new MessageBroadcastS2CPacket(mce.MessageId, mce.MessageCreated, mce.MessageText));
|
|
||||||
} else if(mce.IsPrivate) {
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// this entire routine is garbage, channels should probably in the db
|
switch(info.Type) {
|
||||||
if(mce.ChannelName?.StartsWith("@") != true)
|
case "user:connect":
|
||||||
return;
|
SendTo(info.ChannelName, new UserConnectS2CPacket(
|
||||||
|
info.Id,
|
||||||
|
info.Created,
|
||||||
|
info.SenderId,
|
||||||
|
userInfo == null ? SockChatUtility.GetUserName(info) : SockChatUtility.GetUserNameWithStatus(userInfo),
|
||||||
|
info.SenderColour,
|
||||||
|
info.SenderRank,
|
||||||
|
info.SenderPerms
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
|
||||||
long[] targetIds = mce.ChannelName[1..].Split('-', 3).Select(u => long.TryParse(u, out long up) ? up : -1).ToArray();
|
case "user:disconnect":
|
||||||
if(targetIds.Length != 2)
|
SendTo(info.ChannelName, new UserDisconnectS2CPacket(
|
||||||
return;
|
info.Id,
|
||||||
|
info.Created,
|
||||||
|
info.SenderId,
|
||||||
|
userInfo == null ? SockChatUtility.GetUserName(info) : SockChatUtility.GetUserNameWithStatus(userInfo),
|
||||||
|
info.Data is UserDisconnectEventData userDisconnect ? userDisconnect.Reason : UserDisconnectReason.Leave
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
|
||||||
UserInfo[] users = Users.GetMany(targetIds);
|
case "chan:join":
|
||||||
UserInfo? target = users.FirstOrDefault(u => u.UserId != mce.SenderId);
|
SendTo(info.ChannelName, new UserChannelJoinS2CPacket(
|
||||||
if(target == null)
|
info.SenderId,
|
||||||
return;
|
userInfo == null ? SockChatUtility.GetUserName(info) : SockChatUtility.GetUserNameWithStatus(userInfo),
|
||||||
|
info.SenderColour,
|
||||||
|
info.SenderRank,
|
||||||
|
info.SenderPerms
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
|
||||||
foreach(UserInfo user in users)
|
case "chan:leave":
|
||||||
SendTo(user, new MessageAddS2CPacket(
|
SendTo(info.ChannelName, new UserChannelLeaveS2CPacket(info.SenderId));
|
||||||
mce.MessageId,
|
break;
|
||||||
DateTimeOffset.Now,
|
|
||||||
mce.SenderId,
|
|
||||||
mce.SenderId == user.UserId ? $"{SockChatUtility.GetUserName(target)} {mce.MessageText}" : mce.MessageText,
|
|
||||||
mce.IsAction,
|
|
||||||
true
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
ChannelInfo? channel = Channels.Get(mce.ChannelName, SockChatUtility.SanitiseChannelName);
|
|
||||||
if(channel != null)
|
|
||||||
SendTo(channel, new MessageAddS2CPacket(
|
|
||||||
mce.MessageId,
|
|
||||||
DateTimeOffset.Now,
|
|
||||||
mce.SenderId,
|
|
||||||
mce.MessageText,
|
|
||||||
mce.IsAction,
|
|
||||||
false
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
object msgData = mce.IsAction
|
case "msg:delete":
|
||||||
? new { text = mce.MessageText, action = true }
|
if(info.Data is not MessageDeleteEventData msgDelete)
|
||||||
: new { text = mce.MessageText };
|
break;
|
||||||
|
|
||||||
Events.AddEvent(
|
MessageDeleteS2CPacket msgDelPacket = new(msgDelete.MessageId);
|
||||||
mce.MessageId,
|
|
||||||
"msg:add",
|
if(info.IsBroadcast) {
|
||||||
mce.IsBroadcast ? null : mce.ChannelName,
|
Send(msgDelPacket);
|
||||||
mce.SenderId, mce.SenderName, mce.SenderColour, mce.SenderRank, mce.SenderNickName, mce.SenderPerms,
|
} else if(info.ChannelName.StartsWith('@')) {
|
||||||
msgData
|
long[] targetIds = info.ChannelName[1..].Split('-', 3).Select(u => long.TryParse(u, out long up) ? up : -1).ToArray();
|
||||||
);
|
if(targetIds.Length != 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
UserInfo[] users = Users.GetMany(targetIds);
|
||||||
|
UserInfo? target = users.FirstOrDefault(u => u.UserId != info.SenderId);
|
||||||
|
if(target == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach(UserInfo user in users)
|
||||||
|
SendTo(user, msgDelPacket);
|
||||||
|
} else {
|
||||||
|
SendTo(info.ChannelName, msgDelPacket);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "msg:add":
|
||||||
|
if(info.Data is not MessageAddEventData msgAdd)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(info.IsBroadcast) {
|
||||||
|
Send(new MessageBroadcastS2CPacket(info.Id, info.Created, msgAdd.Text));
|
||||||
|
} else if(info.ChannelName.StartsWith('@')) {
|
||||||
|
// 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
|
||||||
|
|
||||||
|
long[] targetIds = info.ChannelName[1..].Split('-', 3).Select(u => long.TryParse(u, out long up) ? up : -1).ToArray();
|
||||||
|
if(targetIds.Length != 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
UserInfo[] users = Users.GetMany(targetIds);
|
||||||
|
UserInfo? target = users.FirstOrDefault(u => u.UserId != info.SenderId);
|
||||||
|
if(target == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach(UserInfo user in users)
|
||||||
|
SendTo(user, new MessageAddS2CPacket(
|
||||||
|
info.Id,
|
||||||
|
DateTimeOffset.Now,
|
||||||
|
info.SenderId,
|
||||||
|
info.SenderId == user.UserId ? $"{SockChatUtility.GetUserName(target)} {msgAdd.Text}" : msgAdd.Text,
|
||||||
|
msgAdd.IsAction,
|
||||||
|
true
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
ChannelInfo? channel = Channels.Get(info.ChannelName, SockChatUtility.SanitiseChannelName);
|
||||||
|
if(channel != null)
|
||||||
|
SendTo(channel, new MessageAddS2CPacket(
|
||||||
|
info.Id,
|
||||||
|
DateTimeOffset.Now,
|
||||||
|
info.SenderId,
|
||||||
|
msgAdd.Text,
|
||||||
|
msgAdd.IsAction,
|
||||||
|
false
|
||||||
|
));
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,105 +261,81 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleChannelEventLog(string channelName, Action<ISockChatS2CPacket> handler) {
|
public void HandleChannelEventLog(string channelName, Action<ISockChatS2CPacket> handler) {
|
||||||
foreach(StoredEventInfo msg in Events.GetChannelEventLog(channelName)) {
|
foreach(ChatEventInfo info in EventStorage.GetChannelEventLog(channelName)) {
|
||||||
ISockChatS2CPacket packet;
|
ISockChatS2CPacket? packet;
|
||||||
|
|
||||||
switch(msg.Type) {
|
switch(info.Type) {
|
||||||
case "msg:add":
|
case "msg:add":
|
||||||
string maText = string.Empty;
|
string maText = string.Empty;
|
||||||
if(msg.Data.RootElement.TryGetProperty("text", out JsonElement maTextElem))
|
|
||||||
maText = maTextElem.ToString();
|
|
||||||
|
|
||||||
bool maAction = false;
|
bool maAction = false;
|
||||||
if(msg.Data.RootElement.TryGetProperty("action", out JsonElement maIsActionElem))
|
|
||||||
maAction = maIsActionElem.ValueKind == JsonValueKind.True;
|
if(info.Data is MessageAddEventData messageAdd) {
|
||||||
|
maText = messageAdd.Text;
|
||||||
|
maAction = messageAdd.IsAction;
|
||||||
|
}
|
||||||
|
|
||||||
packet = new MessageAddLogS2CPacket(
|
packet = new MessageAddLogS2CPacket(
|
||||||
msg.Id,
|
info.Id,
|
||||||
msg.Created,
|
info.Created,
|
||||||
msg.Sender?.UserId ?? -1,
|
info.SenderId,
|
||||||
msg.Sender == null ? "ChatBot" : SockChatUtility.GetUserName(msg.Sender),
|
info.SenderName,
|
||||||
msg.Sender?.Colour ?? Colour.None,
|
info.SenderColour,
|
||||||
msg.Sender?.Rank ?? 0,
|
info.SenderRank,
|
||||||
msg.Sender?.Permissions ?? 0,
|
info.SenderPerms,
|
||||||
maText,
|
maText,
|
||||||
maAction,
|
maAction,
|
||||||
msg.ChannelName?.StartsWith('@') == true,
|
info.ChannelName.StartsWith('@'),
|
||||||
msg.ChannelName == null,
|
info.IsBroadcast,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "user:connect":
|
case "user:connect":
|
||||||
packet = new UserConnectLogS2CPacket(
|
packet = new UserConnectLogS2CPacket(
|
||||||
msg.Id,
|
info.Id,
|
||||||
msg.Created,
|
info.Created,
|
||||||
msg.Sender == null ? string.Empty : SockChatUtility.GetUserName(msg.Sender)
|
SockChatUtility.GetUserName(info)
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "user:disconnect":
|
case "user:disconnect":
|
||||||
UserDisconnectReason udReason = 0;
|
|
||||||
if(msg.Data.RootElement.TryGetProperty("reason", out JsonElement udElem) && udElem.TryGetByte(out byte udReasonRaw))
|
|
||||||
udReason = (UserDisconnectReason)udReasonRaw;
|
|
||||||
|
|
||||||
packet = new UserDisconnectLogS2CPacket(
|
packet = new UserDisconnectLogS2CPacket(
|
||||||
msg.Id,
|
info.Id,
|
||||||
msg.Created,
|
info.Created,
|
||||||
msg.Sender == null ? string.Empty : SockChatUtility.GetUserNameWithStatus(msg.Sender),
|
SockChatUtility.GetUserName(info),
|
||||||
udReason
|
info.Data is UserDisconnectEventData userDisconnect ? userDisconnect.Reason : UserDisconnectReason.Leave
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "chan:join":
|
case "chan:join":
|
||||||
packet = new UserChannelJoinLogS2CPacket(
|
packet = new UserChannelJoinLogS2CPacket(
|
||||||
msg.Id,
|
info.Id,
|
||||||
msg.Created,
|
info.Created,
|
||||||
msg.Sender == null ? string.Empty : SockChatUtility.GetUserName(msg.Sender)
|
SockChatUtility.GetUserName(info)
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "chan:leave":
|
case "chan:leave":
|
||||||
packet = new UserChannelLeaveLogS2CPacket(
|
packet = new UserChannelLeaveLogS2CPacket(
|
||||||
msg.Id,
|
info.Id,
|
||||||
msg.Created,
|
info.Created,
|
||||||
msg.Sender == null ? string.Empty : SockChatUtility.GetUserName(msg.Sender)
|
SockChatUtility.GetUserName(info)
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Exception($"Unsupported backlog type: {msg.Type}");
|
packet = null;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
handler(packet);
|
if(packet != null)
|
||||||
|
handler(packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleJoin(UserInfo user, ChannelInfo chan, SockChatConnectionInfo conn, int maxMsgLength) {
|
public void HandleJoin(UserInfo user, ChannelInfo chan, SockChatConnectionInfo conn, int maxMsgLength) {
|
||||||
if(!ChannelsUsers.Has(chan, user)) {
|
if(!ChannelsUsers.Has(chan, user))
|
||||||
long eventId = SharpId.Next();
|
Events.Dispatch("user:connect", chan, user);
|
||||||
SendTo(chan, new UserConnectS2CPacket(
|
|
||||||
eventId,
|
|
||||||
DateTimeOffset.UtcNow,
|
|
||||||
user.UserId,
|
|
||||||
SockChatUtility.GetUserNameWithStatus(user),
|
|
||||||
user.Colour,
|
|
||||||
user.Rank,
|
|
||||||
user.Permissions
|
|
||||||
));
|
|
||||||
Events.AddEvent(
|
|
||||||
eventId,
|
|
||||||
"user:connect",
|
|
||||||
chan.Name,
|
|
||||||
user.UserId,
|
|
||||||
user.UserName,
|
|
||||||
user.Colour,
|
|
||||||
user.Rank,
|
|
||||||
user.NickName,
|
|
||||||
user.Permissions,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.Send(new AuthSuccessS2CPacket(
|
conn.Send(new AuthSuccessS2CPacket(
|
||||||
user.UserId,
|
user.UserId,
|
||||||
|
@ -347,26 +377,7 @@ namespace SharpChat {
|
||||||
ChannelsUsers.DeleteUser(user);
|
ChannelsUsers.DeleteUser(user);
|
||||||
|
|
||||||
foreach(ChannelInfo chan in channels) {
|
foreach(ChannelInfo chan in channels) {
|
||||||
long eventId = SharpId.Next();
|
Events.Dispatch("user:disconnect", chan, user, new UserDisconnectEventData(reason));
|
||||||
SendTo(chan, new UserDisconnectS2CPacket(
|
|
||||||
eventId,
|
|
||||||
DateTimeOffset.UtcNow,
|
|
||||||
user.UserId,
|
|
||||||
SockChatUtility.GetUserNameWithStatus(user),
|
|
||||||
reason
|
|
||||||
));
|
|
||||||
Events.AddEvent(
|
|
||||||
eventId,
|
|
||||||
"user:disconnect",
|
|
||||||
chan.Name,
|
|
||||||
user.UserId,
|
|
||||||
user.UserName,
|
|
||||||
user.Colour,
|
|
||||||
user.Rank,
|
|
||||||
user.NickName,
|
|
||||||
user.Permissions,
|
|
||||||
new { reason = (int)reason }
|
|
||||||
);
|
|
||||||
|
|
||||||
if(chan.IsTemporary && chan.IsOwner(user))
|
if(chan.IsTemporary && chan.IsOwner(user))
|
||||||
RemoveChannel(chan);
|
RemoveChannel(chan);
|
||||||
|
@ -397,45 +408,13 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ForceChannelSwitch(UserInfo user, ChannelInfo chan) {
|
public void ForceChannelSwitch(UserInfo user, ChannelInfo chan) {
|
||||||
|
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||||
ChannelInfo? oldChan = Channels.Get(ChannelsUsers.GetUserLastChannel(user));
|
ChannelInfo? oldChan = Channels.Get(ChannelsUsers.GetUserLastChannel(user));
|
||||||
|
|
||||||
if(oldChan != null) {
|
|
||||||
SendTo(oldChan, new UserChannelLeaveS2CPacket(user.UserId));
|
|
||||||
Events.AddEvent(
|
|
||||||
SharpId.Next(),
|
|
||||||
"chan:leave",
|
|
||||||
oldChan.Name,
|
|
||||||
user.UserId,
|
|
||||||
user.UserName,
|
|
||||||
user.Colour,
|
|
||||||
user.Rank,
|
|
||||||
user.NickName,
|
|
||||||
user.Permissions,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
SendTo(chan, new UserChannelJoinS2CPacket(
|
|
||||||
user.UserId,
|
|
||||||
SockChatUtility.GetUserNameWithStatus(user),
|
|
||||||
user.Colour,
|
|
||||||
user.Rank,
|
|
||||||
user.Permissions
|
|
||||||
));
|
|
||||||
|
|
||||||
if(oldChan != null)
|
if(oldChan != null)
|
||||||
Events.AddEvent(
|
Events.Dispatch("chan:leave", now, oldChan, user);
|
||||||
SharpId.Next(),
|
|
||||||
"chan:join",
|
Events.Dispatch("chan:join", now, chan, user);
|
||||||
oldChan.Name,
|
|
||||||
user.UserId,
|
|
||||||
user.UserName,
|
|
||||||
user.Colour,
|
|
||||||
user.Rank,
|
|
||||||
user.NickName,
|
|
||||||
user.Permissions,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
SendTo(user, new ContextClearS2CPacket(ContextClearS2CPacket.ClearMode.MessagesUsers));
|
SendTo(user, new ContextClearS2CPacket(ContextClearS2CPacket.ClearMode.MessagesUsers));
|
||||||
SendTo(user, new UsersPopulateS2CPacket(GetChannelUsers(chan).Except(new[] { user }).Select(
|
SendTo(user, new UsersPopulateS2CPacket(GetChannelUsers(chan).Except(new[] { user }).Select(
|
||||||
|
@ -481,7 +460,15 @@ namespace SharpChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendTo(ChannelInfo channel, string packet) {
|
public void SendTo(ChannelInfo channel, string packet) {
|
||||||
long[] userIds = ChannelsUsers.GetChannelUserIds(channel);
|
SendTo(channel.Name, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendTo(string channelName, ISockChatS2CPacket packet) {
|
||||||
|
SendTo(channelName, packet.Pack());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendTo(string channelName, string packet) {
|
||||||
|
long[] userIds = ChannelsUsers.GetChannelUserIds(channelName);
|
||||||
foreach(long userId in userIds)
|
foreach(long userId in userIds)
|
||||||
Connections.WithUser(userId, conn => {
|
Connections.WithUser(userId, conn => {
|
||||||
if(conn is SockChatConnectionInfo scConn)
|
if(conn is SockChatConnectionInfo scConn)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using SharpChat.Events;
|
||||||
|
using System;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace SharpChat.SockChat {
|
namespace SharpChat.SockChat {
|
||||||
|
@ -37,6 +38,10 @@ namespace SharpChat.SockChat {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetUserName(ChatEventInfo info) {
|
||||||
|
return string.IsNullOrWhiteSpace(info.SenderNickName) ? info.SenderName : $"~{info.SenderNickName}";
|
||||||
|
}
|
||||||
|
|
||||||
public static (string, UsersContext.NameTarget) ExplodeUserName(string name) {
|
public static (string, UsersContext.NameTarget) ExplodeUserName(string name) {
|
||||||
UsersContext.NameTarget target = UsersContext.NameTarget.UserName;
|
UsersContext.NameTarget target = UsersContext.NameTarget.UserName;
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,9 @@
|
||||||
using System.Collections.Generic;
|
using SharpChat.Events;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace SharpChat.EventStorage {
|
namespace SharpChat.EventStorage {
|
||||||
public interface IEventStorage {
|
public interface IEventStorage : IChatEventHandler {
|
||||||
void AddEvent(
|
ChatEventInfo? GetEvent(long eventId);
|
||||||
long id,
|
IEnumerable<ChatEventInfo> GetChannelEventLog(string channelName, int amount = 20, int startAt = 0);
|
||||||
string type,
|
|
||||||
string? channelName,
|
|
||||||
long senderId,
|
|
||||||
string? senderName,
|
|
||||||
Colour senderColour,
|
|
||||||
int senderRank,
|
|
||||||
string? senderNick,
|
|
||||||
UserPermissions senderPerms,
|
|
||||||
object? data = null
|
|
||||||
);
|
|
||||||
|
|
||||||
void RemoveEvent(StoredEventInfo evt);
|
|
||||||
StoredEventInfo? GetEvent(long seqId);
|
|
||||||
IEnumerable<StoredEventInfo> GetChannelEventLog(string channelName, int amount = 20, int startAt = 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
using MySqlConnector;
|
using MySqlConnector;
|
||||||
|
using SharpChat.Events;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
|
@ -8,41 +10,58 @@ namespace SharpChat.EventStorage {
|
||||||
public partial class MariaDBEventStorage : IEventStorage {
|
public partial class MariaDBEventStorage : IEventStorage {
|
||||||
private string ConnectionString { get; }
|
private string ConnectionString { get; }
|
||||||
|
|
||||||
|
private readonly JsonSerializerOptions jsonOpts = new() {
|
||||||
|
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault,
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly Dictionary<string, Type> EventDataTypes = new();
|
||||||
|
|
||||||
|
static MariaDBEventStorage() {
|
||||||
|
foreach(Type type in Assembly.GetExecutingAssembly().GetTypes()) {
|
||||||
|
ChatEventDataForAttribute? forAttr = type.GetCustomAttribute<ChatEventDataForAttribute>();
|
||||||
|
if(forAttr == null)
|
||||||
|
continue;
|
||||||
|
if(EventDataTypes.ContainsKey(forAttr.EventName))
|
||||||
|
throw new InvalidOperationException($"Attempted to register more than one data type for {forAttr.EventName}!");
|
||||||
|
|
||||||
|
EventDataTypes.Add(forAttr.EventName, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public MariaDBEventStorage(string connString) {
|
public MariaDBEventStorage(string connString) {
|
||||||
ConnectionString = connString;
|
ConnectionString = connString;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddEvent(
|
public void HandleEvent(ChatEventInfo info) {
|
||||||
long id,
|
MySqlParameter dataParam = new("data", MySqlDbType.Blob) {
|
||||||
string type,
|
Value = JsonSerializer.SerializeToUtf8Bytes(info.Data, info.Data.GetType(), jsonOpts)
|
||||||
string? channelName,
|
};
|
||||||
long senderId,
|
|
||||||
string? senderName,
|
|
||||||
Colour senderColour,
|
|
||||||
int senderRank,
|
|
||||||
string? senderNick,
|
|
||||||
UserPermissions senderPerms,
|
|
||||||
object? data = null
|
|
||||||
) {
|
|
||||||
RunCommand(
|
RunCommand(
|
||||||
"INSERT INTO `sqc_events` (`event_id`, `event_created`, `event_type`, `event_target`, `event_data`"
|
"INSERT INTO `sqc_events` (`event_id`, `event_created`, `event_type`, `event_target`, `event_data`"
|
||||||
+ ", `event_sender`, `event_sender_name`, `event_sender_colour`, `event_sender_rank`, `event_sender_nick`, `event_sender_perms`)"
|
+ ", `event_sender`, `event_sender_name`, `event_sender_colour`, `event_sender_rank`, `event_sender_nick`, `event_sender_perms`)"
|
||||||
+ " VALUES (@id, NOW(), @type, @target, @data"
|
+ " VALUES (@id, NOW(), @type, @target, @data"
|
||||||
+ ", @sender, @sender_name, @sender_colour, @sender_rank, @sender_nick, @sender_perms)",
|
+ ", @sender, @sender_name, @sender_colour, @sender_rank, @sender_nick, @sender_perms)",
|
||||||
new MySqlParameter("id", id),
|
new MySqlParameter("id", info.Id),
|
||||||
new MySqlParameter("type", type),
|
new MySqlParameter("type", info.Type),
|
||||||
new MySqlParameter("target", string.IsNullOrWhiteSpace(channelName) ? null : channelName),
|
new MySqlParameter("target", string.IsNullOrWhiteSpace(info.ChannelName) ? null : info.ChannelName),
|
||||||
new MySqlParameter("data", data == null ? "{}" : JsonSerializer.SerializeToUtf8Bytes(data)),
|
dataParam,
|
||||||
new MySqlParameter("sender", senderId < 1 ? null : senderId),
|
new MySqlParameter("sender", info.SenderId < 1 ? null : info.SenderId),
|
||||||
new MySqlParameter("sender_name", senderName),
|
new MySqlParameter("sender_name", info.SenderName),
|
||||||
new MySqlParameter("sender_colour", senderColour.ToMisuzu()),
|
new MySqlParameter("sender_colour", info.SenderColour.ToMisuzu()),
|
||||||
new MySqlParameter("sender_rank", senderRank),
|
new MySqlParameter("sender_rank", info.SenderRank),
|
||||||
new MySqlParameter("sender_nick", string.IsNullOrWhiteSpace(senderNick) ? null : senderNick),
|
new MySqlParameter("sender_nick", info.SenderNickName),
|
||||||
new MySqlParameter("sender_perms", senderPerms)
|
new MySqlParameter("sender_perms", info.SenderPerms)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if(info.Type.Equals("msg:delete") && info.Data is MessageDeleteEventData msgDelete)
|
||||||
|
RunCommand(
|
||||||
|
"UPDATE IGNORE `sqc_events` SET `event_deleted` = NOW() WHERE `event_id` = @id AND `event_deleted` IS NULL",
|
||||||
|
new MySqlParameter("id", msgDelete.MessageId)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StoredEventInfo? GetEvent(long seqId) {
|
public ChatEventInfo? GetEvent(long eventId) {
|
||||||
try {
|
try {
|
||||||
using MySqlDataReader? reader = RunQuery(
|
using MySqlDataReader? reader = RunQuery(
|
||||||
"SELECT `event_id`, `event_type`, `event_data`, `event_target`"
|
"SELECT `event_id`, `event_type`, `event_data`, `event_target`"
|
||||||
|
@ -51,12 +70,12 @@ namespace SharpChat.EventStorage {
|
||||||
+ ", UNIX_TIMESTAMP(`event_deleted`) AS `event_deleted`"
|
+ ", UNIX_TIMESTAMP(`event_deleted`) AS `event_deleted`"
|
||||||
+ " FROM `sqc_events`"
|
+ " FROM `sqc_events`"
|
||||||
+ " WHERE `event_id` = @id",
|
+ " WHERE `event_id` = @id",
|
||||||
new MySqlParameter("id", seqId)
|
new MySqlParameter("id", eventId)
|
||||||
);
|
);
|
||||||
|
|
||||||
if(reader != null)
|
if(reader != null)
|
||||||
while(reader.Read()) {
|
while(reader.Read()) {
|
||||||
StoredEventInfo evt = ReadEvent(reader);
|
ChatEventInfo evt = ReadEvent(reader);
|
||||||
if(evt != null)
|
if(evt != null)
|
||||||
return evt;
|
return evt;
|
||||||
}
|
}
|
||||||
|
@ -67,27 +86,29 @@ namespace SharpChat.EventStorage {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static StoredEventInfo ReadEvent(MySqlDataReader reader) {
|
private static ChatEventInfo ReadEvent(MySqlDataReader reader) {
|
||||||
return new StoredEventInfo(
|
string eventType = Encoding.ASCII.GetString((byte[])reader["event_type"]);
|
||||||
|
ChatEventData eventData = EventDataTypes.ContainsKey(eventType)
|
||||||
|
? (ChatEventData)(JsonSerializer.Deserialize((byte[])reader["event_data"], EventDataTypes[eventType]) ?? ChatEventData.EmptyInstance)
|
||||||
|
: ChatEventData.EmptyInstance;
|
||||||
|
|
||||||
|
return new ChatEventInfo(
|
||||||
reader.GetInt64("event_id"),
|
reader.GetInt64("event_id"),
|
||||||
Encoding.ASCII.GetString((byte[])reader["event_type"]),
|
eventType,
|
||||||
reader.IsDBNull(reader.GetOrdinal("event_sender")) ? null : new UserInfo(
|
|
||||||
reader.GetInt64("event_sender"),
|
|
||||||
reader.IsDBNull(reader.GetOrdinal("event_sender_name")) ? string.Empty : reader.GetString("event_sender_name"),
|
|
||||||
Colour.FromMisuzu(reader.GetInt32("event_sender_colour")),
|
|
||||||
reader.GetInt32("event_sender_rank"),
|
|
||||||
(UserPermissions)reader.GetInt32("event_sender_perms"),
|
|
||||||
reader.IsDBNull(reader.GetOrdinal("event_sender_nick")) ? null : reader.GetString("event_sender_nick")
|
|
||||||
),
|
|
||||||
DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32("event_created")),
|
DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32("event_created")),
|
||||||
reader.IsDBNull(reader.GetOrdinal("event_deleted")) ? null : DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32("event_deleted")),
|
reader.IsDBNull(reader.GetOrdinal("event_target")) ? string.Empty : Encoding.ASCII.GetString((byte[])reader["event_target"]),
|
||||||
reader.IsDBNull(reader.GetOrdinal("event_target")) ? null : Encoding.ASCII.GetString((byte[])reader["event_target"]),
|
reader.IsDBNull(reader.GetOrdinal("event_sender")) ? -1 : reader.GetInt64("event_sender"),
|
||||||
JsonDocument.Parse(Encoding.ASCII.GetString((byte[])reader["event_data"]))
|
reader.IsDBNull(reader.GetOrdinal("event_sender_name")) ? string.Empty : reader.GetString("event_sender_name"),
|
||||||
|
reader.IsDBNull(reader.GetOrdinal("event_sender_colour")) ? Colour.None : Colour.FromMisuzu(reader.GetInt32("event_sender_colour")),
|
||||||
|
reader.GetInt32("event_sender_rank"),
|
||||||
|
reader.IsDBNull(reader.GetOrdinal("event_sender_nick")) ? null : reader.GetString("event_sender_nick"),
|
||||||
|
(UserPermissions)reader.GetInt32("event_sender_perms"),
|
||||||
|
eventData
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<StoredEventInfo> GetChannelEventLog(string channelName, int amount = 20, int startAt = 0) {
|
public IEnumerable<ChatEventInfo> GetChannelEventLog(string channelName, int amount = 20, int startAt = 0) {
|
||||||
List<StoredEventInfo> events = new();
|
List<ChatEventInfo> events = new();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
using MySqlDataReader? reader = RunQuery(
|
using MySqlDataReader? reader = RunQuery(
|
||||||
|
@ -107,7 +128,7 @@ namespace SharpChat.EventStorage {
|
||||||
|
|
||||||
if(reader != null)
|
if(reader != null)
|
||||||
while(reader.Read()) {
|
while(reader.Read()) {
|
||||||
StoredEventInfo evt = ReadEvent(reader);
|
ChatEventInfo evt = ReadEvent(reader);
|
||||||
if(evt != null)
|
if(evt != null)
|
||||||
events.Add(evt);
|
events.Add(evt);
|
||||||
}
|
}
|
||||||
|
@ -119,12 +140,5 @@ namespace SharpChat.EventStorage {
|
||||||
|
|
||||||
return events;
|
return events;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveEvent(StoredEventInfo evt) {
|
|
||||||
RunCommand(
|
|
||||||
"UPDATE IGNORE `sqc_events` SET `event_deleted` = NOW() WHERE `event_id` = @id AND `event_deleted` IS NULL",
|
|
||||||
new MySqlParameter("id", evt.Id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace SharpChat.EventStorage {
|
|
||||||
public class StoredEventInfo {
|
|
||||||
public long Id { get; set; }
|
|
||||||
public string Type { get; set; }
|
|
||||||
public UserInfo? Sender { get; set; }
|
|
||||||
public DateTimeOffset Created { get; set; }
|
|
||||||
public DateTimeOffset? Deleted { get; set; }
|
|
||||||
public string? ChannelName { get; set; }
|
|
||||||
public JsonDocument Data { get; set; }
|
|
||||||
|
|
||||||
public StoredEventInfo(
|
|
||||||
long id,
|
|
||||||
string type,
|
|
||||||
UserInfo? sender,
|
|
||||||
DateTimeOffset created,
|
|
||||||
DateTimeOffset? deleted,
|
|
||||||
string? channelName,
|
|
||||||
JsonDocument data
|
|
||||||
) {
|
|
||||||
Id = id;
|
|
||||||
Type = type;
|
|
||||||
Sender = sender;
|
|
||||||
Created = created;
|
|
||||||
Deleted = deleted;
|
|
||||||
ChannelName = channelName;
|
|
||||||
Data = data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,46 +1,24 @@
|
||||||
using System;
|
using SharpChat.Events;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace SharpChat.EventStorage {
|
namespace SharpChat.EventStorage {
|
||||||
public class VirtualEventStorage : IEventStorage {
|
public class VirtualEventStorage : IEventStorage {
|
||||||
private readonly Dictionary<long, StoredEventInfo> Events = new();
|
private readonly Dictionary<string, ChatEventInfo> Events = new();
|
||||||
|
|
||||||
public void AddEvent(
|
public void HandleEvent(ChatEventInfo info) {
|
||||||
long id,
|
Events.Add(info.Id.ToString(), info);
|
||||||
string type,
|
|
||||||
string? channelName,
|
if(info.Type.Equals("msg:delete") && info.Data is MessageDeleteEventData msgDelete)
|
||||||
long senderId,
|
Events.Remove(msgDelete.MessageId);
|
||||||
string? senderName,
|
|
||||||
Colour senderColour,
|
|
||||||
int senderRank,
|
|
||||||
string? senderNick,
|
|
||||||
UserPermissions senderPerms,
|
|
||||||
object? data = null
|
|
||||||
) {
|
|
||||||
// VES is meant as an emergency fallback but this is something else
|
|
||||||
JsonDocument hack = JsonDocument.Parse(data == null ? "{}" : JsonSerializer.Serialize(data));
|
|
||||||
Events.Add(id, new(id, type, senderId < 1 ? null : new UserInfo(
|
|
||||||
senderId,
|
|
||||||
senderName ?? string.Empty,
|
|
||||||
senderColour,
|
|
||||||
senderRank,
|
|
||||||
senderPerms,
|
|
||||||
senderNick
|
|
||||||
), DateTimeOffset.Now, null, channelName, hack));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public StoredEventInfo? GetEvent(long seqId) {
|
public ChatEventInfo? GetEvent(long eventId) {
|
||||||
return Events.TryGetValue(seqId, out StoredEventInfo? evt) ? evt : null;
|
return Events.TryGetValue(eventId.ToString(), out ChatEventInfo? evt) ? evt : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveEvent(StoredEventInfo evt) {
|
public IEnumerable<ChatEventInfo> GetChannelEventLog(string channelName, int amount = 20, int startAt = 0) {
|
||||||
Events.Remove(evt.Id);
|
IEnumerable<ChatEventInfo> subset = Events.Values.Where(ev => ev.ChannelName == channelName);
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<StoredEventInfo> GetChannelEventLog(string channelName, int amount = 20, int startAt = 0) {
|
|
||||||
IEnumerable<StoredEventInfo> subset = Events.Values.Where(ev => ev.ChannelName == channelName);
|
|
||||||
|
|
||||||
int start = subset.Count() - startAt - amount;
|
int start = subset.Count() - startAt - amount;
|
||||||
if(start < 0) {
|
if(start < 0) {
|
||||||
|
|
8
SharpChatCommon/Events/ChatEventData.cs
Normal file
8
SharpChatCommon/Events/ChatEventData.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SharpChat.Events {
|
||||||
|
public class ChatEventData {
|
||||||
|
[JsonIgnore]
|
||||||
|
public static readonly ChatEventData EmptyInstance = new();
|
||||||
|
}
|
||||||
|
}
|
12
SharpChatCommon/Events/ChatEventDataForAttribute.cs
Normal file
12
SharpChatCommon/Events/ChatEventDataForAttribute.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace SharpChat.Events {
|
||||||
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||||
|
public class ChatEventDataForAttribute : Attribute {
|
||||||
|
public string EventName { get; }
|
||||||
|
|
||||||
|
public ChatEventDataForAttribute(string eventName) {
|
||||||
|
EventName = eventName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
88
SharpChatCommon/Events/ChatEventDispatcher.cs
Normal file
88
SharpChatCommon/Events/ChatEventDispatcher.cs
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace SharpChat.Events {
|
||||||
|
public class ChatEventDispatcher {
|
||||||
|
private readonly object HandlersAccess = new();
|
||||||
|
private readonly List<IChatEventHandler> Handlers = new();
|
||||||
|
|
||||||
|
public void Subscribe(IChatEventHandler handler) {
|
||||||
|
lock(HandlersAccess)
|
||||||
|
if(!Handlers.Contains(handler))
|
||||||
|
Handlers.Add(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unsubscribe(IChatEventHandler handler) {
|
||||||
|
lock(HandlersAccess)
|
||||||
|
Handlers.Remove(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChatEventInfo Dispatch(ChatEventInfo info) {
|
||||||
|
lock(HandlersAccess)
|
||||||
|
foreach(IChatEventHandler handler in Handlers)
|
||||||
|
handler.HandleEvent(info);
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChatEventInfo Dispatch(
|
||||||
|
string eventType,
|
||||||
|
DateTimeOffset eventCreated,
|
||||||
|
ChannelInfo channelInfo,
|
||||||
|
UserInfo userInfo,
|
||||||
|
ChatEventData? eventData = null
|
||||||
|
) {
|
||||||
|
return Dispatch(new ChatEventInfo(
|
||||||
|
SharpId.Next(),
|
||||||
|
eventType,
|
||||||
|
eventCreated,
|
||||||
|
channelInfo.Name,
|
||||||
|
userInfo.UserId,
|
||||||
|
userInfo.UserName,
|
||||||
|
userInfo.Colour,
|
||||||
|
userInfo.Rank,
|
||||||
|
userInfo.NickName,
|
||||||
|
userInfo.Permissions,
|
||||||
|
eventData
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChatEventInfo Dispatch(
|
||||||
|
string eventType,
|
||||||
|
string channelName,
|
||||||
|
UserInfo userInfo,
|
||||||
|
ChatEventData? eventData = null
|
||||||
|
) {
|
||||||
|
return Dispatch(new ChatEventInfo(
|
||||||
|
SharpId.Next(),
|
||||||
|
eventType,
|
||||||
|
DateTimeOffset.UtcNow,
|
||||||
|
channelName,
|
||||||
|
userInfo.UserId,
|
||||||
|
userInfo.UserName,
|
||||||
|
userInfo.Colour,
|
||||||
|
userInfo.Rank,
|
||||||
|
userInfo.NickName,
|
||||||
|
userInfo.Permissions,
|
||||||
|
eventData
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChatEventInfo Dispatch(
|
||||||
|
string eventType,
|
||||||
|
ChannelInfo channelInfo,
|
||||||
|
UserInfo userInfo,
|
||||||
|
ChatEventData? eventData = null
|
||||||
|
) {
|
||||||
|
return Dispatch(eventType, channelInfo.Name, userInfo, eventData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChatEventInfo Dispatch(
|
||||||
|
string eventType,
|
||||||
|
UserInfo userInfo,
|
||||||
|
ChatEventData? eventData = null
|
||||||
|
) {
|
||||||
|
return Dispatch(eventType, string.Empty, userInfo, eventData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
SharpChatCommon/Events/ChatEventInfo.cs
Normal file
45
SharpChatCommon/Events/ChatEventInfo.cs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace SharpChat.Events {
|
||||||
|
public class ChatEventInfo {
|
||||||
|
public long Id { get; }
|
||||||
|
public string Type { get; }
|
||||||
|
public DateTimeOffset Created { get; }
|
||||||
|
public string ChannelName { get; }
|
||||||
|
public long SenderId { get; }
|
||||||
|
public string SenderName { get; }
|
||||||
|
public Colour SenderColour { get; }
|
||||||
|
public int SenderRank { get; }
|
||||||
|
public string? SenderNickName { get; }
|
||||||
|
public UserPermissions SenderPerms { get; }
|
||||||
|
public ChatEventData Data { get; }
|
||||||
|
|
||||||
|
public bool IsBroadcast => string.IsNullOrWhiteSpace(ChannelName);
|
||||||
|
|
||||||
|
public ChatEventInfo(
|
||||||
|
long id,
|
||||||
|
string type,
|
||||||
|
DateTimeOffset created,
|
||||||
|
string channelName,
|
||||||
|
long senderId,
|
||||||
|
string senderName,
|
||||||
|
Colour senderColour,
|
||||||
|
int senderRank,
|
||||||
|
string? senderNickName,
|
||||||
|
UserPermissions senderPerms,
|
||||||
|
ChatEventData? data = null
|
||||||
|
) {
|
||||||
|
Id = id;
|
||||||
|
Type = type;
|
||||||
|
Created = created;
|
||||||
|
ChannelName = channelName;
|
||||||
|
SenderId = senderId;
|
||||||
|
SenderName = senderName;
|
||||||
|
SenderColour = senderColour;
|
||||||
|
SenderRank = senderRank;
|
||||||
|
SenderNickName = senderNickName;
|
||||||
|
SenderPerms = senderPerms;
|
||||||
|
Data = data ?? ChatEventData.EmptyInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +0,0 @@
|
||||||
namespace SharpChat.Events {
|
|
||||||
public interface IChatEvent {
|
|
||||||
}
|
|
||||||
}
|
|
5
SharpChatCommon/Events/IChatEventHandler.cs
Normal file
5
SharpChatCommon/Events/IChatEventHandler.cs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
namespace SharpChat.Events {
|
||||||
|
public interface IChatEventHandler {
|
||||||
|
void HandleEvent(ChatEventInfo info);
|
||||||
|
}
|
||||||
|
}
|
17
SharpChatCommon/Events/MessageAddEventData.cs
Normal file
17
SharpChatCommon/Events/MessageAddEventData.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SharpChat.Events {
|
||||||
|
[ChatEventDataFor("msg:add")]
|
||||||
|
public class MessageAddEventData : ChatEventData {
|
||||||
|
[JsonPropertyName("text")]
|
||||||
|
public string Text { get; }
|
||||||
|
|
||||||
|
[JsonPropertyName("action")]
|
||||||
|
public bool IsAction { get; }
|
||||||
|
|
||||||
|
public MessageAddEventData(string text, bool isAction = false) {
|
||||||
|
Text = text;
|
||||||
|
IsAction = isAction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,94 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace SharpChat.Events {
|
|
||||||
public class MessageCreateEvent : IChatEvent {
|
|
||||||
public long MessageId { get; }
|
|
||||||
public string? ChannelName { get; }
|
|
||||||
public long SenderId { get; }
|
|
||||||
public string? SenderName { get; }
|
|
||||||
public Colour SenderColour { get; }
|
|
||||||
public int SenderRank { get; }
|
|
||||||
public string? SenderNickName { get; }
|
|
||||||
public UserPermissions SenderPerms { get; }
|
|
||||||
public DateTimeOffset MessageCreated { get; }
|
|
||||||
public string MessageText { get; }
|
|
||||||
public bool IsPrivate { get; }
|
|
||||||
public bool IsAction { get; }
|
|
||||||
public bool IsBroadcast { get; }
|
|
||||||
|
|
||||||
public MessageCreateEvent(
|
|
||||||
long msgId,
|
|
||||||
string? channelName,
|
|
||||||
long senderId,
|
|
||||||
string? senderName,
|
|
||||||
Colour senderColour,
|
|
||||||
int senderRank,
|
|
||||||
string? senderNickName,
|
|
||||||
UserPermissions senderPerms,
|
|
||||||
DateTimeOffset msgCreated,
|
|
||||||
string msgText,
|
|
||||||
bool isPrivate,
|
|
||||||
bool isAction,
|
|
||||||
bool isBroadcast
|
|
||||||
) {
|
|
||||||
MessageId = msgId;
|
|
||||||
ChannelName = channelName;
|
|
||||||
SenderId = senderId;
|
|
||||||
SenderName = senderName;
|
|
||||||
SenderColour = senderColour;
|
|
||||||
SenderRank = senderRank;
|
|
||||||
SenderNickName = senderNickName;
|
|
||||||
SenderPerms = senderPerms;
|
|
||||||
MessageCreated = msgCreated;
|
|
||||||
MessageText = msgText;
|
|
||||||
IsPrivate = isPrivate;
|
|
||||||
IsAction = isAction;
|
|
||||||
IsBroadcast = isBroadcast;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MessageCreateEvent(
|
|
||||||
long msgId,
|
|
||||||
string? channelName,
|
|
||||||
UserInfo? sender,
|
|
||||||
DateTimeOffset msgCreated,
|
|
||||||
string msgText,
|
|
||||||
bool isPrivate,
|
|
||||||
bool isAction,
|
|
||||||
bool isBroadcast
|
|
||||||
) : this(
|
|
||||||
msgId,
|
|
||||||
channelName,
|
|
||||||
sender?.UserId ?? -1,
|
|
||||||
sender?.UserName ?? null,
|
|
||||||
sender?.Colour ?? Colour.None,
|
|
||||||
sender?.Rank ?? 0,
|
|
||||||
sender?.NickName ?? null,
|
|
||||||
sender?.Permissions ?? 0,
|
|
||||||
msgCreated,
|
|
||||||
msgText,
|
|
||||||
isPrivate,
|
|
||||||
isAction,
|
|
||||||
isBroadcast
|
|
||||||
) { }
|
|
||||||
|
|
||||||
public MessageCreateEvent(
|
|
||||||
long msgId,
|
|
||||||
ChannelInfo channel,
|
|
||||||
UserInfo sender,
|
|
||||||
DateTimeOffset msgCreated,
|
|
||||||
string msgText,
|
|
||||||
bool isPrivate,
|
|
||||||
bool isAction,
|
|
||||||
bool isBroadcast
|
|
||||||
) : this(
|
|
||||||
msgId,
|
|
||||||
channel?.Name ?? null,
|
|
||||||
sender,
|
|
||||||
msgCreated,
|
|
||||||
msgText,
|
|
||||||
isPrivate,
|
|
||||||
isAction,
|
|
||||||
isBroadcast
|
|
||||||
) { }
|
|
||||||
}
|
|
||||||
}
|
|
13
SharpChatCommon/Events/MessageDeleteEventData.cs
Normal file
13
SharpChatCommon/Events/MessageDeleteEventData.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SharpChat.Events {
|
||||||
|
[ChatEventDataFor("msg:delete")]
|
||||||
|
public class MessageDeleteEventData : ChatEventData {
|
||||||
|
[JsonPropertyName("msg")]
|
||||||
|
public string MessageId { get; }
|
||||||
|
|
||||||
|
public MessageDeleteEventData(string messageId) {
|
||||||
|
MessageId = messageId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
SharpChatCommon/Events/UserDisconnectEventData.cs
Normal file
13
SharpChatCommon/Events/UserDisconnectEventData.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SharpChat.Events {
|
||||||
|
[ChatEventDataFor("user:disconnect")]
|
||||||
|
public class UserDisconnectEventData : ChatEventData {
|
||||||
|
[JsonPropertyName("reason")]
|
||||||
|
public UserDisconnectReason Reason { get; }
|
||||||
|
|
||||||
|
public UserDisconnectEventData(UserDisconnectReason reason) {
|
||||||
|
Reason = reason;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
namespace SharpChat {
|
namespace SharpChat {
|
||||||
public enum UserDisconnectReason {
|
public enum UserDisconnectReason : int {
|
||||||
Leave,
|
Leave = 0,
|
||||||
TimeOut,
|
TimeOut = 1,
|
||||||
Kicked,
|
Kicked = 2,
|
||||||
Flood,
|
Flood = 3,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ namespace SharpChat {
|
||||||
public enum UserPermissions : int {
|
public enum UserPermissions : int {
|
||||||
KickUser = 0x00000001,
|
KickUser = 0x00000001,
|
||||||
BanUser = 0x00000002,
|
BanUser = 0x00000002,
|
||||||
//SilenceUser = 0x00000004,
|
//SilenceUser = 0x00000004,
|
||||||
Broadcast = 0x00000008,
|
Broadcast = 0x00000008,
|
||||||
SetOwnNickname = 0x00000010,
|
SetOwnNickname = 0x00000010,
|
||||||
SetOthersNickname = 0x00000020,
|
SetOthersNickname = 0x00000020,
|
||||||
|
|
Loading…
Reference in a new issue