Revised event storage to use less magic classes.
This commit is contained in:
parent
82973f7a33
commit
4e0def980f
18 changed files with 250 additions and 325 deletions
|
@ -1,13 +1,14 @@
|
|||
using SharpChat.Events;
|
||||
using SharpChat.EventStorage;
|
||||
using SharpChat.EventStorage;
|
||||
using SharpChat.Packet;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Tracing;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
|
||||
namespace SharpChat {
|
||||
namespace SharpChat
|
||||
{
|
||||
public class ChatContext {
|
||||
public record ChannelUserAssoc(long UserId, string ChannelName);
|
||||
|
||||
|
@ -148,13 +149,13 @@ namespace SharpChat {
|
|||
public void HandleJoin(ChatUser user, ChatChannel chan, ChatConnection conn, int maxMsgLength) {
|
||||
if(!IsInChannel(user, chan)) {
|
||||
SendTo(chan, new UserConnectPacket(DateTimeOffset.Now, user));
|
||||
Events.AddEvent(new UserConnectEvent(DateTimeOffset.Now, user, chan));
|
||||
Events.AddEvent("user:connect", user, chan, flags: StoredEventFlags.Log);
|
||||
}
|
||||
|
||||
conn.Send(new AuthSuccessPacket(user, chan, conn, maxMsgLength));
|
||||
conn.Send(new ContextUsersPacket(GetChannelUsers(chan).Except(new[] { user }).OrderByDescending(u => u.Rank)));
|
||||
|
||||
foreach(IChatEvent msg in Events.GetChannelEventLog(chan.Name))
|
||||
foreach(StoredEventInfo msg in Events.GetChannelEventLog(chan.Name))
|
||||
conn.Send(new ContextMessagePacket(msg));
|
||||
|
||||
conn.Send(new ContextChannelsPacket(Channels.Where(c => c.Rank <= user.Rank)));
|
||||
|
@ -176,7 +177,7 @@ namespace SharpChat {
|
|||
ChannelUsers.Remove(new ChannelUserAssoc(user.UserId, chan.Name));
|
||||
|
||||
SendTo(chan, new UserDisconnectPacket(DateTimeOffset.Now, user, reason));
|
||||
Events.AddEvent(new UserDisconnectEvent(DateTimeOffset.Now, user, chan, reason));
|
||||
Events.AddEvent("user:disconnect", user, chan, new { reason = (int)reason }, StoredEventFlags.Log);
|
||||
|
||||
if(chan.IsTemporary && chan.IsOwner(user))
|
||||
RemoveChannel(chan);
|
||||
|
@ -213,14 +214,14 @@ namespace SharpChat {
|
|||
ChatChannel oldChan = UserLastChannel[user.UserId];
|
||||
|
||||
SendTo(oldChan, new UserChannelLeavePacket(user));
|
||||
Events.AddEvent(new UserChannelLeaveEvent(DateTimeOffset.Now, user, oldChan));
|
||||
Events.AddEvent("chan:leave", user, oldChan, flags: StoredEventFlags.Log);
|
||||
SendTo(chan, new UserChannelJoinPacket(user));
|
||||
Events.AddEvent(new UserChannelJoinEvent(DateTimeOffset.Now, user, chan));
|
||||
Events.AddEvent("chan:join", user, oldChan, flags: StoredEventFlags.Log);
|
||||
|
||||
SendTo(user, new ContextClearPacket(chan, ContextClearMode.MessagesUsers));
|
||||
SendTo(user, new ContextUsersPacket(GetChannelUsers(chan).Except(new[] { user }).OrderByDescending(u => u.Rank)));
|
||||
|
||||
foreach(IChatEvent msg in Events.GetChannelEventLog(chan.Name))
|
||||
foreach(StoredEventInfo msg in Events.GetChannelEventLog(chan.Name))
|
||||
SendTo(user, new ContextMessagePacket(msg));
|
||||
|
||||
ForceChannel(user, chan);
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
using SharpChat.Events;
|
||||
using SharpChat.EventStorage;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace SharpChat.Commands {
|
||||
namespace SharpChat.Commands
|
||||
{
|
||||
public class ActionCommand : IChatCommand {
|
||||
public bool IsMatch(ChatCommandContext ctx) {
|
||||
return ctx.NameEquals("action")
|
||||
|
@ -13,17 +14,8 @@ namespace SharpChat.Commands {
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ChatMessage ActionDispatch(ChatCommandContext ctx) {
|
||||
if(!ctx.Args.Any())
|
||||
return null;
|
||||
|
||||
return new ChatMessage {
|
||||
ChannelName = ctx.Channel.Name,
|
||||
DateTime = DateTimeOffset.UtcNow,
|
||||
Sender = ctx.User,
|
||||
Text = string.Join(' ', ctx.Args),
|
||||
Flags = ChatMessageFlags.Action,
|
||||
};
|
||||
public (string, bool)? ActionDispatch(ChatCommandContext ctx) {
|
||||
return ctx.Args.Any() ? (string.Join(' ', ctx.Args), true) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
using SharpChat.Events;
|
||||
using SharpChat.EventStorage;
|
||||
using SharpChat.Packet;
|
||||
using System.Linq;
|
||||
|
||||
namespace SharpChat.Commands {
|
||||
namespace SharpChat.Commands
|
||||
{
|
||||
public class DeleteMessageCommand : IChatCommand {
|
||||
public bool IsMatch(ChatCommandContext ctx) {
|
||||
return ctx.NameEquals("delmsg") || (
|
||||
|
@ -26,7 +27,7 @@ namespace SharpChat.Commands {
|
|||
return;
|
||||
}
|
||||
|
||||
IChatEvent 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)) {
|
||||
ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.MESSAGE_DELETE_ERROR));
|
||||
|
@ -34,7 +35,7 @@ namespace SharpChat.Commands {
|
|||
}
|
||||
|
||||
ctx.Chat.Events.RemoveEvent(delMsg);
|
||||
ctx.Chat.Send(new ChatMessageDeletePacket(delMsg.SequenceId));
|
||||
ctx.Chat.Send(new ChatMessageDeletePacket(delMsg.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
using SharpChat.Events;
|
||||
using SharpChat.EventStorage;
|
||||
using SharpChat.Packet;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace SharpChat.Commands {
|
||||
namespace SharpChat.Commands
|
||||
{
|
||||
public class WhisperCommand : IChatCommand {
|
||||
public bool IsMatch(ChatCommandContext ctx) {
|
||||
return ctx.NameEquals("whisper")
|
||||
|
@ -31,20 +32,22 @@ namespace SharpChat.Commands {
|
|||
string whisperChan = ChatUser.GetDMChannelName(ctx.User, whisperUser);
|
||||
DateTimeOffset dateTime = DateTimeOffset.Now;
|
||||
|
||||
ctx.Chat.SendTo(whisperUser, new ChatMessageAddPacket(new ChatMessage {
|
||||
DateTime = dateTime,
|
||||
ChannelName = whisperChan,
|
||||
Sender = ctx.User,
|
||||
Text = whisperStr,
|
||||
Flags = ChatMessageFlags.Private,
|
||||
}));
|
||||
ctx.Chat.SendTo(ctx.User, new ChatMessageAddPacket(new ChatMessage {
|
||||
DateTime = dateTime,
|
||||
ChannelName = whisperChan,
|
||||
Sender = ctx.User,
|
||||
Text = $"{whisperUser.LegacyName} {whisperStr}",
|
||||
Flags = ChatMessageFlags.Private,
|
||||
}));
|
||||
ctx.Chat.SendTo(whisperUser, new ChatMessageAddPacket(
|
||||
SharpId.Next(),
|
||||
dateTime,
|
||||
ctx.User.UserId,
|
||||
whisperStr,
|
||||
false,
|
||||
true
|
||||
));
|
||||
ctx.Chat.SendTo(ctx.User, new ChatMessageAddPacket(
|
||||
SharpId.Next(),
|
||||
dateTime,
|
||||
ctx.User.UserId,
|
||||
$"{whisperUser.LegacyName} {whisperStr}",
|
||||
false,
|
||||
true
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
using SharpChat.Events;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SharpChat.EventStorage {
|
||||
namespace SharpChat.EventStorage
|
||||
{
|
||||
public interface IEventStorage {
|
||||
void AddEvent(IChatEvent evt);
|
||||
void RemoveEvent(IChatEvent evt);
|
||||
IChatEvent GetEvent(long seqId);
|
||||
IEnumerable<IChatEvent> GetChannelEventLog(string channelName, int amount = 20, int offset = 0);
|
||||
long AddEvent(string type, ChatUser user, ChatChannel channel, object data = null, StoredEventFlags flags = StoredEventFlags.None);
|
||||
void RemoveEvent(StoredEventInfo evt);
|
||||
StoredEventInfo GetEvent(long seqId);
|
||||
IEnumerable<StoredEventInfo> GetChannelEventLog(string channelName, int amount = 20, int offset = 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
using MySqlConnector;
|
||||
using SharpChat.Events;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace SharpChat.EventStorage {
|
||||
namespace SharpChat.EventStorage
|
||||
{
|
||||
public partial class MariaDBEventStorage : IEventStorage {
|
||||
private string ConnectionString { get; }
|
||||
|
||||
|
@ -13,46 +14,47 @@ namespace SharpChat.EventStorage {
|
|||
ConnectionString = connString ?? throw new ArgumentNullException(nameof(connString));
|
||||
}
|
||||
|
||||
public void AddEvent(IChatEvent evt) {
|
||||
if(evt == null)
|
||||
throw new ArgumentNullException(nameof(evt));
|
||||
public long AddEvent(string type, ChatUser user, ChatChannel channel, object data = null, StoredEventFlags flags = StoredEventFlags.None) {
|
||||
if(type == null)
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
|
||||
if(evt.SequenceId < 1)
|
||||
evt.SequenceId = SharpId.Next();
|
||||
long id = SharpId.Next();
|
||||
|
||||
RunCommand(
|
||||
"INSERT INTO `sqc_events` (`event_id`, `event_created`, `event_type`, `event_target`, `event_flags`, `event_data`"
|
||||
+ ", `event_sender`, `event_sender_name`, `event_sender_colour`, `event_sender_rank`, `event_sender_nick`, `event_sender_perms`)"
|
||||
+ " VALUES (@id, FROM_UNIXTIME(@created), @type, @target, @flags, @data"
|
||||
+ " VALUES (@id, NOW(), @type, @target, @flags, @data"
|
||||
+ ", @sender, @sender_name, @sender_colour, @sender_rank, @sender_nick, @sender_perms)",
|
||||
new MySqlParameter("id", evt.SequenceId),
|
||||
new MySqlParameter("created", evt.DateTime.ToUnixTimeSeconds()),
|
||||
new MySqlParameter("type", evt.GetType().FullName),
|
||||
new MySqlParameter("target", evt.ChannelName),
|
||||
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_colour", evt.Sender?.Colour.ToMisuzu()),
|
||||
new MySqlParameter("sender_rank", evt.Sender?.Rank),
|
||||
new MySqlParameter("sender_nick", evt.Sender?.NickName),
|
||||
new MySqlParameter("sender_perms", evt.Sender?.Permissions)
|
||||
new MySqlParameter("id", id),
|
||||
new MySqlParameter("type", type),
|
||||
new MySqlParameter("target", channel?.Name ?? null),
|
||||
new MySqlParameter("flags", (byte)flags),
|
||||
new MySqlParameter("data", data == null ? "{}" : JsonSerializer.SerializeToUtf8Bytes(data)),
|
||||
new MySqlParameter("sender", user?.UserId < 1 ? null : (long?)user.UserId),
|
||||
new MySqlParameter("sender_name", user?.UserName),
|
||||
new MySqlParameter("sender_colour", user?.Colour.ToMisuzu()),
|
||||
new MySqlParameter("sender_rank", user?.Rank),
|
||||
new MySqlParameter("sender_nick", string.IsNullOrWhiteSpace(user?.NickName) ? null : user.NickName),
|
||||
new MySqlParameter("sender_perms", user?.Permissions)
|
||||
);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
public IChatEvent GetEvent(long seqId) {
|
||||
public StoredEventInfo GetEvent(long seqId) {
|
||||
try {
|
||||
using MySqlDataReader reader = RunQuery(
|
||||
"SELECT `event_id`, `event_type`, `event_flags`, `event_data`, `event_target`"
|
||||
+ ", `event_sender`, `event_sender_name`, `event_sender_colour`, `event_sender_rank`, `event_sender_nick`, `event_sender_perms`"
|
||||
+ ", UNIX_TIMESTAMP(`event_created`) AS `event_created`"
|
||||
+ ", UNIX_TIMESTAMP(`event_deleted`) AS `event_deleted`"
|
||||
+ " FROM `sqc_events`"
|
||||
+ " WHERE `event_id` = @id",
|
||||
new MySqlParameter("id", seqId)
|
||||
);
|
||||
|
||||
while(reader.Read()) {
|
||||
IChatEvent evt = ReadEvent(reader);
|
||||
StoredEventInfo evt = ReadEvent(reader);
|
||||
if(evt != null)
|
||||
return evt;
|
||||
}
|
||||
|
@ -63,36 +65,35 @@ namespace SharpChat.EventStorage {
|
|||
return null;
|
||||
}
|
||||
|
||||
private static IChatEvent ReadEvent(MySqlDataReader reader) {
|
||||
Type evtType = Type.GetType(Encoding.ASCII.GetString((byte[])reader["event_type"]));
|
||||
IChatEvent evt = JsonSerializer.Deserialize(Encoding.ASCII.GetString((byte[])reader["event_data"]), evtType) as IChatEvent;
|
||||
evt.SequenceId = reader.GetInt64("event_id");
|
||||
evt.ChannelName = Encoding.ASCII.GetString((byte[])reader["event_target"]);
|
||||
evt.Flags = (ChatMessageFlags)reader.GetByte("event_flags");
|
||||
evt.DateTime = DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32("event_created"));
|
||||
|
||||
if(!reader.IsDBNull(reader.GetOrdinal("event_sender"))) {
|
||||
evt.Sender = new ChatUser(
|
||||
private static StoredEventInfo ReadEvent(MySqlDataReader reader) {
|
||||
return new StoredEventInfo(
|
||||
reader.GetInt64("event_id"),
|
||||
Encoding.ASCII.GetString((byte[])reader["event_type"]),
|
||||
reader.IsDBNull(reader.GetOrdinal("event_sender")) ? null : 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;
|
||||
),
|
||||
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")) ? null : Encoding.ASCII.GetString((byte[])reader["event_target"]),
|
||||
JsonDocument.Parse(Encoding.ASCII.GetString((byte[])reader["event_data"])),
|
||||
(StoredEventFlags)reader.GetByte("event_flags")
|
||||
);
|
||||
}
|
||||
|
||||
public IEnumerable<IChatEvent> GetChannelEventLog(string channelName, int amount = 20, int offset = 0) {
|
||||
List<IChatEvent> events = new();
|
||||
public IEnumerable<StoredEventInfo> GetChannelEventLog(string channelName, int amount = 20, int offset = 0) {
|
||||
List<StoredEventInfo> events = new();
|
||||
|
||||
try {
|
||||
using MySqlDataReader reader = RunQuery(
|
||||
"SELECT `event_id`, `event_type`, `event_flags`, `event_data`, `event_target`"
|
||||
+ ", `event_sender`, `event_sender_name`, `event_sender_colour`, `event_sender_rank`, `event_sender_nick`, `event_sender_perms`"
|
||||
+ ", UNIX_TIMESTAMP(`event_created`) AS `event_created`"
|
||||
+ ", UNIX_TIMESTAMP(`event_deleted`) AS `event_deleted`"
|
||||
+ " FROM `sqc_events`"
|
||||
+ " WHERE `event_deleted` IS NULL AND `event_target` = @target"
|
||||
+ " AND `event_id` > @offset"
|
||||
|
@ -104,7 +105,7 @@ namespace SharpChat.EventStorage {
|
|||
);
|
||||
|
||||
while(reader.Read()) {
|
||||
IChatEvent evt = ReadEvent(reader);
|
||||
StoredEventInfo evt = ReadEvent(reader);
|
||||
if(evt != null)
|
||||
events.Add(evt);
|
||||
}
|
||||
|
@ -117,12 +118,12 @@ namespace SharpChat.EventStorage {
|
|||
return events;
|
||||
}
|
||||
|
||||
public void RemoveEvent(IChatEvent evt) {
|
||||
public void RemoveEvent(StoredEventInfo evt) {
|
||||
if(evt == null)
|
||||
throw new ArgumentNullException(nameof(evt));
|
||||
RunCommand(
|
||||
"UPDATE IGNORE `sqc_events` SET `event_deleted` = NOW() WHERE `event_id` = @id AND `event_deleted` IS NULL",
|
||||
new MySqlParameter("id", evt.SequenceId)
|
||||
new MySqlParameter("id", evt.Id)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
14
SharpChat/EventStorage/StoredEventFlags.cs
Normal file
14
SharpChat/EventStorage/StoredEventFlags.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
|
||||
namespace SharpChat.EventStorage
|
||||
{
|
||||
[Flags]
|
||||
public enum StoredEventFlags
|
||||
{
|
||||
None = 0,
|
||||
Action = 1,
|
||||
Broadcast = 1 << 1,
|
||||
Log = 1 << 2,
|
||||
Private = 1 << 3,
|
||||
}
|
||||
}
|
35
SharpChat/EventStorage/StoredEventInfo.cs
Normal file
35
SharpChat/EventStorage/StoredEventInfo.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace SharpChat.EventStorage {
|
||||
public class StoredEventInfo {
|
||||
public long Id { get; set; }
|
||||
public string Type { get; set; }
|
||||
public ChatUser Sender { get; set; }
|
||||
public DateTimeOffset Created { get; set; }
|
||||
public DateTimeOffset? Deleted { get; set; }
|
||||
public string ChannelName { get; set; }
|
||||
public StoredEventFlags Flags { get; set; }
|
||||
public JsonDocument Data { get; set; }
|
||||
|
||||
public StoredEventInfo(
|
||||
long id,
|
||||
string type,
|
||||
ChatUser sender,
|
||||
DateTimeOffset created,
|
||||
DateTimeOffset? deleted,
|
||||
string channelName,
|
||||
JsonDocument data,
|
||||
StoredEventFlags flags
|
||||
) {
|
||||
Id = id;
|
||||
Type = type;
|
||||
Sender = sender;
|
||||
Created = created;
|
||||
Deleted = deleted;
|
||||
ChannelName = channelName;
|
||||
Data = data;
|
||||
Flags = flags;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,30 +1,38 @@
|
|||
using SharpChat.Events;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace SharpChat.EventStorage {
|
||||
namespace SharpChat.EventStorage
|
||||
{
|
||||
public class VirtualEventStorage : IEventStorage {
|
||||
private readonly Dictionary<long, IChatEvent> Events = new();
|
||||
private readonly Dictionary<long, StoredEventInfo> Events = new();
|
||||
|
||||
public void AddEvent(IChatEvent evt) {
|
||||
public long AddEvent(string type, ChatUser user, ChatChannel channel, object data = null, StoredEventFlags flags = StoredEventFlags.None) {
|
||||
if(type == null)
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
|
||||
// VES is meant as an emergency fallback but this is something else
|
||||
JsonDocument hack = JsonDocument.Parse(data == null ? "{}" : JsonSerializer.Serialize(data));
|
||||
|
||||
long id = SharpId.Next();
|
||||
Events.Add(id, new(id, type, user, DateTimeOffset.Now, null, channel?.Name ?? null, hack, flags));
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
public StoredEventInfo GetEvent(long seqId) {
|
||||
return Events.TryGetValue(seqId, out StoredEventInfo evt) ? evt : null;
|
||||
}
|
||||
|
||||
public void RemoveEvent(StoredEventInfo evt) {
|
||||
if(evt == null)
|
||||
throw new ArgumentNullException(nameof(evt));
|
||||
Events.Add(evt.SequenceId, evt);
|
||||
Events.Remove(evt.Id);
|
||||
}
|
||||
|
||||
public IChatEvent GetEvent(long seqId) {
|
||||
return Events.TryGetValue(seqId, out IChatEvent evt) ? evt : null;
|
||||
}
|
||||
|
||||
public void RemoveEvent(IChatEvent evt) {
|
||||
if(evt == null)
|
||||
throw new ArgumentNullException(nameof(evt));
|
||||
Events.Remove(evt.SequenceId);
|
||||
}
|
||||
|
||||
public IEnumerable<IChatEvent> GetChannelEventLog(string channelName, int amount = 20, int offset = 0) {
|
||||
IEnumerable<IChatEvent> subset = Events.Values.Where(ev => ev.ChannelName == channelName);
|
||||
public IEnumerable<StoredEventInfo> GetChannelEventLog(string channelName, int amount = 20, int offset = 0) {
|
||||
IEnumerable<StoredEventInfo> subset = Events.Values.Where(ev => ev.ChannelName == channelName);
|
||||
|
||||
int start = subset.Count() - offset - amount;
|
||||
if(start < 0) {
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace SharpChat.Events {
|
||||
public class ChatMessage : IChatMessage {
|
||||
[JsonIgnore]
|
||||
public ChatUser Sender { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string ChannelName { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public DateTimeOffset DateTime { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public ChatMessageFlags Flags { get; set; } = ChatMessageFlags.None;
|
||||
|
||||
[JsonIgnore]
|
||||
public long SequenceId { get; set; }
|
||||
|
||||
[JsonPropertyName("text")]
|
||||
public string Text { get; set; }
|
||||
|
||||
public static string PackBotMessage(int type, string id, params string[] parts) {
|
||||
return type.ToString() + '\f' + id + '\f' + string.Join('\f', parts);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace SharpChat.Events {
|
||||
[Flags]
|
||||
public enum ChatMessageFlags {
|
||||
None = 0,
|
||||
Action = 1,
|
||||
Broadcast = 1 << 1,
|
||||
Log = 1 << 2,
|
||||
Private = 1 << 3,
|
||||
}
|
||||
|
||||
public interface IChatEvent {
|
||||
DateTimeOffset DateTime { get; set; }
|
||||
ChatUser Sender { get; set; }
|
||||
string ChannelName { get; set; }
|
||||
ChatMessageFlags Flags { get; set; }
|
||||
long SequenceId { get; set; }
|
||||
}
|
||||
|
||||
public interface IChatMessage : IChatEvent {
|
||||
string Text { get; }
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace SharpChat.Events {
|
||||
public class UserChannelJoinEvent : IChatEvent {
|
||||
[JsonIgnore]
|
||||
public DateTimeOffset DateTime { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public ChatUser Sender { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string ChannelName { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public ChatMessageFlags Flags { get; set; } = ChatMessageFlags.Log;
|
||||
|
||||
[JsonIgnore]
|
||||
public long SequenceId { get; set; }
|
||||
|
||||
public UserChannelJoinEvent() { }
|
||||
public UserChannelJoinEvent(DateTimeOffset joined, ChatUser user, ChatChannel channel) {
|
||||
DateTime = joined;
|
||||
Sender = user;
|
||||
ChannelName = channel.Name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace SharpChat.Events {
|
||||
public class UserChannelLeaveEvent : IChatEvent {
|
||||
[JsonIgnore]
|
||||
public DateTimeOffset DateTime { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public ChatUser Sender { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string ChannelName { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public ChatMessageFlags Flags { get; set; } = ChatMessageFlags.Log;
|
||||
|
||||
[JsonIgnore]
|
||||
public long SequenceId { get; set; }
|
||||
|
||||
public UserChannelLeaveEvent() { }
|
||||
public UserChannelLeaveEvent(DateTimeOffset parted, ChatUser user, ChatChannel channel) {
|
||||
DateTime = parted;
|
||||
Sender = user;
|
||||
ChannelName = channel.Name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace SharpChat.Events {
|
||||
public class UserConnectEvent : IChatEvent {
|
||||
[JsonIgnore]
|
||||
public DateTimeOffset DateTime { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public ChatUser Sender { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string ChannelName { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public ChatMessageFlags Flags { get; set; } = ChatMessageFlags.Log;
|
||||
|
||||
[JsonIgnore]
|
||||
public long SequenceId { get; set; }
|
||||
|
||||
public UserConnectEvent() { }
|
||||
public UserConnectEvent(DateTimeOffset joined, ChatUser user, ChatChannel channel) {
|
||||
DateTime = joined;
|
||||
Sender = user;
|
||||
ChannelName = channel.Name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
using SharpChat.Packet;
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace SharpChat.Events {
|
||||
public class UserDisconnectEvent : IChatEvent {
|
||||
|
||||
[JsonIgnore]
|
||||
public DateTimeOffset DateTime { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public ChatUser Sender { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string ChannelName { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public ChatMessageFlags Flags { get; set; } = ChatMessageFlags.Log;
|
||||
|
||||
[JsonIgnore]
|
||||
public long SequenceId { get; set; }
|
||||
|
||||
[JsonPropertyName("reason")]
|
||||
public UserDisconnectReason Reason { get; set; }
|
||||
|
||||
public UserDisconnectEvent() { }
|
||||
public UserDisconnectEvent(DateTimeOffset parted, ChatUser user, ChatChannel channel, UserDisconnectReason reason) {
|
||||
DateTime = parted;
|
||||
Sender = user;
|
||||
ChannelName = channel.Name;
|
||||
Reason = reason;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +1,46 @@
|
|||
using SharpChat.Events;
|
||||
using SharpChat.EventStorage;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SharpChat.Packet {
|
||||
namespace SharpChat.Packet
|
||||
{
|
||||
public class ChatMessageAddPacket : ServerPacket {
|
||||
public IChatMessage Message { get; private set; }
|
||||
public DateTimeOffset Created { get; }
|
||||
public long UserId { get; }
|
||||
public string Text { get; }
|
||||
public bool IsAction { get; }
|
||||
public bool IsPrivate { get; }
|
||||
|
||||
public ChatMessageAddPacket(IChatMessage message) : base(message?.SequenceId ?? 0) {
|
||||
Message = message ?? throw new ArgumentNullException(nameof(message));
|
||||
public ChatMessageAddPacket(
|
||||
long msgId,
|
||||
DateTimeOffset created,
|
||||
long userId,
|
||||
string text,
|
||||
bool isAction,
|
||||
bool isPrivate
|
||||
) : base(msgId) {
|
||||
Created = created;
|
||||
UserId = userId < 0 ? -1 : userId;
|
||||
Text = text;
|
||||
IsAction = isAction;
|
||||
IsPrivate = isPrivate;
|
||||
}
|
||||
|
||||
if(Message.SequenceId < 1)
|
||||
Message.SequenceId = SequenceId;
|
||||
public static ChatMessageAddPacket FromStoredEvent(StoredEventInfo sei) {
|
||||
if(sei == null)
|
||||
throw new ArgumentNullException(nameof(sei));
|
||||
if(sei.Type is not "msg:add" and not "SharpChat.Events.ChatMessage")
|
||||
throw new ArgumentException("Wrong event type.", nameof(sei));
|
||||
|
||||
return new ChatMessageAddPacket(
|
||||
sei.Id,
|
||||
sei.Created,
|
||||
sei.Sender?.UserId ?? -1,
|
||||
string.Empty, // todo: this
|
||||
(sei.Flags & StoredEventFlags.Action) > 0,
|
||||
(sei.Flags & StoredEventFlags.Private) > 0
|
||||
);
|
||||
}
|
||||
|
||||
public override IEnumerable<string> Pack() {
|
||||
|
@ -20,33 +49,32 @@ namespace SharpChat.Packet {
|
|||
sb.Append('2');
|
||||
sb.Append('\t');
|
||||
|
||||
sb.Append(Message.DateTime.ToUnixTimeSeconds());
|
||||
sb.Append(Created.ToUnixTimeSeconds());
|
||||
sb.Append('\t');
|
||||
|
||||
sb.Append(Message.Sender?.UserId ?? -1);
|
||||
sb.Append(UserId);
|
||||
sb.Append('\t');
|
||||
|
||||
if(Message.Flags.HasFlag(ChatMessageFlags.Action))
|
||||
if(IsAction)
|
||||
sb.Append("<i>");
|
||||
|
||||
sb.Append(
|
||||
Message.Text
|
||||
.Replace("<", "<")
|
||||
Text.Replace("<", "<")
|
||||
.Replace(">", ">")
|
||||
.Replace("\n", " <br/> ")
|
||||
.Replace("\t", " ")
|
||||
);
|
||||
|
||||
if(Message.Flags.HasFlag(ChatMessageFlags.Action))
|
||||
if(IsAction)
|
||||
sb.Append("</i>");
|
||||
|
||||
sb.Append('\t');
|
||||
sb.Append(SequenceId);
|
||||
sb.AppendFormat(
|
||||
"\t1{0}0{1}{2}",
|
||||
Message.Flags.HasFlag(ChatMessageFlags.Action) ? '1' : '0',
|
||||
Message.Flags.HasFlag(ChatMessageFlags.Action) ? '0' : '1',
|
||||
Message.Flags.HasFlag(ChatMessageFlags.Private) ? '1' : '0'
|
||||
IsAction ? '1' : '0',
|
||||
IsAction ? '0' : '1',
|
||||
IsPrivate ? '1' : '0'
|
||||
);
|
||||
|
||||
yield return sb.ToString();
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
using SharpChat.Events;
|
||||
using SharpChat.EventStorage;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SharpChat.Packet {
|
||||
namespace SharpChat.Packet
|
||||
{
|
||||
public class ContextMessagePacket : ServerPacket {
|
||||
public IChatEvent Event { get; private set; }
|
||||
public StoredEventInfo Event { get; private set; }
|
||||
public bool Notify { get; private set; }
|
||||
|
||||
public ContextMessagePacket(IChatEvent evt, bool notify = false) {
|
||||
public ContextMessagePacket(StoredEventInfo evt, bool notify = false) {
|
||||
Event = evt ?? throw new ArgumentNullException(nameof(evt));
|
||||
Notify = notify;
|
||||
}
|
||||
|
@ -22,15 +23,16 @@ namespace SharpChat.Packet {
|
|||
sb.Append('\t');
|
||||
sb.Append('1');
|
||||
sb.Append('\t');
|
||||
sb.Append(Event.DateTime.ToUnixTimeSeconds());
|
||||
sb.Append(Event.Created.ToUnixTimeSeconds());
|
||||
sb.Append('\t');
|
||||
|
||||
switch(Event) {
|
||||
case IChatMessage msg:
|
||||
switch(Event.Type) {
|
||||
case "msg:add":
|
||||
case "SharpChat.Events.ChatMessage":
|
||||
sb.Append(Event.Sender.Pack());
|
||||
sb.Append('\t');
|
||||
sb.Append(
|
||||
msg.Text
|
||||
Event.Data.RootElement.GetProperty("text").GetString()
|
||||
.Replace("<", "<")
|
||||
.Replace(">", ">")
|
||||
.Replace("\n", " <br/> ")
|
||||
|
@ -38,29 +40,33 @@ namespace SharpChat.Packet {
|
|||
);
|
||||
break;
|
||||
|
||||
case UserConnectEvent _:
|
||||
case "user:connect":
|
||||
case "SharpChat.Events.UserConnectEvent":
|
||||
sb.Append(V1_CHATBOT);
|
||||
sb.Append("0\fjoin\f");
|
||||
sb.Append(Event.Sender.UserName);
|
||||
break;
|
||||
|
||||
case UserChannelJoinEvent _:
|
||||
case "chan:join":
|
||||
case "SharpChat.Events.UserChannelJoinEvent":
|
||||
sb.Append(V1_CHATBOT);
|
||||
sb.Append("0\fjchan\f");
|
||||
sb.Append(Event.Sender.UserName);
|
||||
break;
|
||||
|
||||
case UserChannelLeaveEvent _:
|
||||
case "chan:leave":
|
||||
case "SharpChat.Events.UserChannelLeaveEvent":
|
||||
sb.Append(V1_CHATBOT);
|
||||
sb.Append("0\flchan\f");
|
||||
sb.Append(Event.Sender.UserName);
|
||||
break;
|
||||
|
||||
case UserDisconnectEvent ude:
|
||||
case "user:disconnect":
|
||||
case "SharpChat.Events.UserDisconnectEvent":
|
||||
sb.Append(V1_CHATBOT);
|
||||
sb.Append("0\f");
|
||||
|
||||
switch(ude.Reason) {
|
||||
switch((UserDisconnectReason)Event.Data.RootElement.GetProperty("reason").GetByte()) {
|
||||
case UserDisconnectReason.Flood:
|
||||
sb.Append("flood");
|
||||
break;
|
||||
|
@ -81,16 +87,15 @@ namespace SharpChat.Packet {
|
|||
break;
|
||||
}
|
||||
|
||||
|
||||
sb.Append('\t');
|
||||
sb.Append(Event.SequenceId < 1 ? SequenceId : Event.SequenceId);
|
||||
sb.Append(Event.Id < 1 ? SequenceId : Event.Id);
|
||||
sb.Append('\t');
|
||||
sb.Append(Notify ? '1' : '0');
|
||||
sb.AppendFormat(
|
||||
"\t1{0}0{1}{2}",
|
||||
Event.Flags.HasFlag(ChatMessageFlags.Action) ? '1' : '0',
|
||||
Event.Flags.HasFlag(ChatMessageFlags.Action) ? '0' : '1',
|
||||
Event.Flags.HasFlag(ChatMessageFlags.Private) ? '1' : '0'
|
||||
Event.Flags.HasFlag(StoredEventFlags.Action) ? '1' : '0',
|
||||
Event.Flags.HasFlag(StoredEventFlags.Action) ? '0' : '1',
|
||||
Event.Flags.HasFlag(StoredEventFlags.Private) ? '1' : '0'
|
||||
);
|
||||
|
||||
yield return sb.ToString();
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
using SharpChat.Commands;
|
||||
using SharpChat.Config;
|
||||
using SharpChat.Events;
|
||||
using SharpChat.EventStorage;
|
||||
using SharpChat.Packet;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace SharpChat.PacketHandlers {
|
||||
namespace SharpChat.PacketHandlers
|
||||
{
|
||||
public class SendMessageHandler : IChatPacketHandler {
|
||||
private readonly CachedValue<int> MaxMessageLength;
|
||||
|
||||
|
@ -63,7 +64,7 @@ namespace SharpChat.PacketHandlers {
|
|||
Logger.Write($"<{ctx.Connection.Id} {user.UserName}> {messageText}");
|
||||
#endif
|
||||
|
||||
IChatMessage message = null;
|
||||
(string text, bool isAction)? messageInfo = null;
|
||||
|
||||
if(messageText.StartsWith("/")) {
|
||||
ChatCommandContext context = new(messageText, ctx.Chat, user, ctx.Connection, channel);
|
||||
|
@ -78,7 +79,7 @@ namespace SharpChat.PacketHandlers {
|
|||
|
||||
if(command != null) {
|
||||
if(command is ActionCommand actionCommand)
|
||||
message = actionCommand.ActionDispatch(context);
|
||||
messageInfo = actionCommand.ActionDispatch(context);
|
||||
else {
|
||||
command.Dispatch(context);
|
||||
return;
|
||||
|
@ -86,15 +87,22 @@ namespace SharpChat.PacketHandlers {
|
|||
}
|
||||
}
|
||||
|
||||
message ??= new ChatMessage {
|
||||
ChannelName = channel.Name,
|
||||
DateTime = DateTimeOffset.UtcNow,
|
||||
Sender = user,
|
||||
Text = messageText,
|
||||
};
|
||||
messageInfo ??= (messageText, false);
|
||||
|
||||
ctx.Chat.Events.AddEvent(message);
|
||||
ctx.Chat.SendTo(channel, new ChatMessageAddPacket(message));
|
||||
long msgId = ctx.Chat.Events.AddEvent(
|
||||
"msg:add",
|
||||
user, channel,
|
||||
new { messageInfo.Value.text },
|
||||
messageInfo.Value.isAction ? StoredEventFlags.Action : StoredEventFlags.None
|
||||
);
|
||||
ctx.Chat.SendTo(channel, new ChatMessageAddPacket(
|
||||
msgId,
|
||||
DateTimeOffset.Now,
|
||||
user.UserId,
|
||||
messageInfo.Value.text,
|
||||
messageInfo.Value.isAction,
|
||||
false
|
||||
));
|
||||
} finally {
|
||||
ctx.Chat.ContextAccess.Release();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue